mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 10:03:33 -06:00
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
This commit is contained in:
parent
b0488259d0
commit
f455f06540
@ -1,5 +1,5 @@
|
|||||||
// Libaries
|
// Libaries
|
||||||
import React, { FC, PureComponent } from 'react';
|
import React, { PureComponent, FC, ReactNode } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
// Utils & Services
|
// Utils & Services
|
||||||
@ -27,10 +27,21 @@ export interface OwnProps {
|
|||||||
onAddPanel: () => void;
|
onAddPanel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const customNavbarContent: Array<FC<Partial<OwnProps>>> = [];
|
interface DashNavButtonModel {
|
||||||
|
show: (props: Props) => boolean;
|
||||||
|
component: FC<Partial<Props>>;
|
||||||
|
index?: number | 'end';
|
||||||
|
}
|
||||||
|
|
||||||
export function addNavbarContent(content: FC<Partial<OwnProps>>) {
|
const customLeftActions: DashNavButtonModel[] = [];
|
||||||
customNavbarContent.push(content);
|
const customRightActions: DashNavButtonModel[] = [];
|
||||||
|
|
||||||
|
export function addCustomLeftAction(content: DashNavButtonModel) {
|
||||||
|
customLeftActions.push(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addCustomRightAction(content: DashNavButtonModel) {
|
||||||
|
customRightActions.push(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StateProps {
|
export interface StateProps {
|
||||||
@ -102,6 +113,60 @@ class DashNav extends PureComponent<Props> {
|
|||||||
this.forceUpdate();
|
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() {
|
renderDashboardTitleSearchButton() {
|
||||||
const { dashboard, isFullscreen } = this.props;
|
const { dashboard, isFullscreen } = this.props;
|
||||||
/* Hard-coded value so we don't have to wrap whole component in withTheme because of 1 variable */
|
/* 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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="navbar-buttons navbar-buttons--actions">{this.renderLeftActionsButton()}</div>
|
||||||
<div className="navbar__spacer" />
|
<div className="navbar__spacer" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -148,12 +214,75 @@ class DashNav extends PureComponent<Props> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderRightActionsButton() {
|
||||||
const { dashboard, onAddPanel, location, isFullscreen } = this.props;
|
const { dashboard, onAddPanel } = this.props;
|
||||||
const { canStar, canSave, canShare, showSettings, isStarred } = dashboard.meta;
|
const { canSave, showSettings } = dashboard.meta;
|
||||||
const { snapshot } = dashboard;
|
const { snapshot } = dashboard;
|
||||||
const snapshotUrl = snapshot && snapshot.originalUrl;
|
const snapshotUrl = snapshot && snapshot.originalUrl;
|
||||||
|
|
||||||
|
const buttons: ReactNode[] = [];
|
||||||
|
if (canSave) {
|
||||||
|
buttons.push(
|
||||||
|
<DashNavButton
|
||||||
|
classSuffix="save"
|
||||||
|
tooltip="Add panel"
|
||||||
|
icon="panel-add"
|
||||||
|
onClick={onAddPanel}
|
||||||
|
iconType="mono"
|
||||||
|
iconSize="xl"
|
||||||
|
key="button-panel-add"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
buttons.push(
|
||||||
|
<ModalsController key="button-save">
|
||||||
|
{({ showModal, hideModal }) => (
|
||||||
|
<DashNavButton
|
||||||
|
tooltip="Save dashboard"
|
||||||
|
classSuffix="save"
|
||||||
|
icon="save"
|
||||||
|
onClick={() => {
|
||||||
|
showModal(SaveDashboardModalProxy, {
|
||||||
|
dashboard,
|
||||||
|
onDismiss: hideModal,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ModalsController>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshotUrl) {
|
||||||
|
buttons.push(
|
||||||
|
<DashNavButton
|
||||||
|
tooltip="Open original dashboard"
|
||||||
|
classSuffix="snapshot-origin"
|
||||||
|
href={textUtil.sanitizeUrl(snapshotUrl)}
|
||||||
|
icon="link"
|
||||||
|
key="button-snapshot"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div className="navbar">
|
<div className="navbar">
|
||||||
{isFullscreen && this.renderBackButton()}
|
{isFullscreen && this.renderBackButton()}
|
||||||
@ -182,86 +311,7 @@ class DashNav extends PureComponent<Props> {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{customNavbarContent.map((Component, index) => (
|
<div className="navbar-buttons navbar-buttons--actions">{this.renderRightActionsButton()}</div>
|
||||||
<Component {...this.props} key={`navbar-custom-content-${index}`} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
<div className="navbar-buttons navbar-buttons--actions">
|
|
||||||
{canSave && (
|
|
||||||
<DashNavButton
|
|
||||||
classSuffix="save"
|
|
||||||
tooltip="Add panel"
|
|
||||||
icon="panel-add"
|
|
||||||
onClick={onAddPanel}
|
|
||||||
iconType="mono"
|
|
||||||
iconSize="xl"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{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>
|
|
||||||
{({ showModal, hideModal }) => (
|
|
||||||
<DashNavButton
|
|
||||||
tooltip="Save dashboard"
|
|
||||||
classSuffix="save"
|
|
||||||
icon="save"
|
|
||||||
onClick={() => {
|
|
||||||
showModal(SaveDashboardModalProxy, {
|
|
||||||
dashboard,
|
|
||||||
onDismiss: hideModal,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ModalsController>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{snapshotUrl && (
|
|
||||||
<DashNavButton
|
|
||||||
tooltip="Open original dashboard"
|
|
||||||
classSuffix="snapshot-origin"
|
|
||||||
href={textUtil.sanitizeUrl(snapshotUrl)}
|
|
||||||
icon="link"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showSettings && (
|
|
||||||
<DashNavButton
|
|
||||||
tooltip="Dashboard settings"
|
|
||||||
classSuffix="settings"
|
|
||||||
icon="cog"
|
|
||||||
onClick={this.onOpenSettings}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="navbar-buttons navbar-buttons--tv">
|
<div className="navbar-buttons navbar-buttons--tv">
|
||||||
<DashNavButton tooltip="Cycle view mode" classSuffix="tv" icon="monitor" onClick={this.onToggleTVMode} />
|
<DashNavButton tooltip="Cycle view mode" classSuffix="tv" icon="monitor" onClick={this.onToggleTVMode} />
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import React, { FunctionComponent } from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
|
import { css } from 'emotion';
|
||||||
// Components
|
// 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 { selectors } from '@grafana/e2e-selectors';
|
||||||
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
icon?: IconName;
|
icon?: IconName;
|
||||||
@ -13,8 +15,16 @@ interface Props {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
iconType?: IconType;
|
iconType?: IconType;
|
||||||
iconSize?: IconSize;
|
iconSize?: IconSize;
|
||||||
|
noBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
||||||
|
noBorderContainer: css`
|
||||||
|
padding: 0 ${theme.spacing.xs};
|
||||||
|
display: flex;
|
||||||
|
`,
|
||||||
|
}));
|
||||||
|
|
||||||
export const DashNavButton: FunctionComponent<Props> = ({
|
export const DashNavButton: FunctionComponent<Props> = ({
|
||||||
icon,
|
icon,
|
||||||
iconType,
|
iconType,
|
||||||
@ -24,10 +34,31 @@ export const DashNavButton: FunctionComponent<Props> = ({
|
|||||||
onClick,
|
onClick,
|
||||||
href,
|
href,
|
||||||
children,
|
children,
|
||||||
|
noBorder,
|
||||||
}) => {
|
}) => {
|
||||||
if (onClick) {
|
const theme = useTheme();
|
||||||
|
const styles = getStyles(theme);
|
||||||
|
|
||||||
|
if (noBorder) {
|
||||||
return (
|
return (
|
||||||
<Tooltip content={tooltip} placement="bottom">
|
<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
|
<button
|
||||||
className={`btn navbar-button navbar-button--${classSuffix}`}
|
className={`btn navbar-button navbar-button--${classSuffix}`}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
@ -36,16 +67,12 @@ export const DashNavButton: FunctionComponent<Props> = ({
|
|||||||
{icon && <Icon name={icon} type={iconType} size={iconSize || 'lg'} />}
|
{icon && <Icon name={icon} type={iconType} size={iconSize || 'lg'} />}
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
) : (
|
||||||
);
|
<a className={`btn navbar-button navbar-button--${classSuffix}`} href={href}>
|
||||||
}
|
{icon && <Icon name={icon} type={iconType} size="lg" />}
|
||||||
|
{children}
|
||||||
return (
|
</a>
|
||||||
<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>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user