mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 01:53: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
|
||||
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';
|
||||
}
|
||||
|
||||
export function addNavbarContent(content: FC<Partial<OwnProps>>) {
|
||||
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<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,12 +214,75 @@ 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;
|
||||
|
||||
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 (
|
||||
<div className="navbar">
|
||||
{isFullscreen && this.renderBackButton()}
|
||||
@ -182,86 +311,7 @@ class DashNav extends PureComponent<Props> {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{customNavbarContent.map((Component, index) => (
|
||||
<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--actions">{this.renderRightActionsButton()}</div>
|
||||
|
||||
<div className="navbar-buttons navbar-buttons--tv">
|
||||
<DashNavButton tooltip="Cycle view mode" classSuffix="tv" icon="monitor" onClick={this.onToggleTVMode} />
|
||||
|
@ -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<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 (
|
||||
<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
|
||||
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>
|
||||
) : (
|
||||
<a className={`btn navbar-button navbar-button--${classSuffix}`} href={href}>
|
||||
{icon && <Icon name={icon} type={iconType} size="lg" />}
|
||||
{children}
|
||||
</a>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user