Commit 0132bca9 by Torkel Ödegaard Committed by GitHub

Notifications: UX tweak to redesign of form and sections to make it left aligned…

Notifications: UX tweak to redesign of form and sections to make it left aligned and cleaner (#27479)

* UX: Redesign of form and sections to make it left aligned and cleaner

* reduced padding

* design tweaks
parent a7ac3f14
import React, { FC, ReactNode, useState } from 'react';
import { css } from 'emotion';
import { css, cx } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { useStyles } from '../../themes';
import { Icon } from '..';
......@@ -13,14 +13,20 @@ export interface Props {
export const CollapsableSection: FC<Props> = ({ label, isOpen, children }) => {
const [open, toggleOpen] = useState<boolean>(isOpen);
const styles = useStyles(collapsableSectionStyles);
const headerClass = cx({
[styles.header]: true,
[styles.headerCollapsed]: !open,
});
const tooltip = `Click to ${open ? 'collapse' : 'expand'}`;
return (
<div>
<div onClick={() => toggleOpen(!open)} className={styles.header}>
<Icon name={open ? 'angle-down' : 'angle-right'} size="xl" />
<div onClick={() => toggleOpen(!open)} className={headerClass} title={tooltip}>
{label}
<Icon name={open ? 'angle-down' : 'angle-right'} size="xl" className={styles.icon} />
</div>
<div className={styles.content}>{open && children}</div>
{open && <div className={styles.content}>{children}</div>}
</div>
);
};
......@@ -28,11 +34,19 @@ export const CollapsableSection: FC<Props> = ({ label, isOpen, children }) => {
const collapsableSectionStyles = (theme: GrafanaTheme) => {
return {
header: css`
display: flex;
justify-content: space-between;
font-size: ${theme.typography.size.lg};
cursor: pointer;
`,
headerCollapsed: css`
border-bottom: 1px solid ${theme.colors.border2};
`,
icon: css`
color: ${theme.colors.textWeak};
`,
content: css`
padding: ${theme.spacing.md} 0 ${theme.spacing.md} ${theme.spacing.md};
padding: ${theme.spacing.md} 0;
`,
};
};
......@@ -86,7 +86,7 @@ export class EditNotificationChannelPage extends PureComponent<Props> {
<h2 className="page-sub-heading">Edit notification channel</h2>
{notificationChannel && notificationChannel.id > 0 ? (
<Form
width={600}
maxWidth={600}
onSubmit={this.onSubmit}
defaultValues={{
...notificationChannel,
......
......@@ -52,7 +52,7 @@ class NewNotificationChannelPage extends PureComponent<Props> {
<Page navModel={navModel}>
<Page.Contents>
<h2 className="page-sub-heading">New notification channel</h2>
<Form onSubmit={this.onSubmit} validateOn="onChange" defaultValues={defaultValues}>
<Form onSubmit={this.onSubmit} validateOn="onChange" defaultValues={defaultValues} maxWidth={600}>
{({ register, errors, control, getValues, watch }) => {
const selectedChannel = notificationChannelTypes.find(c => c.value === getValues().type.value);
......
import React, { FC } from 'react';
import { SelectableValue } from '@grafana/data';
import { CollapsableSection, Field, Input, InputControl, Select } from '@grafana/ui';
import { Field, Input, InputControl, Select } from '@grafana/ui';
import { NotificationChannelOptions } from './NotificationChannelOptions';
import { NotificationSettingsProps } from './NotificationChannelForm';
import { NotificationChannelSecureFields, NotificationChannelType } from '../../../types';
......@@ -23,7 +23,7 @@ export const BasicSettings: FC<Props> = ({
resetSecureField,
}) => {
return (
<CollapsableSection label="Channel" isOpen>
<>
<Field label="Name" invalid={!!errors.name} error={errors.name && errors.name.message}>
<Input name="name" ref={register({ required: 'Name is required' })} />
</Field>
......@@ -39,6 +39,6 @@ export const BasicSettings: FC<Props> = ({
errors={errors}
control={control}
/>
</CollapsableSection>
</>
);
};
......@@ -41,9 +41,14 @@ export const NotificationChannelForm: FC<Props> = ({
}, []);
const currentFormValues = getValues();
return selectedChannel ? (
<>
<div className={styles.basicSettings}>
if (!selectedChannel) {
return <Spinner />;
}
return (
<div className={styles.formContainer}>
<div className={styles.formItem}>
<BasicSettings
selectedChannel={selectedChannel}
channels={selectableChannels}
......@@ -54,8 +59,10 @@ export const NotificationChannelForm: FC<Props> = ({
errors={errors}
control={control}
/>
{/* If there are no non-required fields, don't render this section*/}
{selectedChannel.options.filter(o => !o.required).length > 0 && (
</div>
{/* If there are no non-required fields, don't render this section*/}
{selectedChannel.options.filter(o => !o.required).length > 0 && (
<div className={styles.formItem}>
<ChannelSettings
selectedChannel={selectedChannel}
secureFields={secureFields}
......@@ -65,7 +72,9 @@ export const NotificationChannelForm: FC<Props> = ({
errors={errors}
control={control}
/>
)}
</div>
)}
<div className={styles.formItem}>
<NotificationSettings
imageRendererAvailable={imageRendererAvailable}
currentFormValues={currentFormValues}
......@@ -74,27 +83,32 @@ export const NotificationChannelForm: FC<Props> = ({
control={control}
/>
</div>
<HorizontalGroup>
<Button type="submit">Save</Button>
<Button type="button" variant="secondary" onClick={() => onTestChannel(getValues({ nest: true }))}>
Test
</Button>
<a href="/alerting/notifications">
<Button type="button" variant="secondary">
Back
<div className={styles.formButtons}>
<HorizontalGroup>
<Button type="submit">Save</Button>
<Button type="button" variant="secondary" onClick={() => onTestChannel(getValues({ nest: true }))}>
Test
</Button>
</a>
</HorizontalGroup>
</>
) : (
<Spinner />
<a href="/alerting/notifications">
<Button type="button" variant="secondary">
Back
</Button>
</a>
</HorizontalGroup>
</div>
</div>
);
};
const getStyles = stylesFactory((theme: GrafanaTheme) => {
return {
basicSettings: css`
margin-bottom: ${theme.spacing.xl};
formContainer: css``,
formItem: css`
flex-grow: 1;
padding-top: ${theme.spacing.md};
`,
formButtons: css`
padding-top: ${theme.spacing.xl};
`,
};
});
......@@ -59,9 +59,14 @@ export const NotificationChannelOptions: FC<Props> = ({
<Input
readOnly={true}
value="Configured"
addonAfter={
<Button onClick={() => onResetSecureField(option.propertyName)} variant="secondary" type="button">
Reset
suffix={
<Button
onClick={() => onResetSecureField(option.propertyName)}
variant="link"
type="button"
size="sm"
>
Clear
</Button>
}
/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment