mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
378208c6ae
commit
ca75a29de3
@ -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",
|
||||
|
4
packages/grafana-ui/src/components/Drawer/Drawer.mdx
Normal file
4
packages/grafana-ui/src/components/Drawer/Drawer.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
import { Props } from '@storybook/addon-docs/blocks';
|
||||
import { Drawer } from './Drawer';
|
||||
|
||||
<Props of={Drawer} />
|
205
packages/grafana-ui/src/components/Drawer/Drawer.story.tsx
Normal file
205
packages/grafana-ui/src/components/Drawer/Drawer.story.tsx
Normal 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>
|
||||
);
|
||||
};
|
94
packages/grafana-ui/src/components/Drawer/Drawer.tsx
Normal file
94
packages/grafana-ui/src/components/Drawer/Drawer.tsx
Normal 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>
|
||||
);
|
||||
};
|
2
packages/grafana-ui/src/components/Drawer/_Drawer.scss
Normal file
2
packages/grafana-ui/src/components/Drawer/_Drawer.scss
Normal file
@ -0,0 +1,2 @@
|
||||
// Need to import this to get default styles from rc-drawer
|
||||
@import '../../node_modules/rc-drawer/assets/index.css';
|
@ -15,3 +15,4 @@
|
||||
@import 'RefreshPicker/RefreshPicker';
|
||||
@import 'TimePicker/TimePicker';
|
||||
@import 'TimePicker/TimeOfDayPicker';
|
||||
@import 'Drawer/Drawer';
|
||||
|
17
yarn.lock
17
yarn.lock
@ -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==
|
||||
|
Loading…
Reference in New Issue
Block a user