mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Collapse: add a controlled collapse (#22046)
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
import React, { FunctionComponent, useContext } from 'react';
|
import React, { FunctionComponent, useContext, useState } from 'react';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
|
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
|
||||||
import { ThemeContext } from '../../themes/ThemeContext';
|
import { ThemeContext } from '../../themes/ThemeContext';
|
||||||
import { stylesFactory } from '../../themes/stylesFactory';
|
import { stylesFactory } from '../../themes/stylesFactory';
|
||||||
|
import { Icon } from '../Icon/Icon';
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
||||||
collapse: css`
|
collapse: css`
|
||||||
label: collapse;
|
label: collapse;
|
||||||
margin-top: ${theme.spacing.sm};
|
margin-bottom: ${theme.spacing.sm};
|
||||||
`,
|
`,
|
||||||
collapseBody: css`
|
collapseBody: css`
|
||||||
label: collapse__body;
|
label: collapse__body;
|
||||||
@@ -51,7 +51,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
|||||||
`,
|
`,
|
||||||
header: css`
|
header: css`
|
||||||
label: collapse__header;
|
label: collapse__header;
|
||||||
padding: ${theme.spacing.sm} ${theme.spacing.md} 0 ${theme.spacing.md};
|
padding: ${theme.spacing.sm} ${theme.spacing.md};
|
||||||
display: flex;
|
display: flex;
|
||||||
cursor: inherit;
|
cursor: inherit;
|
||||||
transition: all 0.1s linear;
|
transition: all 0.1s linear;
|
||||||
@@ -60,7 +60,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
|||||||
headerCollapsed: css`
|
headerCollapsed: css`
|
||||||
label: collapse__header--collapsed;
|
label: collapse__header--collapsed;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: ${theme.spacing.sm} ${theme.spacing.md} 0 ${theme.spacing.md};
|
padding: ${theme.spacing.sm} ${theme.spacing.md};
|
||||||
`,
|
`,
|
||||||
headerButtons: css`
|
headerButtons: css`
|
||||||
label: collapse__header-buttons;
|
label: collapse__header-buttons;
|
||||||
@@ -78,7 +78,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
|||||||
font-weight: ${theme.typography.weight.semibold};
|
font-weight: ${theme.typography.weight.semibold};
|
||||||
margin-right: ${theme.spacing.sm};
|
margin-right: ${theme.spacing.sm};
|
||||||
font-size: ${theme.typography.heading.h6};
|
font-size: ${theme.typography.heading.h6};
|
||||||
box-shadow: ${selectThemeVariant({ light: 'none', dark: '1px 1px 4px rgb(45, 45, 45)' }, theme.type)};
|
|
||||||
`,
|
`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -90,6 +89,22 @@ interface Props {
|
|||||||
onToggle?: (isOpen: boolean) => void;
|
onToggle?: (isOpen: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ControlledCollapse: FunctionComponent<Props> = ({ isOpen, onToggle, ...otherProps }) => {
|
||||||
|
const [open, setOpen] = useState(isOpen);
|
||||||
|
return (
|
||||||
|
<Collapse
|
||||||
|
isOpen={open}
|
||||||
|
{...otherProps}
|
||||||
|
onToggle={() => {
|
||||||
|
setOpen(!open);
|
||||||
|
if (onToggle) {
|
||||||
|
onToggle(!open);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const Collapse: FunctionComponent<Props> = ({ isOpen, label, loading, collapsible, onToggle, children }) => {
|
export const Collapse: FunctionComponent<Props> = ({ isOpen, label, loading, collapsible, onToggle, children }) => {
|
||||||
const theme = useContext(ThemeContext);
|
const theme = useContext(ThemeContext);
|
||||||
const style = getStyles(theme);
|
const style = getStyles(theme);
|
||||||
@@ -100,7 +115,6 @@ export const Collapse: FunctionComponent<Props> = ({ isOpen, label, loading, col
|
|||||||
};
|
};
|
||||||
|
|
||||||
const panelClass = cx([style.collapse, 'panel-container']);
|
const panelClass = cx([style.collapse, 'panel-container']);
|
||||||
const iconClass = isOpen ? 'fa fa-caret-up' : 'fa fa-caret-down';
|
|
||||||
const loaderClass = loading ? cx([style.loader, style.loaderActive]) : cx([style.loader]);
|
const loaderClass = loading ? cx([style.loader, style.loaderActive]) : cx([style.loader]);
|
||||||
const headerClass = collapsible ? cx([style.header]) : cx([style.headerCollapsed]);
|
const headerClass = collapsible ? cx([style.header]) : cx([style.headerCollapsed]);
|
||||||
const headerButtonsClass = collapsible ? cx([style.headerButtons]) : cx([style.headerButtonsCollapsed]);
|
const headerButtonsClass = collapsible ? cx([style.headerButtons]) : cx([style.headerButtonsCollapsed]);
|
||||||
@@ -109,7 +123,7 @@ export const Collapse: FunctionComponent<Props> = ({ isOpen, label, loading, col
|
|||||||
<div className={panelClass}>
|
<div className={panelClass}>
|
||||||
<div className={headerClass} onClick={onClickToggle}>
|
<div className={headerClass} onClick={onClickToggle}>
|
||||||
<div className={headerButtonsClass}>
|
<div className={headerButtonsClass}>
|
||||||
<span className={iconClass} />
|
<Icon name={isOpen ? 'caret-up' : 'caret-down'} />
|
||||||
</div>
|
</div>
|
||||||
<div className={cx([style.headerLabel])}>{label}</div>
|
<div className={cx([style.headerLabel])}>{label}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
|
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
|
||||||
import Forms from '../Forms';
|
import Forms from '../Forms';
|
||||||
import { fieldMatchersUI } from '../MatchersUI/fieldMatchersUI';
|
import { fieldMatchersUI } from '../MatchersUI/fieldMatchersUI';
|
||||||
|
import { ControlledCollapse } from '../Collapse/Collapse';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
config: FieldConfigSource;
|
config: FieldConfigSource;
|
||||||
@@ -214,10 +215,17 @@ export class FieldConfigEditor extends React.PureComponent<Props> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.renderStandardConfigs()}
|
<ControlledCollapse label="Standard Field Configuration" collapsible>
|
||||||
{this.renderCustomConfigs()}
|
{this.renderStandardConfigs()}
|
||||||
{this.renderAddOverride()}
|
</ControlledCollapse>
|
||||||
{this.renderOverrides()}
|
{this.props.custom && (
|
||||||
|
<ControlledCollapse label="Standard Field Configuration">{this.renderCustomConfigs()}</ControlledCollapse>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ControlledCollapse label="Field Overrides" collapsible>
|
||||||
|
{this.renderAddOverride()}
|
||||||
|
{this.renderOverrides()}
|
||||||
|
</ControlledCollapse>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export {
|
|||||||
|
|
||||||
export { Alert, AlertVariant } from './Alert/Alert';
|
export { Alert, AlertVariant } from './Alert/Alert';
|
||||||
export { GraphSeriesToggler, GraphSeriesTogglerAPI } from './Graph/GraphSeriesToggler';
|
export { GraphSeriesToggler, GraphSeriesTogglerAPI } from './Graph/GraphSeriesToggler';
|
||||||
export { Collapse } from './Collapse/Collapse';
|
export { Collapse, ControlledCollapse } from './Collapse/Collapse';
|
||||||
export { LogLabels } from './Logs/LogLabels';
|
export { LogLabels } from './Logs/LogLabels';
|
||||||
export { LogRows } from './Logs/LogRows';
|
export { LogRows } from './Logs/LogRows';
|
||||||
export { getLogRowStyles } from './Logs/getLogRowStyles';
|
export { getLogRowStyles } from './Logs/getLogRowStyles';
|
||||||
|
|||||||
@@ -8,7 +8,14 @@ import {
|
|||||||
PanelEvents,
|
PanelEvents,
|
||||||
SelectableValue,
|
SelectableValue,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { stylesFactory, Forms, FieldConfigEditor, CustomScrollbar, selectThemeVariant } from '@grafana/ui';
|
import {
|
||||||
|
stylesFactory,
|
||||||
|
Forms,
|
||||||
|
FieldConfigEditor,
|
||||||
|
CustomScrollbar,
|
||||||
|
selectThemeVariant,
|
||||||
|
ControlledCollapse,
|
||||||
|
} from '@grafana/ui';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
@@ -211,14 +218,12 @@ export class PanelEditor extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<FieldConfigEditor
|
||||||
<FieldConfigEditor
|
config={fieldOptions}
|
||||||
config={fieldOptions}
|
custom={plugin.customFieldConfigs}
|
||||||
custom={plugin.customFieldConfigs}
|
onChange={this.onFieldConfigsChange}
|
||||||
onChange={this.onFieldConfigsChange}
|
data={data.series}
|
||||||
data={data.series}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +244,7 @@ export class PanelEditor extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
if (plugin.editor && panel) {
|
if (plugin.editor && panel) {
|
||||||
return (
|
return (
|
||||||
<div style={{ marginTop: '40px' }}>
|
<div style={{ marginTop: '10px' }}>
|
||||||
<plugin.editor data={data} options={panel.getOptions()} onOptionsChange={this.onPanelOptionsChanged} />
|
<plugin.editor data={data} options={panel.getOptions()} onOptionsChange={this.onPanelOptionsChanged} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -359,7 +364,9 @@ export class PanelEditor extends PureComponent<Props, State> {
|
|||||||
<CustomScrollbar>
|
<CustomScrollbar>
|
||||||
<div style={{ padding: '10px' }}>
|
<div style={{ padding: '10px' }}>
|
||||||
{this.renderFieldOptions()}
|
{this.renderFieldOptions()}
|
||||||
{this.renderVisSettings()}
|
<ControlledCollapse label="Visualization Settings" collapsible>
|
||||||
|
{this.renderVisSettings()}
|
||||||
|
</ControlledCollapse>
|
||||||
</div>
|
</div>
|
||||||
</CustomScrollbar>
|
</CustomScrollbar>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { EditorTab, allTabs } from './types';
|
|||||||
import { DashboardModel } from '../../state';
|
import { DashboardModel } from '../../state';
|
||||||
import { QueriesTab } from '../../panel_editor/QueriesTab';
|
import { QueriesTab } from '../../panel_editor/QueriesTab';
|
||||||
import { PanelModel } from '../../state/PanelModel';
|
import { PanelModel } from '../../state/PanelModel';
|
||||||
|
import { AlertTab } from 'app/features/alerting/AlertTab';
|
||||||
|
|
||||||
interface PanelEditorTabsProps {
|
interface PanelEditorTabsProps {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
@@ -57,7 +58,7 @@ export const PanelEditorTabs: React.FC<PanelEditorTabsProps> = ({ panel, dashboa
|
|||||||
return (
|
return (
|
||||||
<div style={{ width, height }}>
|
<div style={{ width, height }}>
|
||||||
{activeTab === EditorTab.Query && <QueriesTab panel={panel} dashboard={dashboard} />}
|
{activeTab === EditorTab.Query && <QueriesTab panel={panel} dashboard={dashboard} />}
|
||||||
{activeTab === EditorTab.Alerts && <div>TODO: Show Alerts</div>}
|
{activeTab === EditorTab.Alerts && <AlertTab panel={panel} dashboard={dashboard} />}
|
||||||
{activeTab === EditorTab.Transform && <div>TODO: Show Transform</div>}
|
{activeTab === EditorTab.Transform && <div>TODO: Show Transform</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user