mirror of https://github.com/grafana/grafana.git
				
				
				
			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
This commit is contained in:
		
							parent
							
								
									a7ac3f1419
								
							
						
					
					
						commit
						0132bca93a
					
				| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
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>
 | 
			
		||||
                }
 | 
			
		||||
              />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue