mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PageToolbar: Extracting navbar styles & layout into a modern emotion based component (#30588)
* Explore: Replaces navbar-button and overriden explore button css classes with ToolbarButton and cleans up scss & markup, removes ResponsiveButton * Change live button text when paused * For the dashboard toolbar button I need a transparent button so I refactored the states/variants into a new ToolbarButtonVariatn * PageToolbar wip * Progress * Prgress * Minor progress * Fixed back button and responsive titles * Fixed tv mode * Updated * support tv modes and playlist * more progress * Fixing lots of view states and responsive features * Minor fixes * review fixes * Fixes to e2e tests * Review fixes
This commit is contained in:
@@ -8,7 +8,7 @@ export const smokeTestScenario = {
|
||||
skipScenario: false,
|
||||
scenario: () => {
|
||||
e2e.flows.openDashboard();
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click();
|
||||
e2e.components.PageToolbar.item('Add panel').click();
|
||||
e2e.pages.AddDashboard.addNewPanel().click();
|
||||
|
||||
e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer()
|
||||
|
@@ -38,7 +38,7 @@ e2e.scenario({
|
||||
);
|
||||
}
|
||||
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click();
|
||||
e2e.components.PageToolbar.item('Dashboard settings').click();
|
||||
|
||||
e2e.components.TimeZonePicker.container()
|
||||
.should('be.visible')
|
||||
|
@@ -8,7 +8,7 @@ e2e.scenario({
|
||||
skipScenario: false,
|
||||
scenario: () => {
|
||||
e2e.flows.openDashboard({ uid: '5SdHCadmz' });
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click();
|
||||
e2e.components.PageToolbar.item('Dashboard settings').click();
|
||||
|
||||
e2e.components.FolderPicker.container()
|
||||
.should('be.visible')
|
||||
|
@@ -50,7 +50,7 @@ e2e.scenario({
|
||||
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('p2').should('be.visible').click();
|
||||
|
||||
e2e.pages.Dashboard.Toolbar.navBar().click();
|
||||
e2e.components.PageToolbar.container().click();
|
||||
|
||||
e2e.components.DashboardLinks.dropDown().should('be.visible').click().wait('@tagsTemplatingSearch');
|
||||
|
||||
|
@@ -20,7 +20,7 @@ describe('Variables - Set options from ui', () => {
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible').click();
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible').click();
|
||||
|
||||
e2e.pages.Dashboard.Toolbar.navBar().click();
|
||||
e2e.components.PageToolbar.container().click();
|
||||
|
||||
e2e().wait('@query');
|
||||
|
||||
@@ -77,7 +77,7 @@ describe('Variables - Set options from ui', () => {
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A').should('be.visible').click();
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible').click();
|
||||
|
||||
e2e.pages.Dashboard.Toolbar.navBar().click();
|
||||
e2e.components.PageToolbar.container().click();
|
||||
|
||||
e2e().wait('@query');
|
||||
e2e().wait(500);
|
||||
@@ -132,7 +132,7 @@ describe('Variables - Set options from ui', () => {
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A + B').should('be.visible').click();
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible').click();
|
||||
|
||||
e2e.pages.Dashboard.Toolbar.navBar().click();
|
||||
e2e.components.PageToolbar.container().click();
|
||||
|
||||
e2e().wait('@query');
|
||||
e2e().wait(500);
|
||||
|
@@ -185,7 +185,7 @@ function copyExistingDashboard() {
|
||||
}
|
||||
|
||||
function saveDashboard(saveVariables: boolean) {
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Save dashboard').should('be.visible').click();
|
||||
e2e.components.PageToolbar.item('Save dashboard').should('be.visible').click();
|
||||
|
||||
if (saveVariables) {
|
||||
e2e.pages.SaveDashboardModal.saveVariables().should('exist').click({ force: true });
|
||||
@@ -212,7 +212,7 @@ function validateTextboxAndMarkup(value: string) {
|
||||
}
|
||||
|
||||
function validateVariable(value: string) {
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').should('be.visible').click();
|
||||
e2e.components.PageToolbar.item('Dashboard settings').should('be.visible').click();
|
||||
|
||||
e2e.pages.Dashboard.Settings.General.sectionItems('Variables').should('be.visible').click();
|
||||
|
||||
@@ -245,7 +245,7 @@ function changeTextBoxInput() {
|
||||
}
|
||||
|
||||
function changeQueryInput() {
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').should('be.visible').click();
|
||||
e2e.components.PageToolbar.item('Dashboard settings').should('be.visible').click();
|
||||
|
||||
e2e.pages.Dashboard.Settings.General.sectionItems('Variables').should('be.visible').click();
|
||||
|
||||
|
@@ -56,8 +56,8 @@ export const Components = {
|
||||
},
|
||||
OptionsPane: {
|
||||
content: 'Panel editor option pane content',
|
||||
close: 'Dashboard navigation bar button Close options pane',
|
||||
open: 'Dashboard navigation bar button Open options pane',
|
||||
close: 'Page toolbar button Close options pane',
|
||||
open: 'Page toolbar button Open options pane',
|
||||
select: 'Panel editor option pane select',
|
||||
tab: (title: string) => `Panel editor option pane tab ${title}`,
|
||||
},
|
||||
@@ -123,6 +123,10 @@ export const Components = {
|
||||
calculationsLabel: 'Transform calculations label',
|
||||
},
|
||||
},
|
||||
PageToolbar: {
|
||||
container: () => '.page-toolbar',
|
||||
item: (tooltip: string) => `Page toolbar button ${tooltip}`,
|
||||
},
|
||||
QueryEditorToolbarItem: {
|
||||
button: (title: string) => `QueryEditor toolbar item button ${title}`,
|
||||
},
|
||||
|
@@ -34,10 +34,6 @@ export const Pages = {
|
||||
},
|
||||
Dashboard: {
|
||||
url: (uid: string) => `/d/${uid}`,
|
||||
Toolbar: {
|
||||
toolbarItems: (button: string) => `Dashboard navigation bar button ${button}`,
|
||||
navBar: () => '.navbar',
|
||||
},
|
||||
SubMenu: {
|
||||
submenuItem: 'Dashboard template variables submenu item',
|
||||
submenuItemLabels: (item: string) => `Dashboard template variables submenu Label ${item}`,
|
||||
|
@@ -59,7 +59,7 @@ export const addDashboard = (config?: Partial<AddDashboardConfig>) => {
|
||||
e2e.pages.AddDashboard.visit();
|
||||
|
||||
if (annotations.length > 0 || variables.length > 0) {
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click();
|
||||
e2e.components.PageToolbar.item('Dashboard settings').click();
|
||||
addAnnotations(annotations);
|
||||
|
||||
fullConfig.variables = addVariables(variables);
|
||||
@@ -69,7 +69,7 @@ export const addDashboard = (config?: Partial<AddDashboardConfig>) => {
|
||||
|
||||
setDashboardTimeRange(timeRange);
|
||||
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Save dashboard').click();
|
||||
e2e.components.PageToolbar.item('Save dashboard').click();
|
||||
e2e.pages.SaveDashboardAsModal.newName().clear().type(title);
|
||||
e2e.pages.SaveDashboardAsModal.save().click();
|
||||
e2e.flows.assertSuccessNotification();
|
||||
|
@@ -99,7 +99,7 @@ export const configurePanel = (config: PartialAddPanelConfig | PartialEditPanelC
|
||||
e2e.components.Panels.Panel.title(panelTitle).click();
|
||||
e2e.components.Panels.Panel.headerItems('Edit').click();
|
||||
} else {
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click();
|
||||
e2e.components.PageToolbar.item('Add panel').click();
|
||||
e2e.pages.AddDashboard.addNewPanel().click();
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ const quickDelete = (uid: string) => {
|
||||
|
||||
const uiDelete = (uid: string, title: string) => {
|
||||
e2e.pages.Dashboard.visit(uid);
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Dashboard settings').click();
|
||||
e2e.components.PageToolbar.item('Dashboard settings').click();
|
||||
e2e.pages.Dashboard.Settings.General.deleteDashBoard().click();
|
||||
e2e.pages.ConfirmModal.delete().click();
|
||||
e2e.flows.assertSuccessNotification();
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { e2e } from '../index';
|
||||
|
||||
export const saveDashboard = () => {
|
||||
e2e.pages.Dashboard.Toolbar.toolbarItems('Save dashboard').click();
|
||||
e2e.components.PageToolbar.item('Save dashboard').click();
|
||||
|
||||
e2e.pages.SaveDashboardModal.save().click();
|
||||
|
||||
|
@@ -4,4 +4,4 @@ import { setTimeRange, TimeRangeConfig } from './setTimeRange';
|
||||
export { TimeRangeConfig };
|
||||
|
||||
export const setDashboardTimeRange = (config: TimeRangeConfig) =>
|
||||
e2e.pages.Dashboard.Toolbar.navBar().within(() => setTimeRange(config));
|
||||
e2e.components.PageToolbar.container().within(() => setTimeRange(config));
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { ToolbarButton, ButtonGroup, useTheme, VerticalGroup, HorizontalGroup } from '@grafana/ui';
|
||||
import { ToolbarButton, ButtonGroup, VerticalGroup, HorizontalGroup } from '@grafana/ui';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { ToolbarButtonRow } from './ToolbarButtonRow';
|
||||
import { ToolbarButtonVariant } from './ToolbarButton';
|
||||
import { DashboardStoryCanvas } from '../../utils/storybook/DashboardStoryCanvas';
|
||||
|
||||
export default {
|
||||
title: 'Buttons/ToolbarButton',
|
||||
@@ -12,11 +13,10 @@ export default {
|
||||
};
|
||||
|
||||
export const List = () => {
|
||||
const theme = useTheme();
|
||||
const variants: ToolbarButtonVariant[] = ['default', 'active', 'primary', 'destructive'];
|
||||
|
||||
return (
|
||||
<div style={{ background: theme.colors.dashboardBg, padding: '32px' }}>
|
||||
<DashboardStoryCanvas>
|
||||
<VerticalGroup>
|
||||
Button states
|
||||
<ToolbarButtonRow>
|
||||
@@ -47,6 +47,22 @@ export const List = () => {
|
||||
))}
|
||||
</ToolbarButtonRow>
|
||||
<br />
|
||||
disabled
|
||||
<ToolbarButtonRow>
|
||||
<ToolbarButton icon="sync" disabled>
|
||||
Disabled
|
||||
</ToolbarButton>
|
||||
</ToolbarButtonRow>
|
||||
<br />
|
||||
Variants
|
||||
<ToolbarButtonRow>
|
||||
{variants.map((variant) => (
|
||||
<ToolbarButton icon="sync" tooltip="Sync" variant={variant} key={variant}>
|
||||
{variant}
|
||||
</ToolbarButton>
|
||||
))}
|
||||
</ToolbarButtonRow>
|
||||
<br />
|
||||
Wrapped in noSpacing ButtonGroup
|
||||
<ButtonGroup>
|
||||
<ToolbarButton icon="clock-nine" tooltip="Time picker">
|
||||
@@ -76,6 +92,6 @@ export const List = () => {
|
||||
</ButtonGroup>
|
||||
</HorizontalGroup>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
</DashboardStoryCanvas>
|
||||
);
|
||||
};
|
||||
|
@@ -7,6 +7,7 @@ import { Tooltip } from '../Tooltip/Tooltip';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { getPropertiesForVariant } from './Button';
|
||||
import { isString } from 'lodash';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
export interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
/** Icon name */
|
||||
@@ -31,7 +32,20 @@ export type ToolbarButtonVariant = 'default' | 'primary' | 'destructive' | 'acti
|
||||
|
||||
export const ToolbarButton = forwardRef<HTMLButtonElement, Props>(
|
||||
(
|
||||
{ tooltip, icon, className, children, imgSrc, fullWidth, isOpen, narrow, variant = 'default', iconOnly, ...rest },
|
||||
{
|
||||
tooltip,
|
||||
icon,
|
||||
className,
|
||||
children,
|
||||
imgSrc,
|
||||
fullWidth,
|
||||
isOpen,
|
||||
narrow,
|
||||
variant = 'default',
|
||||
iconOnly,
|
||||
'aria-label': ariaLabel,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const styles = useStyles(getStyles);
|
||||
@@ -54,10 +68,10 @@ export const ToolbarButton = forwardRef<HTMLButtonElement, Props>(
|
||||
});
|
||||
|
||||
const body = (
|
||||
<button ref={ref} className={buttonStyles} {...rest}>
|
||||
<button ref={ref} className={buttonStyles} aria-label={getButttonAriaLabel(ariaLabel, tooltip)} {...rest}>
|
||||
{renderIcon(icon)}
|
||||
{imgSrc && <img className={styles.img} src={imgSrc} />}
|
||||
{children && !iconOnly && <span className={contentStyles}>{children}</span>}
|
||||
{children && !iconOnly && <div className={contentStyles}>{children}</div>}
|
||||
{isOpen === false && <Icon name="angle-down" />}
|
||||
{isOpen === true && <Icon name="angle-up" />}
|
||||
</button>
|
||||
@@ -73,6 +87,10 @@ export const ToolbarButton = forwardRef<HTMLButtonElement, Props>(
|
||||
}
|
||||
);
|
||||
|
||||
function getButttonAriaLabel(ariaLabel: string | undefined, tooltip: string | undefined) {
|
||||
return ariaLabel ? ariaLabel : tooltip ? selectors.components.PageToolbar.item(tooltip) : undefined;
|
||||
}
|
||||
|
||||
function renderIcon(icon: IconName | React.ReactNode) {
|
||||
if (!icon) {
|
||||
return null;
|
||||
@@ -100,16 +118,13 @@ const getStyles = (theme: GrafanaTheme) => {
|
||||
line-height: ${theme.height.md - 2}px;
|
||||
font-weight: ${theme.typography.weight.semibold};
|
||||
border: 1px solid ${theme.colors.border2};
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&[disabled],
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
color: ${theme.colors.textWeak};
|
||||
background: ${theme.colors.bg1};
|
||||
@@ -119,7 +134,6 @@ const getStyles = (theme: GrafanaTheme) => {
|
||||
default: css`
|
||||
color: ${theme.colors.textWeak};
|
||||
background-color: ${theme.colors.bg1};
|
||||
|
||||
&:hover {
|
||||
color: ${theme.colors.text};
|
||||
background: ${styleMixins.hoverColor(theme.colors.bg1, theme)};
|
||||
@@ -129,7 +143,6 @@ const getStyles = (theme: GrafanaTheme) => {
|
||||
color: ${theme.palette.orangeDark};
|
||||
border-color: ${theme.palette.orangeDark};
|
||||
background-color: transparent;
|
||||
|
||||
&:hover {
|
||||
color: ${theme.colors.text};
|
||||
background: ${styleMixins.hoverColor(theme.colors.bg1, theme)};
|
||||
@@ -156,14 +169,14 @@ const getStyles = (theme: GrafanaTheme) => {
|
||||
`,
|
||||
content: css`
|
||||
flex-grow: 1;
|
||||
display: none;
|
||||
|
||||
@media only screen and (min-width: ${theme.breakpoints.md}) {
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
contentWithIcon: css`
|
||||
display: none;
|
||||
padding-left: ${theme.spacing.sm};
|
||||
|
||||
@media ${styleMixins.mediaUp(theme.breakpoints.md)} {
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
contentWithRightIcon: css`
|
||||
padding-right: ${theme.spacing.xs};
|
||||
|
@@ -11,7 +11,6 @@ export interface Props<T> extends HTMLAttributes<HTMLButtonElement> {
|
||||
className?: string;
|
||||
options: Array<SelectableValue<T>>;
|
||||
value?: SelectableValue<T>;
|
||||
maxMenuHeight?: number;
|
||||
onChange: (item: SelectableValue<T>) => void;
|
||||
tooltipContent?: PopoverContent;
|
||||
narrow?: boolean;
|
||||
|
@@ -10,7 +10,7 @@ import * as MonoIcon from './assets';
|
||||
import { customIcons } from './custom';
|
||||
import { SvgProps } from './assets/types';
|
||||
|
||||
const alwaysMonoIcons = ['grafana', 'favorite', 'heart-break', 'heart'];
|
||||
const alwaysMonoIcons = ['grafana', 'favorite', 'heart-break', 'heart', 'panel-add'];
|
||||
|
||||
export interface IconProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
name: IconName;
|
||||
|
@@ -1,15 +1,15 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { SvgProps } from './types';
|
||||
|
||||
export const PanelAdd: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
|
||||
export const PanelAdd: FunctionComponent<SvgProps> = ({ ...rest }) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
enableBackground="new 0 0 117.8 64"
|
||||
viewBox="0 0 117.8 64"
|
||||
xmlSpace="preserve"
|
||||
width={size}
|
||||
height={size}
|
||||
width={24}
|
||||
height={24}
|
||||
{...rest}
|
||||
>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="96.4427" y1="83.7013" x2="96.4427" y2="-9.4831">
|
||||
|
@@ -179,6 +179,8 @@ const getMenuStyles = (theme: GrafanaTheme) => {
|
||||
color: ${linkColor};
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
padding: 5px 12px 5px 10px;
|
||||
|
||||
&:hover {
|
||||
color: ${linkColorHover};
|
||||
text-decoration: none;
|
||||
@@ -186,7 +188,6 @@ const getMenuStyles = (theme: GrafanaTheme) => {
|
||||
`,
|
||||
item: css`
|
||||
background: none;
|
||||
padding: 5px 12px 5px 10px;
|
||||
border-left: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { ToolbarButton, VerticalGroup } from '@grafana/ui';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { PageToolbar } from './PageToolbar';
|
||||
import { StoryExample } from '../../utils/storybook/StoryExample';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { IconButton } from '../IconButton/IconButton';
|
||||
|
||||
export default {
|
||||
title: 'Layout/PageToolbar',
|
||||
component: PageToolbar,
|
||||
decorators: [withCenteredStory],
|
||||
parameters: {},
|
||||
};
|
||||
|
||||
export const Examples = () => {
|
||||
return (
|
||||
<VerticalGroup>
|
||||
<StoryExample name="With non clickable title">
|
||||
<PageToolbar pageIcon="bell" title="Dashboard">
|
||||
<ToolbarButton icon="panel-add" />
|
||||
<ToolbarButton icon="sync">Sync</ToolbarButton>
|
||||
</PageToolbar>
|
||||
</StoryExample>
|
||||
<StoryExample name="With clickable title and parent">
|
||||
<PageToolbar
|
||||
pageIcon="apps"
|
||||
title="A very long dashboard name"
|
||||
parent="A long folder name"
|
||||
onClickTitle={() => action('Title clicked')}
|
||||
onClickParent={() => action('Parent clicked')}
|
||||
leftItems={[
|
||||
<IconButton name="share-alt" size="lg" key="share" />,
|
||||
<IconButton name="favorite" iconType="mono" size="lg" key="favorite" />,
|
||||
]}
|
||||
>
|
||||
<ToolbarButton icon="panel-add" />
|
||||
<ToolbarButton icon="share-alt" />
|
||||
<ToolbarButton icon="sync">Sync</ToolbarButton>
|
||||
<ToolbarButton icon="cog">Settings </ToolbarButton>
|
||||
</PageToolbar>
|
||||
</StoryExample>
|
||||
<StoryExample name="Go back version">
|
||||
<PageToolbar title="Service overview / Edit panel" onGoBack={() => action('Go back')}>
|
||||
<ToolbarButton icon="cog" />
|
||||
<ToolbarButton icon="save" />
|
||||
<ToolbarButton>Discard</ToolbarButton>
|
||||
<ToolbarButton>Apply</ToolbarButton>
|
||||
</PageToolbar>
|
||||
</StoryExample>
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
202
packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx
Normal file
202
packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import React, { FC, ReactNode } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { useStyles } from '../../themes/ThemeContext';
|
||||
import { IconName } from '../../types';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { styleMixins } from '../../themes';
|
||||
import { IconButton } from '../IconButton/IconButton';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
export interface Props {
|
||||
pageIcon?: IconName;
|
||||
title: string;
|
||||
parent?: string;
|
||||
onGoBack?: () => void;
|
||||
onClickTitle?: () => void;
|
||||
onClickParent?: () => void;
|
||||
leftItems?: ReactNode[];
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
isFullscreen?: boolean;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export const PageToolbar: FC<Props> = React.memo(
|
||||
({
|
||||
title,
|
||||
parent,
|
||||
pageIcon,
|
||||
onGoBack,
|
||||
children,
|
||||
onClickTitle,
|
||||
onClickParent,
|
||||
leftItems,
|
||||
isFullscreen,
|
||||
className,
|
||||
}) => {
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
/**
|
||||
* .page-toolbar css class is used for some legacy css view modes (TV/Kiosk) and
|
||||
* media queries for mobile view when toolbar needs left padding to make room
|
||||
* for mobile menu icon. This logic hopefylly can be changed when we move to a full react
|
||||
* app and change how the app side menu & mobile menu is rendered.
|
||||
*/
|
||||
const mainStyle = cx(
|
||||
'page-toolbar',
|
||||
styles.toolbar,
|
||||
{
|
||||
['page-toolbar--fullscreen']: isFullscreen,
|
||||
},
|
||||
className
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={mainStyle}>
|
||||
<div className={styles.toolbarLeft}>
|
||||
{pageIcon && !onGoBack && (
|
||||
<div className={styles.pageIcon}>
|
||||
<Icon name={pageIcon} size="lg" />
|
||||
</div>
|
||||
)}
|
||||
{onGoBack && (
|
||||
<div className={styles.goBackButton}>
|
||||
<IconButton
|
||||
name="arrow-left"
|
||||
tooltip="Go back (Esc)"
|
||||
tooltipPlacement="bottom"
|
||||
size="xxl"
|
||||
surface="dashboard"
|
||||
aria-label={selectors.components.BackButton.backArrow}
|
||||
onClick={onGoBack}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.titleWrapper}>
|
||||
{parent && onClickParent && (
|
||||
<button onClick={onClickParent} className={cx(styles.titleLink, styles.parentLink)}>
|
||||
{parent} <span className={styles.parentIcon}>/</span>
|
||||
</button>
|
||||
)}
|
||||
{onClickTitle && (
|
||||
<button onClick={onClickTitle} className={styles.titleLink}>
|
||||
{title}
|
||||
</button>
|
||||
)}
|
||||
{!onClickTitle && <div className={styles.titleText}>{title}</div>}
|
||||
</div>
|
||||
{leftItems?.map((child, index) => (
|
||||
<div className={styles.leftActionItem} key={index}>
|
||||
{child}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.spacer}></div>
|
||||
{React.Children.toArray(children)
|
||||
.filter(Boolean)
|
||||
.map((child, index) => {
|
||||
return (
|
||||
<div className={styles.actionWrapper} key={index}>
|
||||
{child}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
PageToolbar.displayName = 'PageToolbar';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme) => {
|
||||
const { spacing, typography } = theme;
|
||||
|
||||
const titleStyles = `
|
||||
font-size: ${typography.size.lg};
|
||||
padding-left: ${spacing.sm};
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 240px;
|
||||
|
||||
// clear default button styles
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
@media ${styleMixins.mediaUp(theme.breakpoints.xl)} {
|
||||
max-width: unset;
|
||||
}
|
||||
`;
|
||||
|
||||
return {
|
||||
toolbar: css`
|
||||
display: flex;
|
||||
background: ${theme.colors.dashboardBg};
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 ${spacing.md} ${spacing.sm} ${spacing.md};
|
||||
`,
|
||||
toolbarLeft: css`
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
`,
|
||||
spacer: css`
|
||||
flex-grow: 1;
|
||||
`,
|
||||
pageIcon: css`
|
||||
padding-top: ${spacing.sm};
|
||||
align-items: center;
|
||||
display: none;
|
||||
|
||||
@media ${styleMixins.mediaUp(theme.breakpoints.md)} {
|
||||
display: flex;
|
||||
}
|
||||
`,
|
||||
titleWrapper: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: ${spacing.sm};
|
||||
padding-right: ${spacing.sm};
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
`,
|
||||
goBackButton: css`
|
||||
position: relative;
|
||||
top: 8px;
|
||||
`,
|
||||
parentIcon: css`
|
||||
margin-left: 4px;
|
||||
`,
|
||||
titleText: css`
|
||||
${titleStyles};
|
||||
`,
|
||||
titleLink: css`
|
||||
${titleStyles};
|
||||
`,
|
||||
parentLink: css`
|
||||
display: none;
|
||||
|
||||
@media ${styleMixins.mediaUp(theme.breakpoints.md)} {
|
||||
display: inline-block;
|
||||
}
|
||||
`,
|
||||
actionWrapper: css`
|
||||
padding-left: ${spacing.sm};
|
||||
padding-top: ${spacing.sm};
|
||||
`,
|
||||
leftActionItem: css`
|
||||
display: none;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
align-items: center;
|
||||
padding-left: ${spacing.xs};
|
||||
|
||||
@media ${styleMixins.mediaUp(theme.breakpoints.md)} {
|
||||
display: flex;
|
||||
}
|
||||
`,
|
||||
};
|
||||
};
|
@@ -83,7 +83,6 @@ export class RefreshPicker extends PureComponent<Props> {
|
||||
value={selectedValue}
|
||||
options={options}
|
||||
onChange={this.onChangeSelect as any}
|
||||
maxMenuHeight={380}
|
||||
variant={variant}
|
||||
/>
|
||||
)}
|
||||
|
@@ -186,7 +186,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
const getLabelStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
container: css`
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
`,
|
||||
utc: css`
|
||||
color: ${theme.palette.orange};
|
||||
|
@@ -45,6 +45,7 @@ export { ModalHeader } from './Modal/ModalHeader';
|
||||
export { ModalTabsHeader } from './Modal/ModalTabsHeader';
|
||||
export { ModalTabContent } from './Modal/ModalTabContent';
|
||||
export { ModalsProvider, ModalRoot, ModalsController } from './Modal/ModalsContext';
|
||||
export { PageToolbar } from './PageLayout/PageToolbar';
|
||||
|
||||
// Renderless
|
||||
export { SetInterval } from './SetInterval/SetInterval';
|
||||
|
@@ -34,6 +34,10 @@ export function listItemSelected(theme: GrafanaTheme): string {
|
||||
`;
|
||||
}
|
||||
|
||||
export function mediaUp(breakpoint: string) {
|
||||
return `only screen and (min-width: ${breakpoint})`;
|
||||
}
|
||||
|
||||
export const focusCss = (theme: GrafanaTheme) => `
|
||||
outline: 2px dotted transparent;
|
||||
outline-offset: 2px;
|
||||
|
@@ -1,16 +1,14 @@
|
||||
// Libaries
|
||||
import React, { PureComponent, FC, ReactNode } from 'react';
|
||||
import { connect, MapDispatchToProps } from 'react-redux';
|
||||
import { css } from 'emotion';
|
||||
// Utils & Services
|
||||
import { appEvents } from 'app/core/app_events';
|
||||
import { PlaylistSrv } from 'app/features/playlist/playlist_srv';
|
||||
// Components
|
||||
import { DashNavButton } from './DashNavButton';
|
||||
import { DashNavTimeControls } from './DashNavTimeControls';
|
||||
import { Icon, ModalsController } from '@grafana/ui';
|
||||
import { ButtonGroup, ModalsController, ToolbarButton, PageToolbar } from '@grafana/ui';
|
||||
import { textUtil } from '@grafana/data';
|
||||
import { BackButton } from 'app/core/components/BackButton/BackButton';
|
||||
// State
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
|
||||
@@ -126,11 +124,23 @@ class DashNav extends PureComponent<Props> {
|
||||
});
|
||||
}
|
||||
|
||||
isInKioskMode() {
|
||||
return !!this.props.location.query.kiosk;
|
||||
}
|
||||
|
||||
isPlaylistRunning() {
|
||||
return this.playlistSrv.isPlaying;
|
||||
}
|
||||
|
||||
renderLeftActionsButton() {
|
||||
const { dashboard } = this.props;
|
||||
const { canStar, canShare, isStarred } = dashboard.meta;
|
||||
|
||||
const buttons: ReactNode[] = [];
|
||||
|
||||
if (this.isInKioskMode() || this.isPlaylistRunning()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (canStar) {
|
||||
buttons.push(
|
||||
<DashNavButton
|
||||
@@ -172,74 +182,49 @@ class DashNav extends PureComponent<Props> {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
renderDashboardTitleSearchButton() {
|
||||
const { dashboard, isFullscreen } = this.props;
|
||||
|
||||
const folderSymbol = css`
|
||||
margin-right: 0 4px;
|
||||
`;
|
||||
const mainIconClassName = css`
|
||||
margin-right: 8px;
|
||||
margin-bottom: 3px;
|
||||
`;
|
||||
|
||||
const folderTitle = dashboard.meta.folderTitle;
|
||||
const haveFolder = (dashboard.meta.folderId ?? 0) > 0;
|
||||
|
||||
renderPlaylistControls() {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="navbar-page-btn">
|
||||
{!isFullscreen && <Icon name="apps" size="lg" className={mainIconClassName} />}
|
||||
{haveFolder && (
|
||||
<>
|
||||
<a className="navbar-page-btn__folder" onClick={this.onFolderNameClick}>
|
||||
{folderTitle} <span className={folderSymbol}>/</span>
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
<a onClick={this.onDashboardNameClick}>{dashboard.title}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="navbar-buttons navbar-buttons--actions">{this.renderLeftActionsButton()}</div>
|
||||
<div className="navbar__spacer" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderBackButton() {
|
||||
return (
|
||||
<div className="navbar-edit">
|
||||
<BackButton surface="dashboard" onClick={this.onClose} />
|
||||
</div>
|
||||
<ButtonGroup key="playlist-buttons">
|
||||
<ToolbarButton tooltip="Go to previous dashboard" icon="backward" onClick={this.onPlaylistPrev} narrow />
|
||||
<ToolbarButton onClick={this.onPlaylistStop}>Stop playlist</ToolbarButton>
|
||||
<ToolbarButton tooltip="Go to next dashboard" icon="forward" onClick={this.onPlaylistNext} narrow />
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
renderRightActionsButton() {
|
||||
const { dashboard, onAddPanel } = this.props;
|
||||
const { dashboard, onAddPanel, location, updateTimeZoneForSession, isFullscreen } = this.props;
|
||||
const { canEdit, showSettings } = dashboard.meta;
|
||||
const { snapshot } = dashboard;
|
||||
const snapshotUrl = snapshot && snapshot.originalUrl;
|
||||
|
||||
const buttons: ReactNode[] = [];
|
||||
if (canEdit) {
|
||||
buttons.push(
|
||||
<DashNavButton
|
||||
classSuffix="save"
|
||||
tooltip="Add panel"
|
||||
icon="panel-add"
|
||||
onClick={onAddPanel}
|
||||
iconType="mono"
|
||||
iconSize="xl"
|
||||
key="button-panel-add"
|
||||
/>
|
||||
);
|
||||
const tvButton = (
|
||||
<ToolbarButton tooltip="Cycle view mode" icon="monitor" onClick={this.onToggleTVMode} key="tv-button" />
|
||||
);
|
||||
const timeControls = (
|
||||
<DashNavTimeControls
|
||||
dashboard={dashboard}
|
||||
location={location}
|
||||
onChangeTimeZone={updateTimeZoneForSession}
|
||||
key="time-controls"
|
||||
/>
|
||||
);
|
||||
|
||||
if (this.isPlaylistRunning()) {
|
||||
return [this.renderPlaylistControls(), timeControls];
|
||||
}
|
||||
|
||||
if (this.isInKioskMode()) {
|
||||
return [timeControls, tvButton];
|
||||
}
|
||||
|
||||
if (canEdit && !isFullscreen) {
|
||||
buttons.push(<ToolbarButton tooltip="Add panel" icon="panel-add" onClick={onAddPanel} key="button-panel-add" />);
|
||||
buttons.push(
|
||||
<ModalsController key="button-save">
|
||||
{({ showModal, hideModal }) => (
|
||||
<DashNavButton
|
||||
<ToolbarButton
|
||||
tooltip="Save dashboard"
|
||||
classSuffix="save"
|
||||
icon="save"
|
||||
onClick={() => {
|
||||
showModal(SaveDashboardModalProxy, {
|
||||
@@ -255,10 +240,9 @@ class DashNav extends PureComponent<Props> {
|
||||
|
||||
if (snapshotUrl) {
|
||||
buttons.push(
|
||||
<DashNavButton
|
||||
<ToolbarButton
|
||||
tooltip="Open original dashboard"
|
||||
classSuffix="snapshot-origin"
|
||||
href={textUtil.sanitizeUrl(snapshotUrl)}
|
||||
onClick={() => this.gotoSnapshotOrigin(snapshotUrl)}
|
||||
icon="link"
|
||||
key="button-snapshot"
|
||||
/>
|
||||
@@ -267,67 +251,40 @@ class DashNav extends PureComponent<Props> {
|
||||
|
||||
if (showSettings) {
|
||||
buttons.push(
|
||||
<DashNavButton
|
||||
tooltip="Dashboard settings"
|
||||
classSuffix="settings"
|
||||
icon="cog"
|
||||
onClick={this.onOpenSettings}
|
||||
key="button-settings"
|
||||
/>
|
||||
<ToolbarButton tooltip="Dashboard settings" icon="cog" onClick={this.onOpenSettings} key="button-settings" />
|
||||
);
|
||||
}
|
||||
|
||||
this.addCustomContent(customRightActions, buttons);
|
||||
|
||||
if (!dashboard.timepicker.hidden) {
|
||||
buttons.push(timeControls);
|
||||
}
|
||||
|
||||
buttons.push(tvButton);
|
||||
return buttons;
|
||||
}
|
||||
|
||||
gotoSnapshotOrigin(snapshotUrl: string) {
|
||||
window.location.href = textUtil.sanitizeUrl(snapshotUrl);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dashboard, location, isFullscreen, updateTimeZoneForSession } = this.props;
|
||||
const { dashboard, isFullscreen } = this.props;
|
||||
const onGoBack = isFullscreen ? this.onClose : undefined;
|
||||
|
||||
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} />
|
||||
</div>
|
||||
|
||||
{!dashboard.timepicker.hidden && (
|
||||
<div className="navbar-buttons">
|
||||
<DashNavTimeControls
|
||||
dashboard={dashboard}
|
||||
location={location}
|
||||
onChangeTimeZone={updateTimeZoneForSession}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<PageToolbar
|
||||
pageIcon={isFullscreen ? undefined : 'apps'}
|
||||
title={dashboard.title}
|
||||
parent={dashboard.meta.folderTitle}
|
||||
onClickTitle={this.onDashboardNameClick}
|
||||
onClickParent={this.onFolderNameClick}
|
||||
onGoBack={onGoBack}
|
||||
leftItems={this.renderLeftActionsButton()}
|
||||
>
|
||||
{this.renderRightActionsButton()}
|
||||
</PageToolbar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -18,13 +18,6 @@ interface Props {
|
||||
noBorder?: boolean;
|
||||
}
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
||||
noBorderContainer: css`
|
||||
padding: 0 ${theme.spacing.xs};
|
||||
display: flex;
|
||||
`,
|
||||
}));
|
||||
|
||||
export const DashNavButton: FunctionComponent<Props> = ({
|
||||
icon,
|
||||
iconType,
|
||||
@@ -62,7 +55,7 @@ export const DashNavButton: FunctionComponent<Props> = ({
|
||||
<button
|
||||
className={`btn navbar-button navbar-button--${classSuffix}`}
|
||||
onClick={onClick}
|
||||
aria-label={selectors.pages.Dashboard.Toolbar.toolbarItems(tooltip)}
|
||||
aria-label={selectors.components.PageToolbar.item(tooltip)}
|
||||
>
|
||||
{icon && <Icon name={icon} type={iconType} size={iconSize || 'lg'} />}
|
||||
{children}
|
||||
@@ -76,3 +69,10 @@ export const DashNavButton: FunctionComponent<Props> = ({
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
||||
noBorderContainer: css`
|
||||
padding: 0 ${theme.spacing.xs};
|
||||
display: flex;
|
||||
`,
|
||||
}));
|
||||
|
@@ -1,88 +1,9 @@
|
||||
import React from 'react';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { css } from 'emotion';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
import { useTheme, Tooltip, stylesFactory, selectThemeVariant, ButtonGroup, ToolbarButton } from '@grafana/ui';
|
||||
import { useTheme, Tooltip, stylesFactory, ButtonGroup, ToolbarButton } from '@grafana/ui';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
const bgColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.dark1 }, theme.type);
|
||||
const orangeLighter = tinycolor(theme.palette.orangeDark).lighten(10).toString();
|
||||
const pulseTextColor = tinycolor(theme.palette.orangeDark).desaturate(90).toString();
|
||||
|
||||
return {
|
||||
isLive: css`
|
||||
label: isLive;
|
||||
border-color: ${theme.palette.orangeDark};
|
||||
color: ${theme.palette.orangeDark};
|
||||
background: transparent;
|
||||
&:focus {
|
||||
background: transparent;
|
||||
border-color: ${theme.palette.orangeDark};
|
||||
color: ${theme.palette.orangeDark};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${bgColor};
|
||||
}
|
||||
&:active,
|
||||
&:hover {
|
||||
border-color: ${orangeLighter};
|
||||
color: ${orangeLighter};
|
||||
}
|
||||
`,
|
||||
isPaused: css`
|
||||
label: isPaused;
|
||||
border-color: ${theme.palette.orangeDark};
|
||||
background: transparent;
|
||||
animation: pulse 3s ease-out 0s infinite normal forwards;
|
||||
&:focus {
|
||||
background: transparent;
|
||||
border-color: ${theme.palette.orangeDark};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${bgColor};
|
||||
}
|
||||
&:active,
|
||||
&:hover {
|
||||
border-color: ${orangeLighter};
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
color: ${pulseTextColor};
|
||||
}
|
||||
50% {
|
||||
color: ${theme.palette.orangeDark};
|
||||
}
|
||||
100% {
|
||||
color: ${pulseTextColor};
|
||||
}
|
||||
}
|
||||
`,
|
||||
stopButtonEnter: css`
|
||||
label: stopButtonEnter;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
`,
|
||||
stopButtonEnterActive: css`
|
||||
label: stopButtonEnterActive;
|
||||
opacity: 1;
|
||||
width: 32px;
|
||||
`,
|
||||
stopButtonExit: css`
|
||||
label: stopButtonExit;
|
||||
width: 32px;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
`,
|
||||
stopButtonExitActive: css`
|
||||
label: stopButtonExitActive;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
type LiveTailButtonProps = {
|
||||
splitted: boolean;
|
||||
start: () => void;
|
||||
@@ -92,6 +13,7 @@ type LiveTailButtonProps = {
|
||||
isLive: boolean;
|
||||
isPaused: boolean;
|
||||
};
|
||||
|
||||
export function LiveTailButton(props: LiveTailButtonProps) {
|
||||
const { start, pause, resume, isLive, isPaused, stop, splitted } = props;
|
||||
const theme = useTheme();
|
||||
@@ -134,3 +56,30 @@ export function LiveTailButton(props: LiveTailButtonProps) {
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
stopButtonEnter: css`
|
||||
label: stopButtonEnter;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
`,
|
||||
stopButtonEnterActive: css`
|
||||
label: stopButtonEnterActive;
|
||||
opacity: 1;
|
||||
width: 32px;
|
||||
`,
|
||||
stopButtonExit: css`
|
||||
label: stopButtonExit;
|
||||
width: 32px;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
`,
|
||||
stopButtonExitActive: css`
|
||||
label: stopButtonExitActive;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
@@ -76,7 +76,6 @@ export const UnconnectedReturnToDashboardButton: FC<Props> = ({
|
||||
data-testid="returnButtonWithChanges"
|
||||
options={[{ label: 'Return to panel with changes', value: '' }]}
|
||||
onChange={() => returnToPanel({ withChanges: true })}
|
||||
maxMenuHeight={380}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
|
@@ -1,16 +1,3 @@
|
||||
.navbar-buttons--zoom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-page-btn {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.navbar-buttons--tv,
|
||||
.navbar-buttons--actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Media queries
|
||||
// ---------------------
|
||||
|
||||
@@ -21,34 +8,3 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.navbar-page-btn {
|
||||
max-width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.navbar-buttons--tv,
|
||||
.navbar-buttons--actions {
|
||||
display: flex;
|
||||
}
|
||||
.navbar-page-btn {
|
||||
max-width: 325px;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.navbar-buttons--zoom {
|
||||
display: flex;
|
||||
}
|
||||
.navbar-page-btn {
|
||||
max-width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl) {
|
||||
.navbar-page-btn {
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
|
@@ -1,47 +1,3 @@
|
||||
.navbar {
|
||||
position: relative;
|
||||
z-index: $zindex-navbar-fixed;
|
||||
height: $navbarHeight;
|
||||
padding: 0 16px 0 60px;
|
||||
display: flex;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
transition-duration: 350ms;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-property: box-shadow, border-bottom;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
padding-left: $dashboard-padding;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&--edit {
|
||||
background: $panel-bg;
|
||||
border-bottom: $panel-border;
|
||||
box-shadow: 0 0 10px $dashboard-bg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin navbar-alt-look() {
|
||||
background: $page-header-bg;
|
||||
box-shadow: $search-shadow;
|
||||
border-bottom: $navbarBorder;
|
||||
}
|
||||
|
||||
.panel-in-fullscreen,
|
||||
.panel-in-fullscreen.view-mode--tv {
|
||||
.navbar {
|
||||
padding-left: $navbar-padding;
|
||||
}
|
||||
|
||||
.navbar-button--add-panel,
|
||||
.navbar-button--star,
|
||||
.navbar-button--tv {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-page-btn {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
@@ -75,35 +31,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-page-btn__folder {
|
||||
display: none;
|
||||
padding-right: 4px;
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-left: 10px;
|
||||
|
||||
&--close {
|
||||
display: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&--zoom {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar__spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.navbar-button {
|
||||
background-color: $panel-bg;
|
||||
|
||||
|
@@ -22,10 +22,6 @@
|
||||
}
|
||||
|
||||
.panel-in-fullscreen {
|
||||
.sidemenu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
left: 0 !important;
|
||||
}
|
||||
|
@@ -190,10 +190,10 @@ li.sidemenu-org-switcher {
|
||||
}
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
width: 26px;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
left: 4px;
|
||||
left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,11 +234,12 @@ li.sidemenu-org-switcher {
|
||||
}
|
||||
|
||||
.sidemenu__logo_small_breakpoint {
|
||||
padding: 14px 10px 26px 13px;
|
||||
padding: 13px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
cursor: pointer;
|
||||
|
||||
.fa-bars {
|
||||
font-size: 25px;
|
||||
|
@@ -2,41 +2,10 @@
|
||||
.react-resizable-handle,
|
||||
.add-row-panel-hint,
|
||||
.dash-row-menu-container,
|
||||
.navbar-buttons--actions,
|
||||
.panel-info-corner--info,
|
||||
.panel-info-corner--links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-page-btn {
|
||||
i {
|
||||
display: none;
|
||||
}
|
||||
|
||||
i.navbar-page-btn__folder-icon {
|
||||
display: inline-block;
|
||||
opacity: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-button--zoom {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.view-mode--playlist {
|
||||
@extend .view-mode--inactive;
|
||||
}
|
||||
|
||||
// https://github.com/grafana/grafana/issues/18114
|
||||
.view-mode--tv.panel-in-fullscreen {
|
||||
.navbar {
|
||||
padding-left: $navbar-padding;
|
||||
}
|
||||
|
||||
.navbar-page-btn {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.view-mode--tv {
|
||||
@@ -60,8 +29,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
.page-toolbar {
|
||||
padding-left: $side-menu-width;
|
||||
|
||||
&--fullscreen {
|
||||
padding-left: $space-md;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-controls {
|
||||
@@ -73,15 +46,21 @@
|
||||
@extend .view-mode--tv;
|
||||
|
||||
.sidemenu,
|
||||
.navbar {
|
||||
.page-toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-canvas--dashboard {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.submenu-controls {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
div.page-toolbar {
|
||||
padding-left: 53px;
|
||||
|
||||
&--fullscreen {
|
||||
padding-left: $space-md;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user