diff --git a/packages/grafana-ui/src/components/Tabs/Tab.tsx b/packages/grafana-ui/src/components/Tabs/Tab.tsx index 9d82ab13aa3..e0d46815a73 100644 --- a/packages/grafana-ui/src/components/Tabs/Tab.tsx +++ b/packages/grafana-ui/src/components/Tabs/Tab.tsx @@ -1,7 +1,7 @@ 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 = ({ label, active, icon, onChangeTab }) => { const theme = useTheme(); - const tabsStyles = getTabsStyle(theme); + const tabsStyles = getTabStyles(theme); return (
  • diff --git a/packages/grafana-ui/src/components/Tabs/TabContent.tsx b/packages/grafana-ui/src/components/Tabs/TabContent.tsx new file mode 100644 index 00000000000..3681329cfc9 --- /dev/null +++ b/packages/grafana-ui/src/components/Tabs/TabContent.tsx @@ -0,0 +1,23 @@ +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 = ({ children }) => { + const theme = useTheme(); + const styles = getTabContentStyle(theme); + + return
    {children}
    ; +}; diff --git a/packages/grafana-ui/src/components/Tabs/Tabs.story.tsx b/packages/grafana-ui/src/components/Tabs/Tabs.story.tsx new file mode 100644 index 00000000000..e19b7db4361 --- /dev/null +++ b/packages/grafana-ui/src/components/Tabs/Tabs.story.tsx @@ -0,0 +1,47 @@ +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 ( + + {(state, updateState) => { + return ( +
    + + {state.map((tab, index) => { + return ( + updateState(state.map((tab, idx) => ({ ...tab, active: idx === index })))} + /> + ); + })} + + + {state[0].active &&
    First tab content
    } + {state[1].active &&
    Second tab content
    } + {state[2].active &&
    Third tab content
    } +
    +
    + ); + }} +
    + ); +}; diff --git a/packages/grafana-ui/src/components/Tabs/TabsBar.story.tsx b/packages/grafana-ui/src/components/Tabs/TabsBar.story.tsx index cb724681dd7..24772b9c564 100644 --- a/packages/grafana-ui/src/components/Tabs/TabsBar.story.tsx +++ b/packages/grafana-ui/src/components/Tabs/TabsBar.story.tsx @@ -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 = () => { diff --git a/packages/grafana-ui/src/components/Tabs/TabsBar.tsx b/packages/grafana-ui/src/components/Tabs/TabsBar.tsx index 5c95b769511..dd21e055e80 100644 --- a/packages/grafana-ui/src/components/Tabs/TabsBar.tsx +++ b/packages/grafana-ui/src/components/Tabs/TabsBar.tsx @@ -1,15 +1,35 @@ 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 or an array of */ 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 = ({ children }) => { const theme = useTheme(); - const tabsStyles = getTabsStyle(theme); + const tabsStyles = getTabsBarStyles(theme); - return
      {children}
    ; + return ( +
    +
      {children}
    +
    + ); }; diff --git a/packages/grafana-ui/src/components/Tabs/styles.ts b/packages/grafana-ui/src/components/Tabs/styles.ts deleted file mode 100644 index 48a8f782595..00000000000 --- a/packages/grafana-ui/src/components/Tabs/styles.ts +++ /dev/null @@ -1,60 +0,0 @@ -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%); - } - `, - }; -}); diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 356fcb161cd..0bc873c90b9 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -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 { diff --git a/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap b/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap index bb2899cf48e..76ddcdf8d8c 100644 --- a/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap +++ b/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap @@ -93,19 +93,23 @@ exports[`ServerStats Should render table with stats 1`] = ` -
      -
    • - - Admin -
    • -
    +
  • + + Admin +
  • + + diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx index e74673228dc..1d099294b2a 100644 --- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx @@ -1,7 +1,7 @@ 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 { /> )} -
    - - + +
    ); } @@ -169,19 +168,24 @@ export class PanelInspector extends PureComponent { return ( -