From f455f06540fceeecfd0550548143a4e97daed4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agn=C3=A8s=20Toulet?= <35176601+AgnesToulet@users.noreply.github.com> Date: Mon, 27 Apr 2020 13:30:55 +0200 Subject: [PATCH] DashNav: refactor action buttons and custom content (#23868) * DashNav: refactor action buttons and custom content * DashNav: remove code duplication * DashNav: fix custom element display * DashNav: fix strictNullChecks --- .../dashboard/components/DashNav/DashNav.tsx | 224 +++++++++++------- .../components/DashNav/DashNavButton.tsx | 53 ++++- 2 files changed, 177 insertions(+), 100 deletions(-) diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index d0a0c395132..3e63ebe1cf1 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -1,5 +1,5 @@ // Libaries -import React, { FC, PureComponent } from 'react'; +import React, { PureComponent, FC, ReactNode } from 'react'; import { connect } from 'react-redux'; import { css } from 'emotion'; // Utils & Services @@ -27,10 +27,21 @@ export interface OwnProps { onAddPanel: () => void; } -const customNavbarContent: Array>> = []; +interface DashNavButtonModel { + show: (props: Props) => boolean; + component: FC>; + index?: number | 'end'; +} -export function addNavbarContent(content: FC>) { - customNavbarContent.push(content); +const customLeftActions: DashNavButtonModel[] = []; +const customRightActions: DashNavButtonModel[] = []; + +export function addCustomLeftAction(content: DashNavButtonModel) { + customLeftActions.push(content); +} + +export function addCustomRightAction(content: DashNavButtonModel) { + customRightActions.push(content); } export interface StateProps { @@ -102,6 +113,60 @@ class DashNav extends PureComponent { this.forceUpdate(); }; + addCustomContent(actions: DashNavButtonModel[], buttons: ReactNode[]) { + actions.map((action, index) => { + const Component = action.component; + const element = ; + typeof action.index === 'number' ? buttons.splice(action.index, 0, element) : buttons.push(element); + }); + } + + renderLeftActionsButton() { + const { dashboard } = this.props; + const { canStar, canShare, isStarred } = dashboard.meta; + + const buttons: ReactNode[] = []; + if (canStar) { + buttons.push( + + ); + } + + if (canShare) { + buttons.push( + + {({ showModal, hideModal }) => ( + { + showModal(ShareModal, { + dashboard, + onDismiss: hideModal, + }); + }} + /> + )} + + ); + } + + this.addCustomContent(customLeftActions, buttons); + return buttons; + } + renderDashboardTitleSearchButton() { const { dashboard, isFullscreen } = this.props; /* Hard-coded value so we don't have to wrap whole component in withTheme because of 1 variable */ @@ -135,6 +200,7 @@ class DashNav extends PureComponent { +
{this.renderLeftActionsButton()}
); @@ -148,12 +214,75 @@ class DashNav extends PureComponent { ); } - render() { - const { dashboard, onAddPanel, location, isFullscreen } = this.props; - const { canStar, canSave, canShare, showSettings, isStarred } = dashboard.meta; + renderRightActionsButton() { + const { dashboard, onAddPanel } = this.props; + const { canSave, showSettings } = dashboard.meta; const { snapshot } = dashboard; const snapshotUrl = snapshot && snapshot.originalUrl; + const buttons: ReactNode[] = []; + if (canSave) { + buttons.push( + + ); + buttons.push( + + {({ showModal, hideModal }) => ( + { + showModal(SaveDashboardModalProxy, { + dashboard, + onDismiss: hideModal, + }); + }} + /> + )} + + ); + } + + if (snapshotUrl) { + buttons.push( + + ); + } + + if (showSettings) { + buttons.push( + + ); + } + + this.addCustomContent(customRightActions, buttons); + return buttons; + } + + render() { + const { dashboard, location, isFullscreen } = this.props; + return (
{isFullscreen && this.renderBackButton()} @@ -182,86 +311,7 @@ class DashNav extends PureComponent {
)} - {customNavbarContent.map((Component, index) => ( - - ))} - -
- {canSave && ( - - )} - - {canStar && ( - - )} - - {canShare && ( - - {({ showModal, hideModal }) => ( - { - showModal(ShareModal, { - dashboard, - onDismiss: hideModal, - }); - }} - /> - )} - - )} - - {canSave && ( - - {({ showModal, hideModal }) => ( - { - showModal(SaveDashboardModalProxy, { - dashboard, - onDismiss: hideModal, - }); - }} - /> - )} - - )} - - {snapshotUrl && ( - - )} - - {showSettings && ( - - )} -
+
{this.renderRightActionsButton()}
diff --git a/public/app/features/dashboard/components/DashNav/DashNavButton.tsx b/public/app/features/dashboard/components/DashNav/DashNavButton.tsx index cc49b496be9..9eca8d3a64f 100644 --- a/public/app/features/dashboard/components/DashNav/DashNavButton.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNavButton.tsx @@ -1,8 +1,10 @@ // Libraries import React, { FunctionComponent } from 'react'; +import { css } from 'emotion'; // Components -import { Icon, IconName, IconSize, IconType, Tooltip } from '@grafana/ui'; +import { Tooltip, Icon, IconName, IconType, IconSize, IconButton, useTheme, stylesFactory } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; +import { GrafanaTheme } from '@grafana/data'; interface Props { icon?: IconName; @@ -13,8 +15,16 @@ interface Props { children?: React.ReactNode; iconType?: IconType; iconSize?: IconSize; + noBorder?: boolean; } +const getStyles = stylesFactory((theme: GrafanaTheme) => ({ + noBorderContainer: css` + padding: 0 ${theme.spacing.xs}; + display: flex; + `, +})); + export const DashNavButton: FunctionComponent = ({ icon, iconType, @@ -24,10 +34,31 @@ export const DashNavButton: FunctionComponent = ({ onClick, href, children, + noBorder, }) => { - if (onClick) { + const theme = useTheme(); + const styles = getStyles(theme); + + if (noBorder) { return ( - +
+ {icon && ( + + )} + {children} +
+ ); + } + return ( + + {onClick ? ( - - ); - } - - return ( - - - {icon && } - {children} - + ) : ( + + {icon && } + {children} + + )} ); };