From d5f8d976f01aad7ee2735a53c44967b90a888391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agn=C3=A8s=20Toulet?= <35176601+AgnesToulet@users.noreply.github.com> Date: Mon, 20 Apr 2020 15:27:33 +0200 Subject: [PATCH] Drawer: align component style with PanelInspector (#23694) * Drawer: add subtitle, expandable button and refactor style * Drawer: update header style and z-index * Drawer: refactor Drawer component and PanelInspector * PanelInspector: add expandable * Drawer: update stories with new props * Inspector: rename InspectHeader -> InspectSubtitle * Inspector: fix tabs spacing * Drawer: remove z-index * Update public/app/features/dashboard/components/Inspector/InspectSubtitle.tsx Co-Authored-By: Dominik Prokop * Drawer: apply PR feedbacks Co-authored-by: Dominik Prokop --- .../src/components/Drawer/Drawer.story.tsx | 2 + .../src/components/Drawer/Drawer.tsx | 65 ++++++++----- .../components/Inspector/InspectHeader.tsx | 97 ------------------- .../components/Inspector/InspectSubtitle.tsx | 57 +++++++++++ .../components/Inspector/PanelInspector.tsx | 26 ++--- 5 files changed, 108 insertions(+), 139 deletions(-) delete mode 100644 public/app/features/dashboard/components/Inspector/InspectHeader.tsx create mode 100644 public/app/features/dashboard/components/Inspector/InspectSubtitle.tsx diff --git a/packages/grafana-ui/src/components/Drawer/Drawer.story.tsx b/packages/grafana-ui/src/components/Drawer/Drawer.story.tsx index 75b82b3914c..26a4b59d9d1 100644 --- a/packages/grafana-ui/src/components/Drawer/Drawer.story.tsx +++ b/packages/grafana-ui/src/components/Drawer/Drawer.story.tsx @@ -33,6 +33,7 @@ export const global = () => { {state.isOpen && ( { updateValue({ isOpen: !state.isOpen }); }} @@ -70,6 +71,7 @@ export const longContent = () => { {state.isOpen && ( { updateValue({ isOpen: !state.isOpen }); diff --git a/packages/grafana-ui/src/components/Drawer/Drawer.tsx b/packages/grafana-ui/src/components/Drawer/Drawer.tsx index 9ef23bc23d3..ece1a62a474 100644 --- a/packages/grafana-ui/src/components/Drawer/Drawer.tsx +++ b/packages/grafana-ui/src/components/Drawer/Drawer.tsx @@ -1,21 +1,25 @@ -import React, { CSSProperties, FC, ReactNode } from 'react'; +import React, { CSSProperties, FC, ReactNode, useState } from 'react'; import { GrafanaTheme } from '@grafana/data'; import RcDrawer from 'rc-drawer'; import { css } from 'emotion'; import CustomScrollbar from '../CustomScrollbar/CustomScrollbar'; -import { Icon } from '../Icon/Icon'; +import { IconButton } from '../IconButton/IconButton'; import { stylesFactory, useTheme } from '../../themes'; export interface Props { children: ReactNode; /** Title shown at the top of the drawer */ - title?: JSX.Element | string; + title?: ReactNode; + /** Subtitle shown below the title */ + subtitle?: ReactNode; /** 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; + /** Should the Drawer be expandable to full width */ + expandable?: boolean; /** Set to true if the component rendered within in drawer content has its own scroll */ scrollableContent?: boolean; @@ -24,9 +28,6 @@ export interface Props { } const getStyles = stylesFactory((theme: GrafanaTheme, scollableContent: boolean) => { - const closeButtonWidth = '50px'; - const borderColor = theme.colors.border2; - return { drawer: css` .drawer-content { @@ -36,25 +37,23 @@ const getStyles = stylesFactory((theme: GrafanaTheme, scollableContent: boolean) overflow: hidden; } `, - titleWrapper: css` - font-size: ${theme.typography.size.lg}; - display: flex; - align-items: baseline; - 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}; - top: 0; + header: css` + background-color: ${theme.colors.bg2}; z-index: 1; flex-grow: 0; + padding-top: ${theme.spacing.xs}; `, - close: css` - cursor: pointer; - width: ${closeButtonWidth}; - height: 100%; + actions: css` display: flex; - flex-shrink: 0; - justify-content: center; + align-items: baseline; + justify-content: flex-end; + `, + titleWrapper: css` + margin-bottom: ${theme.spacing.lg}; + padding: 0 ${theme.spacing.sm} 0 ${theme.spacing.lg}; + `, + titleSpacing: css` + margin-bottom: ${theme.spacing.md}; `, content: css` padding: ${theme.spacing.md}; @@ -72,10 +71,14 @@ export const Drawer: FC = ({ closeOnMaskClick = false, scrollableContent = false, title, + subtitle, width = '40%', + expandable = false, }) => { const theme = useTheme(); const drawerStyles = getStyles(theme, scrollableContent); + const [isExpanded, setIsExpanded] = useState(false); + const currentWidth = isExpanded ? '100%' : width; return ( = ({ onClose={onClose} maskClosable={closeOnMaskClick} placement="right" - width={width} + width={currentWidth} getContainer={inline ? false : 'body'} style={{ position: `${inline && 'absolute'}` } as CSSProperties} className={drawerStyles.drawer} > {typeof title === 'string' && ( -
-
{title}
-
- +
+
+ {expandable && !isExpanded && ( + setIsExpanded(true)} surface="header" /> + )} + {expandable && isExpanded && ( + setIsExpanded(false)} surface="header" /> + )} + +
+
+

{title}

+ {typeof subtitle === 'string' &&
{subtitle}
} + {typeof subtitle !== 'string' && subtitle}
)} diff --git a/public/app/features/dashboard/components/Inspector/InspectHeader.tsx b/public/app/features/dashboard/components/Inspector/InspectHeader.tsx deleted file mode 100644 index 602a7b818ad..00000000000 --- a/public/app/features/dashboard/components/Inspector/InspectHeader.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React, { FC } from 'react'; -import { css } from 'emotion'; -import { stylesFactory, Tab, TabsBar, useTheme, IconButton } from '@grafana/ui'; -import { GrafanaTheme, SelectableValue, PanelData, getValueFormat, formattedValueToString } from '@grafana/data'; -import { InspectTab } from './PanelInspector'; -import { PanelModel } from '../../state'; - -interface Props { - tab: InspectTab; - tabs: Array<{ label: string; value: InspectTab }>; - panelData: PanelData; - panel: PanelModel; - isExpanded: boolean; - onSelectTab: (tab: SelectableValue) => void; - onClose: () => void; - onToggleExpand: () => void; -} - -export const InspectHeader: FC = ({ - tab, - tabs, - onSelectTab, - onClose, - onToggleExpand, - panel, - panelData, - isExpanded, -}) => { - const theme = useTheme(); - const styles = getStyles(theme); - - return ( -
-
- {!isExpanded && } - {isExpanded && } - -
-
-

{panel.title || 'Panel inspect'}

-
{formatStats(panelData)}
-
- - {tabs.map((t, index) => { - return ( - onSelectTab(t)} - /> - ); - })} - -
- ); -}; - -const getStyles = stylesFactory((theme: GrafanaTheme) => { - const headerBackground = theme.colors.bg2; - - return { - header: css` - background-color: ${headerBackground}; - z-index: 1; - flex-grow: 0; - padding-top: ${theme.spacing.sm}; - `, - actions: css` - position: absolute; - display: flex; - align-items: baseline; - justify-content: space-between; - right: ${theme.spacing.sm}; - `, - tabsBar: css` - padding-left: ${theme.spacing.md}; - `, - titleWrapper: css` - margin-bottom: ${theme.spacing.lg}; - padding: ${theme.spacing.sm} ${theme.spacing.sm} 0 ${theme.spacing.lg}; - `, - }; -}); - -function formatStats(panelData: PanelData) { - const { request } = panelData; - if (!request) { - return ''; - } - - const queryCount = request.targets.length; - const requestTime = request.endTime ? request.endTime - request.startTime : 0; - const formatted = formattedValueToString(getValueFormat('ms')(requestTime)); - - return `${queryCount} queries with total query time of ${formatted}`; -} diff --git a/public/app/features/dashboard/components/Inspector/InspectSubtitle.tsx b/public/app/features/dashboard/components/Inspector/InspectSubtitle.tsx new file mode 100644 index 00000000000..4c7822e1ab7 --- /dev/null +++ b/public/app/features/dashboard/components/Inspector/InspectSubtitle.tsx @@ -0,0 +1,57 @@ +import React, { FC } from 'react'; +import { css } from 'emotion'; +import { stylesFactory, Tab, TabsBar, useTheme } from '@grafana/ui'; +import { GrafanaTheme, SelectableValue, PanelData, getValueFormat, formattedValueToString } from '@grafana/data'; +import { InspectTab } from './PanelInspector'; + +interface Props { + tab: InspectTab; + tabs: Array<{ label: string; value: InspectTab }>; + panelData: PanelData; + onSelectTab: (tab: SelectableValue) => void; +} + +export const InspectSubtitle: FC = ({ tab, tabs, onSelectTab, panelData }) => { + const theme = useTheme(); + const styles = getStyles(theme); + + return ( + <> +
{formatStats(panelData)}
+ + {tabs.map((t, index) => { + return ( + onSelectTab(t)} + /> + ); + })} + + + ); +}; + +const getStyles = stylesFactory((theme: GrafanaTheme) => { + return { + tabsBar: css` + padding-left: ${theme.spacing.md}; + margin: ${theme.spacing.lg} -${theme.spacing.sm} -${theme.spacing.lg} -${theme.spacing.lg}; + `, + }; +}); + +function formatStats(panelData: PanelData) { + const { request } = panelData; + if (!request) { + return ''; + } + + const queryCount = request.targets.length; + const requestTime = request.endTime ? request.endTime - request.startTime : 0; + const formatted = formattedValueToString(getValueFormat('ms')(requestTime)); + + return `${queryCount} queries with total query time of ${formatted}`; +} diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx index 673238b0f95..88045d2b9f0 100644 --- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { Unsubscribable } from 'rxjs'; import { connect, MapStateToProps } from 'react-redux'; -import { InspectHeader } from './InspectHeader'; +import { InspectSubtitle } from './InspectSubtitle'; import { InspectJSONTab } from './InspectJSONTab'; import { QueryInspector } from './QueryInspector'; @@ -253,22 +253,10 @@ export class PanelInspectorUnconnected extends PureComponent { ); } - drawerHeader(tabs: Array<{ label: string; value: InspectTab }>, activeTab: InspectTab) { - const { panel } = this.props; + drawerSubtitle(tabs: Array<{ label: string; value: InspectTab }>, activeTab: InspectTab) { const { last } = this.state; - return ( - - ); + return ; } getTabs() { @@ -318,7 +306,13 @@ export class PanelInspectorUnconnected extends PureComponent { } return ( - + {activeTab === InspectTab.Data && this.renderDataTab()}