Commit 6b3041d3 by Peter Holmberg Committed by GitHub

Panel: Use Tabs in panel inspector (#21468)

* replace select with tabs

* storybook example

* Update snapshot, move styles to component files
parent 962d0f6a
import React, { FC } from 'react';
import { cx } from 'emotion';
import { useTheme } from '../../themes';
import { getTabsStyle } from './styles';
import { css, cx } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { selectThemeVariant, stylesFactory, useTheme } from '../../themes';
export interface TabProps {
label: string;
......@@ -10,9 +10,61 @@ export interface TabProps {
onChangeTab: () => void;
}
const getTabStyles = stylesFactory((theme: GrafanaTheme) => {
const colors = theme.colors;
const tabBorderColor = selectThemeVariant({ dark: colors.dark9, light: colors.gray5 }, theme.type);
return {
tabItem: css`
list-style: none;
padding: 10px 15px 9px;
margin-right: ${theme.spacing.md};
position: relative;
display: block;
border: solid transparent;
border-width: 0 1px 1px;
border-radius: ${theme.border.radius.md} ${theme.border.radius.md} 0 0;
color: ${colors.text};
cursor: pointer;
i {
margin-right: ${theme.spacing.sm};
}
.gicon {
position: relative;
top: -2px;
}
&:hover,
&:focus {
color: ${colors.linkHover};
}
`,
activeStyle: css`
border-color: ${colors.orange} ${tabBorderColor} transparent;
background: ${colors.pageBg};
color: ${colors.link};
overflow: hidden;
cursor: not-allowed;
&::before {
display: block;
content: ' ';
position: absolute;
left: 0;
right: 0;
height: 2px;
top: 0;
background-image: linear-gradient(to right, #f05a28 30%, #fbca0a 99%);
}
`,
};
});
export const Tab: FC<TabProps> = ({ label, active, icon, onChangeTab }) => {
const theme = useTheme();
const tabsStyles = getTabsStyle(theme);
const tabsStyles = getTabStyles(theme);
return (
<li className={cx(tabsStyles.tabItem, active && tabsStyles.activeStyle)} onClick={onChangeTab}>
......
import React, { FC, ReactNode } from 'react';
import { stylesFactory, useTheme } from '../../themes';
import { css } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
interface Props {
children: ReactNode;
}
const getTabContentStyle = stylesFactory((theme: GrafanaTheme) => {
return {
tabContent: css`
padding: ${theme.spacing.xs} 0;
`,
};
});
export const TabContent: FC<Props> = ({ children }) => {
const theme = useTheme();
const styles = getTabContentStyle(theme);
return <div className={styles.tabContent}>{children}</div>;
};
import React from 'react';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { UseState } from '../../utils/storybook/UseState';
import { TabsBar } from './TabsBar';
import { Tab } from './Tab';
import { TabContent } from './TabContent';
export default {
title: 'UI/Tabs/TabsExample',
decorators: [withCenteredStory],
};
const tabs = [
{ label: '1st child', key: 'first', active: true },
{ label: '2nd child', key: 'second', active: false },
{ label: '3rd child', key: 'third', active: false },
];
export const Simple = () => {
return (
<UseState initialState={tabs}>
{(state, updateState) => {
return (
<div>
<TabsBar>
{state.map((tab, index) => {
return (
<Tab
key={index}
label={tab.label}
active={tab.active}
onChangeTab={() => updateState(state.map((tab, idx) => ({ ...tab, active: idx === index })))}
/>
);
})}
</TabsBar>
<TabContent>
{state[0].active && <div>First tab content</div>}
{state[1].active && <div>Second tab content</div>}
{state[2].active && <div>Third tab content</div>}
</TabContent>
</div>
);
}}
</UseState>
);
};
......@@ -18,9 +18,9 @@ export default {
};
const tabs = [
{ label: '1st child', key: 'first', hide: false, active: true },
{ label: '2nd child', key: 'second', hide: false, active: false },
{ label: '3rd child', key: 'third', hide: false, active: false },
{ label: '1st child', key: 'first', active: true },
{ label: '2nd child', key: 'second', active: false },
{ label: '3rd child', key: 'third', active: false },
];
export const Simple = () => {
......
import React, { FC, ReactNode } from 'react';
import { useTheme } from '../../themes';
import { getTabsStyle } from './styles';
import { stylesFactory, useTheme } from '../../themes';
import { GrafanaTheme } from '@grafana/data';
import { css } from 'emotion';
export interface Props {
/** Children should be a single <Tab /> or an array of <Tab /> */
children: ReactNode;
}
const getTabsBarStyles = stylesFactory((theme: GrafanaTheme) => {
const colors = theme.colors;
return {
tabsWrapper: css`
border-bottom: 1px solid ${colors.pageHeaderBorder};
`,
tabs: css`
position: relative;
top: 1px;
display: flex;
`,
};
});
export const TabsBar: FC<Props> = ({ children }) => {
const theme = useTheme();
const tabsStyles = getTabsStyle(theme);
const tabsStyles = getTabsBarStyles(theme);
return <ul className={tabsStyles.tabs}>{children}</ul>;
return (
<div className={tabsStyles.tabsWrapper}>
<ul className={tabsStyles.tabs}>{children}</ul>
</div>
);
};
import { GrafanaTheme } from '@grafana/data';
import { css } from 'emotion';
import { selectThemeVariant, stylesFactory } from '../../themes';
export const getTabsStyle = stylesFactory((theme: GrafanaTheme) => {
const colors = theme.colors;
const tabBorderColor = selectThemeVariant({ dark: colors.dark9, light: colors.gray5 }, theme.type);
return {
tabs: css`
position: relative;
top: 1px;
display: flex;
`,
tabItem: css`
list-style: none;
padding: 10px 15px 9px;
margin-right: ${theme.spacing.md};
position: relative;
display: block;
border: solid transparent;
border-width: 0 1px 1px;
border-radius: ${theme.border.radius.md} ${theme.border.radius.md} 0 0;
color: ${colors.text};
cursor: pointer;
i {
margin-right: ${theme.spacing.sm};
}
.gicon {
position: relative;
top: -2px;
}
&:hover,
&:focus {
color: ${colors.linkHover};
}
`,
activeStyle: css`
border-color: ${colors.orange} ${tabBorderColor} transparent;
background: ${colors.pageBg};
color: ${colors.link};
overflow: hidden;
cursor: not-allowed;
&::before {
display: block;
content: ' ';
position: absolute;
left: 0;
right: 0;
height: 2px;
top: 0;
background-image: linear-gradient(to right, #f05a28 30%, #fbca0a 99%);
}
`,
};
});
......@@ -50,6 +50,7 @@ export { Table } from './Table/Table';
export { TableInputCSV } from './TableInputCSV/TableInputCSV';
export { TabsBar } from './Tabs/TabsBar';
export { Tab } from './Tabs/Tab';
export { TabContent } from './Tabs/TabContent';
// Visualizations
export {
......
......@@ -93,19 +93,23 @@ exports[`ServerStats Should render table with stats 1`] = `
</option>
</select>
</div>
<ul
className="css-13jkosq"
<div
className="css-yuafq3"
>
<li
className="css-b418eg"
onClick={[Function]}
<ul
className="css-13jkosq"
>
<i
className="icon"
/>
Admin
</li>
</ul>
<li
className="css-b418eg"
onClick={[Function]}
>
<i
className="icon"
/>
Admin
</li>
</ul>
</div>
</nav>
</div>
</div>
......
import React, { PureComponent } from 'react';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { JSONFormatter, Drawer, Select, Table } from '@grafana/ui';
import { JSONFormatter, Drawer, Select, Table, TabsBar, Tab, TabContent } from '@grafana/ui';
import { getLocationSrv, getDataSourceSrv } from '@grafana/runtime';
import { DataFrame, DataSourceApi, SelectableValue, applyFieldOverrides } from '@grafana/data';
import { config } from 'app/core/config';
......@@ -139,9 +139,8 @@ export class PanelInspector extends PureComponent<Props, State> {
/>
</div>
)}
<div style={{ border: '1px solid #666' }}>
<Table width={330} height={400} data={processed[selected]} />
</div>
<Table width={330} height={400} data={processed[selected]} />
</div>
);
}
......@@ -169,19 +168,24 @@ export class PanelInspector extends PureComponent<Props, State> {
return (
<Drawer title={panel.title} onClose={this.onDismiss}>
<Select options={tabs} value={tabs.find(t => t.value === tab)} onChange={this.onSelectTab} />
{tab === InspectTab.Data && this.renderDataTab()}
{tab === InspectTab.Meta && this.renderMetadataInspector()}
{tab === InspectTab.Issue && this.renderIssueTab()}
{tab === InspectTab.Raw && (
<div>
<JSONFormatter json={last} open={2} />
</div>
)}
<TabsBar>
{tabs.map(t => {
return <Tab label={t.label} active={t.value === tab} onChangeTab={() => this.onSelectTab(t)} />;
})}
</TabsBar>
<TabContent>
{tab === InspectTab.Data && this.renderDataTab()}
{tab === InspectTab.Meta && this.renderMetadataInspector()}
{tab === InspectTab.Issue && this.renderIssueTab()}
{tab === InspectTab.Raw && (
<div>
<JSONFormatter json={last} open={2} />
</div>
)}
</TabContent>
</Drawer>
);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment