TopNav: Make dashboard toolbar actions work in new top nav (#51950)

* Initial work on new toolbar button

* Minor step

* Small progress

* Minor progress

* Minor fix

* removed console.log

* Removing stuff we don't need yet
pull/52271/head
Torkel Ödegaard 3 years ago committed by GitHub
parent ecdd4a184f
commit 7947629f82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      packages/grafana-data/src/themes/createTheme.ts
  2. 2
      packages/grafana-data/src/themes/types.ts
  3. 3
      packages/grafana-runtime/src/config.ts
  4. 31
      packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.tsx
  5. 2
      public/app/AppWrapper.tsx
  6. 3
      public/app/angular/react2angular.ts
  7. 2
      public/app/core/components/AppChrome/Breadcrumbs.tsx
  8. 23
      public/app/core/components/AppChrome/NavToolbar.tsx
  9. 38
      public/app/core/components/AppChrome/NavToolbarSeparator.tsx
  10. 6
      public/app/core/services/ModalManager.ts
  11. 20
      public/app/core/utils/ConfigProvider.tsx
  12. 17
      public/app/features/dashboard/components/DashNav/DashNav.tsx
  13. 2
      public/app/plugins/panel/graph/graph.ts

@ -55,6 +55,7 @@ export function createTheme(options: NewThemeOptions = {}): GrafanaTheme2 {
zIndex: { zIndex: {
...zIndex, ...zIndex,
}, },
flags: {},
}; };
return { return {

@ -30,6 +30,8 @@ export interface GrafanaTheme2 {
visualization: ThemeVisualizationColors; visualization: ThemeVisualizationColors;
transitions: ThemeTransitions; transitions: ThemeTransitions;
v1: GrafanaTheme; v1: GrafanaTheme;
/** feature flags that might impact component looks */
flags: { topnav?: boolean };
} }
/** @alpha */ /** @alpha */

@ -169,6 +169,9 @@ export class GrafanaBootConfig implements GrafanaConfig {
} }
overrideFeatureTogglesFromUrl(this); overrideFeatureTogglesFromUrl(this);
// Special feature toggle that impact theme/component looks
this.theme2.flags.topnav = this.featureToggles.topnav;
} }
} }

@ -127,6 +127,27 @@ const getStyles = (theme: GrafanaTheme2) => {
const primaryVariant = getPropertiesForVariant(theme, 'primary', 'solid'); const primaryVariant = getPropertiesForVariant(theme, 'primary', 'solid');
const destructiveVariant = getPropertiesForVariant(theme, 'destructive', 'solid'); const destructiveVariant = getPropertiesForVariant(theme, 'destructive', 'solid');
const defaultOld = css`
color: ${theme.colors.text.secondary};
background-color: ${theme.colors.background.primary};
&:hover {
color: ${theme.colors.text.primary};
background: ${theme.colors.background.secondary};
}
`;
const defaultTopNav = css`
color: ${theme.colors.text.secondary};
background-color: transparent;
border: none;
&:hover {
color: ${theme.colors.text.primary};
background: ${theme.colors.background.secondary};
}
`;
return { return {
button: css` button: css`
label: toolbar-button; label: toolbar-button;
@ -172,15 +193,7 @@ const getStyles = (theme: GrafanaTheme2) => {
} }
} }
`, `,
default: css` default: theme.flags.topnav ? defaultTopNav : defaultOld,
color: ${theme.colors.text.secondary};
background-color: ${theme.colors.background.primary};
&:hover {
color: ${theme.colors.text.primary};
background: ${theme.colors.background.secondary};
}
`,
active: css` active: css`
color: ${theme.v1.palette.orangeDark}; color: ${theme.v1.palette.orangeDark};
border-color: ${theme.v1.palette.orangeDark}; border-color: ${theme.v1.palette.orangeDark};

@ -115,7 +115,7 @@ export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState
<I18nProvider> <I18nProvider>
<ErrorBoundaryAlert style="page"> <ErrorBoundaryAlert style="page">
<ConfigContext.Provider value={config}> <ConfigContext.Provider value={config}>
<ThemeProvider> <ThemeProvider value={config.theme2}>
<KBarProvider <KBarProvider
actions={[]} actions={[]}
options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }} options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }}

@ -1,3 +1,4 @@
import { config } from '@grafana/runtime';
import coreModule from 'app/angular/core_module'; import coreModule from 'app/angular/core_module';
import { provideTheme } from 'app/core/utils/ConfigProvider'; import { provideTheme } from 'app/core/utils/ConfigProvider';
@ -5,7 +6,7 @@ export function react2AngularDirective(name: string, component: any, options: an
coreModule.directive(name, [ coreModule.directive(name, [
'reactDirective', 'reactDirective',
(reactDirective) => { (reactDirective) => {
return reactDirective(provideTheme(component), options); return reactDirective(provideTheme(component, config.theme2), options);
}, },
]); ]);
} }

@ -60,6 +60,7 @@ const getStyles = (theme: GrafanaTheme2) => {
breadcrumbs: css({ breadcrumbs: css({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
flexWrap: 'nowrap',
fontWeight: theme.typography.fontWeightMedium, fontWeight: theme.typography.fontWeightMedium,
}), }),
breadcrumb: css({ breadcrumb: css({
@ -72,6 +73,7 @@ const getStyles = (theme: GrafanaTheme2) => {
}), }),
breadcrumbLink: css({ breadcrumbLink: css({
color: theme.colors.text.primary, color: theme.colors.text.primary,
whiteSpace: 'nowrap',
'&:hover': { '&:hover': {
textDecoration: 'underline', textDecoration: 'underline',
}, },

@ -2,9 +2,10 @@ import { css } from '@emotion/css';
import React from 'react'; import React from 'react';
import { GrafanaTheme2, NavModelItem } from '@grafana/data'; import { GrafanaTheme2, NavModelItem } from '@grafana/data';
import { IconButton, ToolbarButton, useStyles2 } from '@grafana/ui'; import { Icon, IconButton, ToolbarButton, useStyles2 } from '@grafana/ui';
import { Breadcrumbs } from './Breadcrumbs'; import { Breadcrumbs } from './Breadcrumbs';
import { NavToolbarSeparator } from './NavToolbarSeparator';
import { TOP_BAR_LEVEL_HEIGHT } from './types'; import { TOP_BAR_LEVEL_HEIGHT } from './types';
export interface Props { export interface Props {
@ -32,10 +33,12 @@ export function NavToolbar({
<IconButton name="bars" tooltip="Toggle menu" tooltipPlacement="bottom" size="xl" onClick={onToggleMegaMenu} /> <IconButton name="bars" tooltip="Toggle menu" tooltipPlacement="bottom" size="xl" onClick={onToggleMegaMenu} />
</div> </div>
<Breadcrumbs sectionNav={sectionNav} pageNav={pageNav} /> <Breadcrumbs sectionNav={sectionNav} pageNav={pageNav} />
<div className={styles.leftActions}></div> <div className={styles.actions}>
<div className={styles.rightActions}>
{actions} {actions}
<ToolbarButton icon={searchBarHidden ? 'angle-down' : 'angle-up'} onClick={onToggleSearchBar} /> {actions && <NavToolbarSeparator />}
<ToolbarButton onClick={onToggleSearchBar} narrow tooltip="Toggle top search bar">
<Icon name={searchBarHidden ? 'angle-down' : 'angle-up'} size="xl" />
</ToolbarButton>
</div> </div>
</div> </div>
); );
@ -55,16 +58,14 @@ const getStyles = (theme: GrafanaTheme2) => {
alignItems: 'center', alignItems: 'center',
paddingRight: theme.spacing(1), paddingRight: theme.spacing(1),
}), }),
leftActions: css({ actions: css({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
flexWrap: 'nowrap',
justifyContent: 'flex-end',
paddingLeft: theme.spacing(1),
flexGrow: 1, flexGrow: 1,
gap: theme.spacing(2), gap: theme.spacing(0.5),
}),
rightActions: css({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(2),
}), }),
}; };
}; };

@ -0,0 +1,38 @@
import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui';
export interface Props {
leftActionsSeparator?: boolean;
}
export function NavToolbarSeparator({ leftActionsSeparator }: Props) {
const styles = useStyles2(getStyles);
if (leftActionsSeparator) {
return <div className={styles.leftActionsSeparator} />;
}
if (config.featureToggles.topnav) {
return <div className={styles.line} />;
}
return null;
}
const getStyles = (theme: GrafanaTheme2) => {
return {
leftActionsSeparator: css({
display: 'flex',
flexGrow: 1,
}),
line: css({
width: 1,
backgroundColor: theme.colors.border.medium,
height: 24,
}),
};
};

@ -2,7 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { textUtil } from '@grafana/data'; import { textUtil } from '@grafana/data';
import { CopyPanelEvent } from '@grafana/runtime'; import { config, CopyPanelEvent } from '@grafana/runtime';
import { ConfirmModal, ConfirmModalProps } from '@grafana/ui'; import { ConfirmModal, ConfirmModalProps } from '@grafana/ui';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import { copyPanel } from 'app/features/dashboard/utils/panel'; import { copyPanel } from 'app/features/dashboard/utils/panel';
@ -32,7 +32,7 @@ export class ModalManager {
}, },
}; };
const elem = React.createElement(provideTheme(AngularModalProxy), modalProps); const elem = React.createElement(provideTheme(AngularModalProxy, config.theme2), modalProps);
this.reactModalRoot.appendChild(this.reactModalNode); this.reactModalRoot.appendChild(this.reactModalNode);
ReactDOM.render(elem, this.reactModalNode); ReactDOM.render(elem, this.reactModalNode);
} }
@ -83,7 +83,7 @@ export class ModalManager {
props, props,
}; };
const elem = React.createElement(provideTheme(AngularModalProxy), modalProps); const elem = React.createElement(provideTheme(AngularModalProxy, config.theme2), modalProps);
this.reactModalRoot.appendChild(this.reactModalNode); this.reactModalRoot.appendChild(this.reactModalNode);
ReactDOM.render(elem, this.reactModalNode); ReactDOM.render(elem, this.reactModalNode);
} }

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { createTheme } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { config, GrafanaBootConfig, ThemeChangedEvent } from '@grafana/runtime'; import { config, GrafanaBootConfig, ThemeChangedEvent } from '@grafana/runtime';
import { ThemeContext } from '@grafana/ui'; import { ThemeContext } from '@grafana/ui';
@ -16,8 +16,8 @@ export const provideConfig = (component: React.ComponentType<any>) => {
return ConfigProvider; return ConfigProvider;
}; };
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { export const ThemeProvider = ({ children, value }: { children: React.ReactNode; value: GrafanaTheme2 }) => {
const [theme, setTheme] = useState(getCurrentUserTheme()); const [theme, setTheme] = useState(value);
useEffect(() => { useEffect(() => {
const sub = appEvents.subscribe(ThemeChangedEvent, (event) => { const sub = appEvents.subscribe(ThemeChangedEvent, (event) => {
@ -31,14 +31,8 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>; return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
}; };
function getCurrentUserTheme() { export const provideTheme = (component: React.ComponentType<any>, theme: GrafanaTheme2) => {
return createTheme({ return provideConfig((props: any) => (
colors: { <ThemeProvider value={theme}>{React.createElement(component, { ...props })}</ThemeProvider>
mode: config.bootData.user.lightTheme ? 'light' : 'dark', ));
},
});
}
export const provideTheme = (component: React.ComponentType<any>) => {
return provideConfig((props: any) => <ThemeProvider>{React.createElement(component, { ...props })}</ThemeProvider>);
}; };

@ -6,6 +6,7 @@ import { locationUtil, textUtil } from '@grafana/data';
import { locationService } from '@grafana/runtime'; import { locationService } from '@grafana/runtime';
import { ButtonGroup, ModalsController, ToolbarButton, PageToolbar, useForceUpdate } from '@grafana/ui'; import { ButtonGroup, ModalsController, ToolbarButton, PageToolbar, useForceUpdate } from '@grafana/ui';
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate'; import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbarSeparator';
import config from 'app/core/config'; import config from 'app/core/config';
import { toggleKioskMode } from 'app/core/navigation/kiosk'; import { toggleKioskMode } from 'app/core/navigation/kiosk';
import { DashboardCommentsModal } from 'app/features/dashboard/components/DashboardComments/DashboardCommentsModal'; import { DashboardCommentsModal } from 'app/features/dashboard/components/DashboardComments/DashboardCommentsModal';
@ -109,7 +110,7 @@ export const DashNav = React.memo<Props>((props) => {
return playlistSrv.isPlaying; return playlistSrv.isPlaying;
}; };
const renderLeftActionsButton = () => { const renderLeftActions = () => {
const { dashboard, kioskMode } = props; const { dashboard, kioskMode } = props;
const { canStar, canShare, isStarred } = dashboard.meta; const { canStar, canShare, isStarred } = dashboard.meta;
const buttons: ReactNode[] = []; const buttons: ReactNode[] = [];
@ -199,7 +200,7 @@ export const DashNav = React.memo<Props>((props) => {
); );
}; };
const renderRightActionsButton = () => { const renderRightActions = () => {
const { dashboard, onAddPanel, isFullscreen, kioskMode } = props; const { dashboard, onAddPanel, isFullscreen, kioskMode } = props;
const { canSave, canEdit, showSettings } = dashboard.meta; const { canSave, canEdit, showSettings } = dashboard.meta;
const { snapshot } = dashboard; const { snapshot } = dashboard;
@ -279,7 +280,13 @@ export const DashNav = React.memo<Props>((props) => {
return ( return (
<AppChromeUpdate <AppChromeUpdate
pageNav={{ text: title }} pageNav={{ text: title }}
actions={<ToolbarButton onClick={onOpenSettings} icon="cog"></ToolbarButton>} actions={
<>
{renderLeftActions()}
<NavToolbarSeparator leftActionsSeparator />
{renderRightActions()}
</>
}
/> />
); );
} }
@ -292,9 +299,9 @@ export const DashNav = React.memo<Props>((props) => {
titleHref={titleHref} titleHref={titleHref}
parentHref={parentHref} parentHref={parentHref}
onGoBack={onGoBack} onGoBack={onGoBack}
leftItems={renderLeftActionsButton()} leftItems={renderLeftActions()}
> >
{renderRightActionsButton()} {renderRightActions()}
</PageToolbar> </PageToolbar>
); );
}); });

@ -58,7 +58,7 @@ import { ThresholdManager } from './threshold_manager';
import { TimeRegionManager } from './time_region_manager'; import { TimeRegionManager } from './time_region_manager';
import { isLegacyGraphHoverEvent } from './utils'; import { isLegacyGraphHoverEvent } from './utils';
const LegendWithThemeProvider = provideTheme(Legend); const LegendWithThemeProvider = provideTheme(Legend, config.theme2);
class GraphElement { class GraphElement {
ctrl: GraphCtrl; ctrl: GraphCtrl;

Loading…
Cancel
Save