grafana/ui: Drawer component (#20078)

* basic setup

* onclose fix

* styling and inline drawer

* add options and mdx

* remove blank line

* disable pushing page when opening drawer, re add mask opacity

* small fixes from pr review

* Add knobs and story with long content

* set redone markup and made title sticky

* Minor style update

* Minor style updates 2

* More explicit width prop

* fix import
This commit is contained in:
Peter Holmberg 2019-11-01 12:27:24 +01:00 committed by GitHub
parent 378208c6ae
commit ca75a29de3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 323 additions and 1 deletions

View File

@ -39,6 +39,7 @@
"lodash": "4.17.15",
"moment": "2.24.0",
"papaparse": "4.6.3",
"rc-drawer": "3.0.2",
"rc-time-picker": "^3.7.2",
"react": "16.8.6",
"react-calendar": "2.18.1",

View File

@ -0,0 +1,4 @@
import { Props } from '@storybook/addon-docs/blocks';
import { Drawer } from './Drawer';
<Props of={Drawer} />

View File

@ -0,0 +1,205 @@
import React from 'react';
import { text } from '@storybook/addon-knobs';
import { Drawer } from './Drawer';
import { UseState } from '../../utils/storybook/UseState';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import mdx from './Drawer.mdx';
export default {
title: 'UI/Drawer',
component: Drawer,
decorators: [withCenteredStory],
parameters: {
docs: {
page: mdx,
},
},
};
export const global = () => {
const drawerTitle = text('title', 'Storybook');
return (
<UseState initialState={{ isOpen: false }}>
{(state, updateValue) => {
return (
<>
<div
style={{ border: '1px solid gray', borderRadius: '4px', padding: '10px', cursor: 'pointer' }}
onClick={() => updateValue({ isOpen: !state.isOpen })}
>
Open drawer
</div>
{state.isOpen && (
<Drawer
title={drawerTitle}
onClose={() => {
updateValue({ isOpen: !state.isOpen });
}}
>
<ul>
<li>this</li>
<li>is</li>
<li>a</li>
<li>list</li>
<li>of</li>
<li>menu</li>
<li>items</li>
</ul>
</Drawer>
)}
</>
);
}}
</UseState>
);
};
export const longContent = () => {
return (
<UseState initialState={{ isOpen: true }}>
{(state, updateValue) => {
return (
<>
<div
style={{ border: '1px solid gray', borderRadius: '4px', padding: '10px', cursor: 'pointer' }}
onClick={() => updateValue({ isOpen: !state.isOpen })}
>
Open drawer
</div>
{state.isOpen && (
<Drawer
title="Drawer with long content"
onClose={() => {
updateValue({ isOpen: !state.isOpen });
}}
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Iaculis nunc sed augue lacus viverra vitae. Malesuada pellentesque elit eget
gravida cum sociis. Pretium vulputate sapien nec sagittis aliquam malesuada bibendum arcu. Cras
adipiscing enim eu turpis egestas. Ut lectus arcu bibendum at varius. Nulla pellentesque dignissim
enim sit amet venenatis urna. Tempus urna et pharetra pharetra massa massa ultricies mi quis. Vitae
congue mauris rhoncus aenean. Enim ut tellus elementum sagittis vitae et.
</p>
<p>
Arcu non odio euismod lacinia at quis risus sed vulputate. Sit amet consectetur adipiscing elit ut.
Dictum fusce ut placerat orci nulla pellentesque dignissim. Lectus nulla at volutpat diam ut venenatis
tellus. Sed cras ornare arcu dui. Eget mauris pharetra et ultrices neque ornare aenean euismod. Mi
quis hendrerit dolor magna. Commodo viverra maecenas accumsan lacus vel facilisis. Eget mi proin sed
libero enim sed. Magna ac placerat vestibulum lectus mauris ultrices eros in. Mattis nunc sed blandit
libero volutpat.
</p>
<p>
Cursus in hac habitasse platea dictumst quisque sagittis purus. Viverra adipiscing at in tellus.
Semper eget duis at tellus at urna condimentum. Egestas fringilla phasellus faucibus scelerisque
eleifend. Sem nulla pharetra diam sit amet nisl. Ut ornare lectus sit amet est placerat in egestas
erat. Id neque aliquam vestibulum morbi blandit cursus risus. In iaculis nunc sed augue. Eu volutpat
odio facilisis mauris sit. Quisque egestas diam in arcu cursus euismod. At quis risus sed vulputate
odio ut enim blandit volutpat. Cursus risus at ultrices mi tempus imperdiet nulla malesuada. Sed
adipiscing diam donec adipiscing tristique risus nec. Id neque aliquam vestibulum morbi. Pretium nibh
ipsum consequat nisl vel pretium lectus quam. Platea dictumst quisque sagittis purus sit. Nascetur
ridiculus mus mauris vitae ultricies leo.
</p>
<p>
Cursus in hac habitasse platea dictumst quisque sagittis purus. Viverra adipiscing at in tellus.
Semper eget duis at tellus at urna condimentum. Egestas fringilla phasellus faucibus scelerisque
eleifend. Sem nulla pharetra diam sit amet nisl. Ut ornare lectus sit amet est placerat in egestas
erat. Id neque aliquam vestibulum morbi blandit cursus risus. In iaculis nunc sed augue. Eu volutpat
odio facilisis mauris sit. Quisque egestas diam in arcu cursus euismod. At quis risus sed vulputate
odio ut enim blandit volutpat. Cursus risus at ultrices mi tempus imperdiet nulla malesuada. Sed
adipiscing diam donec adipiscing tristique risus nec. Id neque aliquam vestibulum morbi. Pretium nibh
ipsum consequat nisl vel pretium lectus quam. Platea dictumst quisque sagittis purus sit. Nascetur
ridiculus mus mauris vitae ultricies leo.
</p>
<p>
Cursus in hac habitasse platea dictumst quisque sagittis purus. Viverra adipiscing at in tellus.
Semper eget duis at tellus at urna condimentum. Egestas fringilla phasellus faucibus scelerisque
eleifend. Sem nulla pharetra diam sit amet nisl. Ut ornare lectus sit amet est placerat in egestas
erat. Id neque aliquam vestibulum morbi blandit cursus risus. In iaculis nunc sed augue. Eu volutpat
odio facilisis mauris sit. Quisque egestas diam in arcu cursus euismod. At quis risus sed vulputate
odio ut enim blandit volutpat. Cursus risus at ultrices mi tempus imperdiet nulla malesuada. Sed
adipiscing diam donec adipiscing tristique risus nec. Id neque aliquam vestibulum morbi. Pretium nibh
ipsum consequat nisl vel pretium lectus quam. Platea dictumst quisque sagittis purus sit. Nascetur
ridiculus mus mauris vitae ultricies leo.
</p>
<p>
Cursus in hac habitasse platea dictumst quisque sagittis purus. Viverra adipiscing at in tellus.
Semper eget duis at tellus at urna condimentum. Egestas fringilla phasellus faucibus scelerisque
eleifend. Sem nulla pharetra diam sit amet nisl. Ut ornare lectus sit amet est placerat in egestas
erat. Id neque aliquam vestibulum morbi blandit cursus risus. In iaculis nunc sed augue. Eu volutpat
odio facilisis mauris sit. Quisque egestas diam in arcu cursus euismod. At quis risus sed vulputate
odio ut enim blandit volutpat. Cursus risus at ultrices mi tempus imperdiet nulla malesuada. Sed
adipiscing diam donec adipiscing tristique risus nec. Id neque aliquam vestibulum morbi. Pretium nibh
ipsum consequat nisl vel pretium lectus quam. Platea dictumst quisque sagittis purus sit. Nascetur
ridiculus mus mauris vitae ultricies leo.
</p>
<p>
Cursus in hac habitasse platea dictumst quisque sagittis purus. Viverra adipiscing at in tellus.
Semper eget duis at tellus at urna condimentum. Egestas fringilla phasellus faucibus scelerisque
eleifend. Sem nulla pharetra diam sit amet nisl. Ut ornare lectus sit amet est placerat in egestas
erat. Id neque aliquam vestibulum morbi blandit cursus risus. In iaculis nunc sed augue. Eu volutpat
odio facilisis mauris sit. Quisque egestas diam in arcu cursus euismod. At quis risus sed vulputate
odio ut enim blandit volutpat. Cursus risus at ultrices mi tempus imperdiet nulla malesuada. Sed
adipiscing diam donec adipiscing tristique risus nec. Id neque aliquam vestibulum morbi. Pretium nibh
ipsum consequat nisl vel pretium lectus quam. Platea dictumst quisque sagittis purus sit. Nascetur
ridiculus mus mauris vitae ultricies leo.
</p>
</Drawer>
)}
</>
);
}}
</UseState>
);
};
export const inLine = () => {
const drawerTitle = text('title', 'Storybook');
return (
<UseState initialState={{ isOpen: false }}>
{(state, updateValue) => {
return (
<>
<div
style={{
height: '300px',
width: '500px',
border: '1px solid white',
position: 'relative',
overflow: 'hidden',
}}
>
<div
style={{ border: '1px solid gray', borderRadius: '4px', padding: '10px', cursor: 'pointer' }}
onClick={() => updateValue({ isOpen: !state.isOpen })}
>
Open drawer
</div>
{state.isOpen && (
<Drawer
inline={true}
title={drawerTitle}
onClose={() => {
updateValue({ isOpen: !state.isOpen });
}}
>
<ul>
<li>this</li>
<li>is</li>
<li>a</li>
<li>list</li>
<li>of</li>
<li>menu</li>
<li>items</li>
</ul>
</Drawer>
)}
</div>
</>
);
}}
</UseState>
);
};

View File

@ -0,0 +1,94 @@
import React, { CSSProperties, FC, ReactNode } from 'react';
import { GrafanaTheme } from '@grafana/data';
import RcDrawer from 'rc-drawer';
import { css } from 'emotion';
import { stylesFactory, useTheme, selectThemeVariant } from '../../themes';
interface Props {
children: ReactNode;
/** Title shown at the top of the drawer */
title?: string;
/** Should the Drawer be closable by clicking on the mask */
closeOnMaskClick?: boolean;
/** Render the drawer inside a container on the page */
inline?: boolean;
/** Either a number in px or a string with unit postfix */
width?: number | string;
onClose: () => void;
}
const getStyles = stylesFactory((theme: GrafanaTheme) => {
const closeButtonWidth = '50px';
const borderColor = selectThemeVariant(
{
light: theme.colors.gray4,
dark: theme.colors.dark9,
},
theme.type
);
return {
drawer: css`
.drawer-content {
background-color: ${theme.colors.bodyBg};
}
`,
titleWrapper: css`
font-size: ${theme.typography.size.lg};
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid ${borderColor};
padding: ${theme.spacing.sm} 0 ${theme.spacing.sm} ${theme.spacing.md};
background-color: ${theme.colors.bodyBg};
position: sticky;
top: 0;
`,
close: css`
cursor: pointer;
width: ${closeButtonWidth};
height: 100%;
display: flex;
flex-shrink: 0;
justify-content: center;
`,
content: css`
padding: ${theme.spacing.md};
`,
};
});
export const Drawer: FC<Props> = ({
children,
inline = false,
onClose,
closeOnMaskClick = false,
title,
width = '40%',
}) => {
const theme = useTheme();
const drawerStyles = getStyles(theme);
return (
<RcDrawer
level={null}
handler={false}
open={true}
onClose={onClose}
maskClosable={closeOnMaskClick}
placement="right"
width={width}
getContainer={inline ? false : 'body'}
style={{ position: `${inline && 'absolute'}` } as CSSProperties}
className={drawerStyles.drawer}
>
<div className={drawerStyles.titleWrapper}>
<div>{title}</div>
<div className={drawerStyles.close} onClick={onClose}>
<i className="fa fa-close" />
</div>
</div>
<div className={drawerStyles.content}>{children}</div>
</RcDrawer>
);
};

View File

@ -0,0 +1,2 @@
// Need to import this to get default styles from rc-drawer
@import '../../node_modules/rc-drawer/assets/index.css';

View File

@ -15,3 +15,4 @@
@import 'RefreshPicker/RefreshPicker';
@import 'TimePicker/TimePicker';
@import 'TimePicker/TimeOfDayPicker';
@import 'Drawer/Drawer';

View File

@ -5862,6 +5862,11 @@ 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.30000939, caniuse-lite@^1.0.30000947, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30000999:
version "1.0.30000999"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000999.tgz#427253a69ad7bea4aa8d8345687b8eec51ca0e43"
@ -16835,6 +16840,16 @@ rc-cascader@0.17.5:
shallow-equal "^1.0.0"
warning "^4.0.1"
rc-drawer@3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-3.0.2.tgz#1c42b2b7790040344f8f05f1d132b1ef0e97b783"
integrity sha512-oPScGXB/8/ov9gEFLxPH8RBv/9jLTZboZtyF/GgrrnCAvbFwUxXdELH6n6XIowmuDKKvTGIMgZdnao0T46Yv3A==
dependencies:
babel-runtime "^6.26.0"
classnames "^2.2.6"
rc-util "^4.11.2"
react-lifecycles-compat "^3.0.4"
rc-time-picker@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/rc-time-picker/-/rc-time-picker-3.7.2.tgz#fabe5501adf1374d31a2d3b47f1ba89fc2dc2467"
@ -16860,7 +16875,7 @@ rc-trigger@^2.2.0:
rc-util "^4.4.0"
react-lifecycles-compat "^3.0.4"
rc-util@^4.0.4, rc-util@^4.4.0, rc-util@^4.8.0:
rc-util@^4.0.4, rc-util@^4.11.2, rc-util@^4.4.0, rc-util@^4.8.0:
version "4.13.0"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.13.0.tgz#75682e50a934e7d32ada7ee48fc9f5b20fc0143f"
integrity sha512-rjfPy+afc2n40APHp6GYScXfgwHuUnYLz/4SCEWRaF8CHXKR8xw598LtPA36J3fEXENuMm6liO/CoKBoSrYCDw==