Commit f455f065 by Agnès Toulet Committed by GitHub

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
parent b0488259
// 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<FC<Partial<OwnProps>>> = [];
interface DashNavButtonModel {
show: (props: Props) => boolean;
component: FC<Partial<Props>>;
index?: number | 'end';
}
const customLeftActions: DashNavButtonModel[] = [];
const customRightActions: DashNavButtonModel[] = [];
export function addNavbarContent(content: FC<Partial<OwnProps>>) {
customNavbarContent.push(content);
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<Props> {
this.forceUpdate();
};
addCustomContent(actions: DashNavButtonModel[], buttons: ReactNode[]) {
actions.map((action, index) => {
const Component = action.component;
const element = <Component {...this.props} key={`button-custom-${index}`} />;
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(
<DashNavButton
tooltip="Mark as favorite"
classSuffix="star"
icon={isStarred ? 'favorite' : 'star'}
iconType={isStarred ? 'mono' : 'default'}
iconSize="lg"
noBorder={true}
onClick={this.onStarDashboard}
key="button-star"
/>
);
}
if (canShare) {
buttons.push(
<ModalsController key="button-share">
{({ showModal, hideModal }) => (
<DashNavButton
tooltip="Share dashboard"
classSuffix="share"
icon="share-alt"
iconSize="lg"
noBorder={true}
onClick={() => {
showModal(ShareModal, {
dashboard,
onDismiss: hideModal,
});
}}
/>
)}
</ModalsController>
);
}
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<Props> {
</a>
</div>
</div>
<div className="navbar-buttons navbar-buttons--actions">{this.renderLeftActionsButton()}</div>
<div className="navbar__spacer" />
</>
);
......@@ -148,46 +214,15 @@ class DashNav extends PureComponent<Props> {
);
}
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;
return (
<div className="navbar">
{isFullscreen && this.renderBackButton()}
{this.renderDashboardTitleSearchButton()}
{this.playlistSrv.isPlaying && (
<div className="navbar-buttons navbar-buttons--playlist">
<DashNavButton
tooltip="Go to previous dashboard"
classSuffix="tight"
icon="step-backward"
onClick={this.onPlaylistPrev}
/>
<DashNavButton
tooltip="Stop playlist"
classSuffix="tight"
icon="square-shape"
onClick={this.onPlaylistStop}
/>
<DashNavButton
tooltip="Go to next dashboard"
classSuffix="tight"
icon="forward"
onClick={this.onPlaylistNext}
/>
</div>
)}
{customNavbarContent.map((Component, index) => (
<Component {...this.props} key={`navbar-custom-content-${index}`} />
))}
<div className="navbar-buttons navbar-buttons--actions">
{canSave && (
const buttons: ReactNode[] = [];
if (canSave) {
buttons.push(
<DashNavButton
classSuffix="save"
tooltip="Add panel"
......@@ -195,39 +230,11 @@ class DashNav extends PureComponent<Props> {
onClick={onAddPanel}
iconType="mono"
iconSize="xl"
key="button-panel-add"
/>
)}
{canStar && (
<DashNavButton
tooltip="Mark as favorite"
classSuffix="star"
icon={isStarred ? 'favorite' : 'star'}
iconType={isStarred ? 'mono' : 'default'}
onClick={this.onStarDashboard}
/>
)}
{canShare && (
<ModalsController>
{({ showModal, hideModal }) => (
<DashNavButton
tooltip="Share dashboard"
classSuffix="share"
icon="share-alt"
onClick={() => {
showModal(ShareModal, {
dashboard,
onDismiss: hideModal,
});
}}
/>
)}
</ModalsController>
)}
{canSave && (
<ModalsController>
);
buttons.push(
<ModalsController key="button-save">
{({ showModal, hideModal }) => (
<DashNavButton
tooltip="Save dashboard"
......@@ -242,26 +249,69 @@ class DashNav extends PureComponent<Props> {
/>
)}
</ModalsController>
)}
);
}
{snapshotUrl && (
if (snapshotUrl) {
buttons.push(
<DashNavButton
tooltip="Open original dashboard"
classSuffix="snapshot-origin"
href={textUtil.sanitizeUrl(snapshotUrl)}
icon="link"
key="button-snapshot"
/>
)}
);
}
{showSettings && (
if (showSettings) {
buttons.push(
<DashNavButton
tooltip="Dashboard settings"
classSuffix="settings"
icon="cog"
onClick={this.onOpenSettings}
key="button-settings"
/>
);
}
this.addCustomContent(customRightActions, buttons);
return buttons;
}
render() {
const { dashboard, location, isFullscreen } = this.props;
return (
<div className="navbar">
{isFullscreen && this.renderBackButton()}
{this.renderDashboardTitleSearchButton()}
{this.playlistSrv.isPlaying && (
<div className="navbar-buttons navbar-buttons--playlist">
<DashNavButton
tooltip="Go to previous dashboard"
classSuffix="tight"
icon="step-backward"
onClick={this.onPlaylistPrev}
/>
<DashNavButton
tooltip="Stop playlist"
classSuffix="tight"
icon="square-shape"
onClick={this.onPlaylistStop}
/>
<DashNavButton
tooltip="Go to next dashboard"
classSuffix="tight"
icon="forward"
onClick={this.onPlaylistNext}
/>
)}
</div>
)}
<div className="navbar-buttons navbar-buttons--actions">{this.renderRightActionsButton()}</div>
<div className="navbar-buttons navbar-buttons--tv">
<DashNavButton tooltip="Cycle view mode" classSuffix="tv" icon="monitor" onClick={this.onToggleTVMode} />
......
// 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<Props> = ({
icon,
iconType,
......@@ -24,10 +34,31 @@ export const DashNavButton: FunctionComponent<Props> = ({
onClick,
href,
children,
noBorder,
}) => {
if (onClick) {
const theme = useTheme();
const styles = getStyles(theme);
if (noBorder) {
return (
<div className={styles.noBorderContainer}>
{icon && (
<IconButton
name={icon}
size={iconSize}
iconType={iconType}
tooltip={tooltip}
tooltipPlacement="bottom"
onClick={onClick}
/>
)}
{children}
</div>
);
}
return (
<Tooltip content={tooltip} placement="bottom">
{onClick ? (
<button
className={`btn navbar-button navbar-button--${classSuffix}`}
onClick={onClick}
......@@ -36,16 +67,12 @@ export const DashNavButton: FunctionComponent<Props> = ({
{icon && <Icon name={icon} type={iconType} size={iconSize || 'lg'} />}
{children}
</button>
</Tooltip>
);
}
return (
<Tooltip content={tooltip} placement="bottom">
) : (
<a className={`btn navbar-button navbar-button--${classSuffix}`} href={href}>
{icon && <Icon name={icon} type={iconType} size="lg" />}
{children}
</a>
)}
</Tooltip>
);
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment