mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Drawer: Introduce a size property that set's width percentage and minWidth (#67809)
* Drawer: Introduce drawer size that sets width and min-width * media queries * Change large drawer to 75% * Change news drawer to medium as the news items have better layout then with images on the side * Tweaks and fixed inline drawer issue * review fixes * Deprecate inline, update mdx docs * remove inline var
This commit is contained in:
parent
fcb14d2548
commit
20217db100
@ -21,10 +21,20 @@ onClose = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer title="This a Drawer" width="60%" onClose={this.onClose}>
|
||||
<Drawer title="This a Drawer" size="md" onClose={this.onClose}>
|
||||
<div>Put your Drawer content here</div>
|
||||
</Drawer>
|
||||
);
|
||||
```
|
||||
|
||||
## Sizes
|
||||
|
||||
The Drawer supports 3 sizes: `sm`, `md`, and `lg`. This option defines a width in percentage and as well as a min-width.
|
||||
|
||||
- sm: width = 25vh and min-width = 384px
|
||||
- md: width = 50vh and min-width = 568px
|
||||
- lg: width = 75vh and min-width = 744px
|
||||
|
||||
## Props
|
||||
|
||||
<Props of={Drawer} />
|
||||
|
@ -22,7 +22,6 @@ const meta: ComponentMeta<typeof Drawer> = {
|
||||
args: {
|
||||
closeOnMaskClick: true,
|
||||
scrollableContent: false,
|
||||
width: '40%',
|
||||
expandable: false,
|
||||
subtitle: 'This is a subtitle.',
|
||||
},
|
||||
@ -142,42 +141,6 @@ LongContent.args = {
|
||||
title: 'Drawer title with long content',
|
||||
};
|
||||
|
||||
export const InLine: ComponentStory<typeof Drawer> = (args) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
height: '300px',
|
||||
width: '500px',
|
||||
border: '1px solid white',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => setIsOpen(true)}>Open drawer</Button>
|
||||
{isOpen && (
|
||||
<Drawer {...args} onClose={() => setIsOpen(false)}>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
InLine.args = {
|
||||
title: 'Drawer title inline',
|
||||
inline: true,
|
||||
};
|
||||
|
||||
export const WithTabs: ComponentStory<typeof Drawer> = (args) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState('options');
|
||||
@ -206,6 +169,7 @@ export const WithTabs: ComponentStory<typeof Drawer> = (args) => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WithTabs.args = {
|
||||
title: 'Drawer title with tabs',
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { useDialog } from '@react-aria/dialog';
|
||||
import { FocusScope } from '@react-aria/focus';
|
||||
import { useOverlay } from '@react-aria/overlays';
|
||||
import RcDrawer from 'rc-drawer';
|
||||
import React, { CSSProperties, ReactNode, useState, useEffect } from 'react';
|
||||
import React, { ReactNode, useState, useEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@ -20,12 +20,21 @@ export interface Props {
|
||||
subtitle?: ReactNode;
|
||||
/** Should the Drawer be closable by clicking on the mask, defaults to true */
|
||||
closeOnMaskClick?: boolean;
|
||||
/** Render the drawer inside a container on the page */
|
||||
/** @deprecated */
|
||||
inline?: boolean;
|
||||
/** Either a number in px or a string with unit postfix */
|
||||
/**
|
||||
* @deprecated use the size property instead
|
||||
**/
|
||||
width?: number | string;
|
||||
/** Should the Drawer be expandable to full width */
|
||||
expandable?: boolean;
|
||||
/**
|
||||
* Specifies the width and min-width.
|
||||
* sm = width 25vw & min-width 384px
|
||||
* md = width 50vw & min-width 568px
|
||||
* lg = width 75vw & min-width 744px
|
||||
**/
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
/** Tabs */
|
||||
tabs?: React.ReactNode;
|
||||
/** Set to true if the component rendered within in drawer content has its own scroll */
|
||||
@ -36,20 +45,19 @@ export interface Props {
|
||||
|
||||
export function Drawer({
|
||||
children,
|
||||
inline = false,
|
||||
onClose,
|
||||
closeOnMaskClick = true,
|
||||
scrollableContent = false,
|
||||
title,
|
||||
subtitle,
|
||||
width = '40%',
|
||||
width,
|
||||
size = 'md',
|
||||
expandable = false,
|
||||
tabs,
|
||||
}: Props) {
|
||||
const drawerStyles = useStyles2(getStyles);
|
||||
const styles = useStyles2(getStyles);
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const currentWidth = isExpanded ? '100%' : width;
|
||||
const overlayRef = React.useRef(null);
|
||||
const { dialogProps, titleProps } = useDialog({}, overlayRef);
|
||||
const { overlayProps } = useOverlay(
|
||||
@ -66,31 +74,29 @@ export function Drawer({
|
||||
setIsOpen(true);
|
||||
}, []);
|
||||
|
||||
const content = <div className={drawerStyles.content}>{children}</div>;
|
||||
const style: CSSProperties = {};
|
||||
if (inline) {
|
||||
style.position = 'absolute';
|
||||
}
|
||||
// deprecated width prop now defaults to empty string which make the size prop take over
|
||||
const fixedWidth = isExpanded ? '100%' : width ?? '';
|
||||
const rootClass = cx(styles.drawer, !fixedWidth && styles.sizes[size]);
|
||||
const content = <div className={styles.content}>{children}</div>;
|
||||
|
||||
return (
|
||||
<RcDrawer
|
||||
open={isOpen}
|
||||
onClose={onClose}
|
||||
placement="right"
|
||||
width={currentWidth}
|
||||
getContainer={inline ? undefined : 'body'}
|
||||
style={style}
|
||||
className={drawerStyles.drawerContent}
|
||||
rootClassName={drawerStyles.drawer}
|
||||
width={fixedWidth}
|
||||
getContainer={'.main-view'}
|
||||
className={styles.drawerContent}
|
||||
rootClassName={rootClass}
|
||||
motion={{
|
||||
motionAppear: true,
|
||||
motionName: drawerStyles.drawerMotion,
|
||||
motionName: styles.drawerMotion,
|
||||
}}
|
||||
maskClassName={drawerStyles.mask}
|
||||
maskClassName={styles.mask}
|
||||
maskClosable={closeOnMaskClick}
|
||||
maskMotion={{
|
||||
motionAppear: true,
|
||||
motionName: drawerStyles.maskMotion,
|
||||
motionName: styles.maskMotion,
|
||||
}}
|
||||
>
|
||||
<FocusScope restoreFocus contain autoFocus>
|
||||
@ -100,14 +106,14 @@ export function Drawer({
|
||||
? selectors.components.Drawer.General.title(title)
|
||||
: selectors.components.Drawer.General.title('no title')
|
||||
}
|
||||
className={drawerStyles.container}
|
||||
className={styles.container}
|
||||
{...overlayProps}
|
||||
{...dialogProps}
|
||||
ref={overlayRef}
|
||||
>
|
||||
{typeof title === 'string' && (
|
||||
<div className={drawerStyles.header}>
|
||||
<div className={drawerStyles.actions}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.actions}>
|
||||
{expandable && !isExpanded && (
|
||||
<IconButton
|
||||
name="angle-left"
|
||||
@ -131,16 +137,16 @@ export function Drawer({
|
||||
aria-label={selectors.components.Drawer.General.close}
|
||||
/>
|
||||
</div>
|
||||
<div className={drawerStyles.titleWrapper}>
|
||||
<div className={styles.titleWrapper}>
|
||||
<h3 {...titleProps}>{title}</h3>
|
||||
{typeof subtitle === 'string' && <div className="muted">{subtitle}</div>}
|
||||
{typeof subtitle !== 'string' && subtitle}
|
||||
{tabs && <div className={drawerStyles.tabsWrapper}>{tabs}</div>}
|
||||
{tabs && <div className={styles.tabsWrapper}>{tabs}</div>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{typeof title !== 'string' && title}
|
||||
<div className={drawerStyles.contentScroll}>
|
||||
<div className={styles.contentScroll}>
|
||||
{!scrollableContent ? content : <CustomScrollbar autoHeightMin="100%">{content}</CustomScrollbar>}
|
||||
</div>
|
||||
</div>
|
||||
@ -162,10 +168,39 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
box-shadow: ${theme.shadows.z3};
|
||||
|
||||
${theme.breakpoints.down('sm')} {
|
||||
width: 100% !important;
|
||||
width: calc(100% - ${theme.spacing(2)}) !important;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
}
|
||||
`,
|
||||
sizes: {
|
||||
sm: css({
|
||||
'.rc-drawer-content-wrapper': {
|
||||
label: 'drawer-sm',
|
||||
width: '25vw',
|
||||
minWidth: theme.spacing(48),
|
||||
},
|
||||
}),
|
||||
md: css({
|
||||
'.rc-drawer-content-wrapper': {
|
||||
label: 'drawer-md',
|
||||
width: '50vw',
|
||||
minWidth: theme.spacing(66),
|
||||
},
|
||||
}),
|
||||
lg: css({
|
||||
'.rc-drawer-content-wrapper': {
|
||||
label: 'drawer-lg',
|
||||
width: '75vw',
|
||||
minWidth: theme.spacing(93),
|
||||
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width: `calc(100% - ${theme.spacing(2)}) !important`,
|
||||
minWidth: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
drawerContent: css`
|
||||
background-color: ${theme.colors.background.primary} !important;
|
||||
display: flex;
|
||||
|
@ -22,7 +22,12 @@ export function NewsContainer({ className }: NewsContainerProps) {
|
||||
<>
|
||||
<ToolbarButton className={className} onClick={onChildClick} iconOnly icon="rss" aria-label="News" />
|
||||
{showNewsDrawer && (
|
||||
<Drawer title={t('news.title', 'Latest from the blog')} scrollableContent onClose={onToggleShowNewsDrawer}>
|
||||
<Drawer
|
||||
title={t('news.title', 'Latest from the blog')}
|
||||
scrollableContent
|
||||
onClose={onToggleShowNewsDrawer}
|
||||
size="md"
|
||||
>
|
||||
<NewsWrapper feedUrl={DEFAULT_FEED_URL} />
|
||||
</Drawer>
|
||||
)}
|
||||
|
@ -72,7 +72,7 @@ export function HelpWizard({ panel, plugin, onClose }: Props) {
|
||||
return (
|
||||
<Drawer
|
||||
title={`Get help with this panel`}
|
||||
width="90%"
|
||||
size="lg"
|
||||
onClose={onClose}
|
||||
expandable
|
||||
scrollableContent
|
||||
|
@ -68,7 +68,6 @@ export const InspectContent = ({
|
||||
<Drawer
|
||||
title={title}
|
||||
subtitle={data && formatStats(data)}
|
||||
width="50%"
|
||||
onClose={onClose}
|
||||
expandable
|
||||
scrollableContent
|
||||
|
@ -131,7 +131,6 @@ export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCop
|
||||
<Drawer
|
||||
title={title}
|
||||
onClose={onDismiss}
|
||||
width={'40%'}
|
||||
subtitle={dashboard.title}
|
||||
tabs={
|
||||
<TabsBar>
|
||||
|
Loading…
Reference in New Issue
Block a user