UI: Adds Modal component (#19369)

* UI: Adds Modal component
This commit is contained in:
kay delaney 2019-10-03 13:30:24 +01:00 committed by GitHub
parent f9611250ea
commit e16064b9b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 178 additions and 5 deletions

View File

@ -59,6 +59,7 @@
"@storybook/react": "5.0.6",
"@storybook/theming": "5.0.6",
"@types/classnames": "2.2.7",
"@types/common-tags": "^1.8.0",
"@types/d3": "5.7.1",
"@types/jest": "23.3.14",
"@types/jquery": "1.10.35",
@ -75,6 +76,7 @@
"@types/storybook__addon-knobs": "4.0.4",
"@types/storybook__react": "4.0.1",
"@types/tinycolor2": "1.4.1",
"common-tags": "^1.8.0",
"pretty-format": "24.9.0",
"react-docgen-typescript-loader": "3.0.1",
"react-docgen-typescript-webpack-plugin": "1.1.0",

View File

@ -0,0 +1,45 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { oneLineTrim } from 'common-tags';
import { text, boolean } from '@storybook/addon-knobs';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { Modal } from './Modal';
const getKnobs = () => {
return {
body: text(
'Body',
oneLineTrim`Id incididunt do pariatur qui labore. Sint culpa irure cillum et ullamco proident. Deserunt ipsum velit dolore est enim proident dolore consectetur. Et cillum tempor pariatur et. Est tempor cillum ad id nulla. Cillum ut proident
magna do cillum consequat reprehenderit excepteur. Pariatur culpa id excepteur reprehenderit consequat qui qui sit
consectetur esse enim mollit incididunt. Ea excepteur nisi mollit reprehenderit eiusmod tempor. Eiusmod incididunt
occaecat velit consectetur dolor cillum anim commodo fugiat cupidatat ut tempor officia. Aliquip fugiat occaecat
excepteur consectetur ullamco consectetur exercitation occaecat sint sint incididunt cillum minim. Sint aliquip ea
pariatur anim. Veniam laboris mollit in voluptate exercitation sint deserunt dolor ullamco ex dolor. Enim
reprehenderit ut Lorem aliquip est laborum in. Aliqua in ut aute elit nulla amet. Ex proident pariatur ex in
aliquip. Labore eu Lorem sint aliqua reprehenderit ipsum veniam aliquip laborum dolor deserunt cupidatat velit
amet.`
),
visible: boolean('Visible', true),
};
};
const ModalStories = storiesOf('UI/Modal', module);
ModalStories.addDecorator(withCenteredStory);
ModalStories.add('default', () => {
const { body, visible } = getKnobs();
return (
<Modal
title={
<div className="modal-header-title">
<i className="fa fa-share-square-o" />
<span className="p-l-1">My Modal</span>
</div>
}
isOpen={visible}
>
{body}
</Modal>
);
});

View File

@ -0,0 +1,98 @@
import React from 'react';
import { Portal } from '../Portal/Portal';
import { css, cx } from 'emotion';
import { GrafanaTheme, ThemeContext } from '../..';
const getStyles = (theme: GrafanaTheme) => ({
modal: css`
position: fixed;
z-index: ${theme.zIndex.modal};
width: 100%;
background: ${theme.colors.pageBg};
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
background-clip: padding-box;
outline: none;
max-width: 750px;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
top: 10%;
`,
modalBackdrop: css`
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: ${theme.zIndex.modalBackdrop}
background-color: ${theme.colors.bodyBg}
opacity: 0.8;
backdrop-filter: blur(4px);
`,
modalHeader: css`
background: ${theme.background.pageHeader};
box-shadow: ${theme.shadow.pageHeader};
border-bottom: 1px soliod ${theme.colors.pageHeaderBorder};
display: flex;
`,
modalHeaderTitle: css`
font-size: ${theme.typography.heading.h3};
padding-top: calc(${theme.spacing.d} * 0.75);
margin: 0 calc(${theme.spacing.d} * 3) 0 calc(${theme.spacing.d} * 1.5);
`,
modalHeaderClose: css`
margin-left: auto;
padding: 9px ${theme.spacing.d};
`,
modalContent: css`
padding: calc(${theme.spacing.d} * 2);
`,
});
interface Props {
title: string | JSX.Element;
isOpen?: boolean;
onDismiss?: () => void;
onClickBackdrop?: () => void;
}
export class Modal extends React.PureComponent<Props> {
static contextType = ThemeContext;
context!: React.ContextType<typeof ThemeContext>;
onDismiss = () => {
if (this.props.onDismiss) {
this.props.onDismiss();
}
};
onClickBackdrop = () => {
this.onDismiss();
};
render() {
const { title, isOpen = false } = this.props;
const styles = getStyles(this.context);
if (!isOpen) {
return null;
}
return (
<Portal>
<div className={cx(styles.modal)}>
<div className={cx(styles.modalHeader)}>
{typeof title === 'string' ? <h2 className={cx(styles.modalHeaderTitle)}>{title}</h2> : <>{title}</>}
<a className={cx(styles.modalHeaderClose)} onClick={this.onDismiss}>
<i className="fa fa-remove" />
</a>
</div>
<div className={cx(styles.modalContent)}>{this.props.children}</div>
</div>
<div className={cx(styles.modalBackdrop)} onClick={this.props.onClickBackdrop || this.onClickBackdrop} />
</Portal>
);
}
}

View File

@ -74,6 +74,7 @@ exports[`Render should render with base threshold 1`] = `
Object {
"background": Object {
"dropdown": "#1f1f20",
"pageHeader": "linear-gradient(90deg, #292a2d, #000000)",
"scrollbar": "#343436",
"scrollbar2": "#343436",
},
@ -138,6 +139,7 @@ exports[`Render should render with base threshold 1`] = `
"orange": "#eb7b18",
"orangeDark": "#ff780a",
"pageBg": "#161719",
"pageHeaderBorder": "#343436",
"purple": "#9933cc",
"queryGreen": "#74e680",
"queryKeyword": "#66d9ef",
@ -165,6 +167,9 @@ exports[`Render should render with base threshold 1`] = `
"name": "Grafana Dark",
"panelHeaderHeight": 28,
"panelPadding": 8,
"shadow": Object {
"pageHeader": "inset 0px -4px 14px #1f1f20",
},
"spacing": Object {
"d": "14px",
"gutter": "30px",
@ -236,6 +241,7 @@ exports[`Render should render with base threshold 1`] = `
Object {
"background": Object {
"dropdown": "#1f1f20",
"pageHeader": "linear-gradient(90deg, #292a2d, #000000)",
"scrollbar": "#343436",
"scrollbar2": "#343436",
},
@ -300,6 +306,7 @@ exports[`Render should render with base threshold 1`] = `
"orange": "#eb7b18",
"orangeDark": "#ff780a",
"pageBg": "#161719",
"pageHeaderBorder": "#343436",
"purple": "#9933cc",
"queryGreen": "#74e680",
"queryKeyword": "#66d9ef",
@ -327,6 +334,9 @@ exports[`Render should render with base threshold 1`] = `
"name": "Grafana Dark",
"panelHeaderHeight": 28,
"panelPadding": 8,
"shadow": Object {
"pageHeader": "inset 0px -4px 14px #1f1f20",
},
"spacing": Object {
"d": "14px",
"gutter": "30px",

View File

@ -37,6 +37,7 @@ export { RefreshPicker } from './RefreshPicker/RefreshPicker';
export { TimePicker } from './TimePicker/TimePicker';
export { TimeOfDayPicker } from './TimePicker/TimeOfDayPicker';
export { List } from './List/List';
export { Modal } from './Modal/Modal';
// Renderless
export { SetInterval } from './SetInterval/SetInterval';

View File

@ -71,11 +71,16 @@ const darkTheme: GrafanaTheme = {
linkHover: basicColors.white,
linkExternal: basicColors.blue,
headingColor: basicColors.gray4,
pageHeaderBorder: basicColors.dark9,
},
background: {
dropdown: basicColors.dark3,
scrollbar: basicColors.dark9,
scrollbar2: basicColors.dark9,
pageHeader: `linear-gradient(90deg, ${basicColors.dark7}, ${basicColors.black})`,
},
shadow: {
pageHeader: `inset 0px -4px 14px ${basicColors.dark3}`,
},
};

View File

@ -72,11 +72,16 @@ const lightTheme: GrafanaTheme = {
linkHover: basicColors.dark1,
linkExternal: basicColors.blueLight,
headingColor: basicColors.gray1,
pageHeaderBorder: basicColors.gray4,
},
background: {
dropdown: basicColors.white,
scrollbar: basicColors.gray5,
scrollbar2: basicColors.gray5,
pageHeader: `linear-gradient(90deg, ${basicColors.white}, ${basicColors.gray7})`,
},
shadow: {
pageHeader: `inset 0px -3px 10px ${basicColors.gray6}`,
},
};

View File

@ -96,6 +96,7 @@ export interface GrafanaTheme extends GrafanaThemeCommons {
dropdown: string;
scrollbar: string;
scrollbar2: string;
pageHeader: string;
};
colors: {
black: string;
@ -169,6 +170,11 @@ export interface GrafanaTheme extends GrafanaThemeCommons {
bodyBg: string;
pageBg: string;
headingColor: string;
pageHeaderBorder: string;
};
shadow: {
pageHeader: string;
};
}

View File

@ -2750,6 +2750,11 @@
resolved "https://registry.yarnpkg.com/@types/command-exists/-/command-exists-1.2.0.tgz#d97e0ed10097090e4ab0367ed425b0312fad86f3"
integrity sha512-ugsxEJfsCuqMLSuCD4PIJkp5Uk2z6TCMRCgYVuhRo5cYQY3+1xXTQkSlPtkpGHuvWMjS2KTeVQXxkXRACMbM6A==
"@types/common-tags@^1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@types/common-tags/-/common-tags-1.8.0.tgz#79d55e748d730b997be5b7fce4b74488d8b26a6b"
integrity sha512-htRqZr5qn8EzMelhX/Xmx142z218lLyGaeZ3YR8jlze4TATRU9huKKvuBmAJEW4LCC4pnY1N6JAm6p85fMHjhg==
"@types/connect-history-api-fallback@*":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.3.tgz#4772b79b8b53185f0f4c9deab09236baf76ee3b4"
@ -5266,11 +5271,6 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-db@1.0.30000772:
version "1.0.30000772"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b"
integrity sha1-UarokXaChureSj2DGep21qAbUSs=
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000929, caniuse-lite@^1.0.30000947, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30000963:
version "1.0.30000966"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000966.tgz#f3c6fefacfbfbfb981df6dfa68f2aae7bff41b64"
@ -5735,6 +5735,7 @@ commander@~2.19.0:
common-tags@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==
commondir@^1.0.1:
version "1.0.1"