From b804f05de2f118c9cd36170891b9025b6f55fe9d Mon Sep 17 00:00:00 2001 From: Iris Scholten Date: Fri, 8 Feb 2019 16:48:25 -0800 Subject: [PATCH] refactor(ui): Separate tab navigation from tabs in organization view create tabs components Use TabContentsHeader in individual org tabs rename organizationTabbedPage to OrganizationTabs Move tabs to clockface --- .../components/index_views/IndexList.scss | 3 +- .../components/tabs/NavigationTab.tsx | 19 ++++ .../clockface/components/tabs/TabContents.tsx | 12 +++ .../components/tabs/TabContentsHeader.tsx | 12 +++ ui/src/clockface/components/tabs/Tabs.scss | 93 +++++++++++++++++++ ui/src/clockface/components/tabs/Tabs.tsx | 30 ++++++ ui/src/clockface/components/tabs/TabsNav.tsx | 14 +++ ui/src/clockface/index.ts | 2 + ui/src/organizations/components/Buckets.tsx | 6 +- .../organizations/components/Collectors.tsx | 6 +- ui/src/organizations/components/Members.tsx | 7 +- .../components/OrgDashboardIndex.tsx | 6 +- .../components/OrganizationNavigation.tsx | 66 +++++++++++++ .../components/OrganizationTabs.tsx | 53 +++++++++++ ui/src/organizations/components/Scrapers.tsx | 6 +- .../containers/OrganizationView.tsx | 14 +-- 16 files changed, 325 insertions(+), 24 deletions(-) create mode 100644 ui/src/clockface/components/tabs/NavigationTab.tsx create mode 100644 ui/src/clockface/components/tabs/TabContents.tsx create mode 100644 ui/src/clockface/components/tabs/TabContentsHeader.tsx create mode 100644 ui/src/clockface/components/tabs/Tabs.scss create mode 100644 ui/src/clockface/components/tabs/Tabs.tsx create mode 100644 ui/src/clockface/components/tabs/TabsNav.tsx create mode 100644 ui/src/organizations/components/OrganizationNavigation.tsx create mode 100644 ui/src/organizations/components/OrganizationTabs.tsx diff --git a/ui/src/clockface/components/index_views/IndexList.scss b/ui/src/clockface/components/index_views/IndexList.scss index 53df207dd9..4de48708d8 100644 --- a/ui/src/clockface/components/index_views/IndexList.scss +++ b/ui/src/clockface/components/index_views/IndexList.scss @@ -129,7 +129,8 @@ ------------------------------------------------------------------------------ */ -.tabbed-page-content { +.tabbed-page-content, +.tabs--contents { .index-list--cell { background-color: $g4-onyx; } diff --git a/ui/src/clockface/components/tabs/NavigationTab.tsx b/ui/src/clockface/components/tabs/NavigationTab.tsx new file mode 100644 index 0000000000..532e455cb4 --- /dev/null +++ b/ui/src/clockface/components/tabs/NavigationTab.tsx @@ -0,0 +1,19 @@ +// Libraries +import React, {SFC} from 'react' +import classnames from 'classnames' +import {Link} from 'react-router' + +interface Props { + id: string + title: string + active: boolean + url: string +} + +const NavigationTab: SFC = ({title, active, url}) => ( + + {title} + +) + +export default NavigationTab diff --git a/ui/src/clockface/components/tabs/TabContents.tsx b/ui/src/clockface/components/tabs/TabContents.tsx new file mode 100644 index 0000000000..d94a62d064 --- /dev/null +++ b/ui/src/clockface/components/tabs/TabContents.tsx @@ -0,0 +1,12 @@ +// Libraries +import React, {SFC} from 'react' + +interface Props { + children: JSX.Element[] +} + +const TabContents: SFC = ({children}) => ( +
{children}
+) + +export default TabContents diff --git a/ui/src/clockface/components/tabs/TabContentsHeader.tsx b/ui/src/clockface/components/tabs/TabContentsHeader.tsx new file mode 100644 index 0000000000..28d5fc1e18 --- /dev/null +++ b/ui/src/clockface/components/tabs/TabContentsHeader.tsx @@ -0,0 +1,12 @@ +// Libraries +import React, {SFC} from 'react' + +interface Props { + children: JSX.Element[] | JSX.Element +} + +const TabContentsHeader: SFC = ({children}) => ( +
{children}
+) + +export default TabContentsHeader diff --git a/ui/src/clockface/components/tabs/Tabs.scss b/ui/src/clockface/components/tabs/Tabs.scss new file mode 100644 index 0000000000..84940729c2 --- /dev/null +++ b/ui/src/clockface/components/tabs/Tabs.scss @@ -0,0 +1,93 @@ +/* + Styles for Tabs + ---------------------------------------------------------------------------- +*/ + +@import 'src/style/modules'; + +.tabs { + display: flex; + align-items: stretch; + background-color: rgba($g3-castle, 0.25); + border-radius: $radius; +} + +.tabs--nav { + display: flex; + flex-direction: column; + align-items: stretch; + flex: 0 1 0; + padding: $ix-marg-d; + padding-right: 0; +} + +.tabs--nav-tabs { + @include no-user-select(); + display: flex; + flex-direction: column; + align-items: stretch; +} + +.tabs--tab { + border-radius: $radius 0 0 $radius; + font-size: $form-lg-font; + height: $nav-size; + line-height: $nav-size; + padding: 0 $form-lg-font; + color: $g11-sidewalk; + white-space: nowrap; + transition: background-color 0.25s ease, color 0.25s ease; + + &:hover { + background-color: rgba($g3-castle, 0.5); + color: $g16-pearl; + cursor: pointer; + } + + &.active { + background-color: $g3-castle; + color: $g18-cloud; + } +} + +.page{ + a:link.tabs--tab, + a:visited.tabs--tab { + border-radius: $radius 0 0 $radius; + font-size: $form-lg-font; + font-weight: 400; + height: $nav-size; + line-height: $nav-size; + padding: 0 $form-lg-font; + color: $g11-sidewalk; + white-space: nowrap; + transition: background-color 0.25s ease, color 0.25s ease; + + &:hover { + background-color: rgba($g3-castle, 0.5); + color: $g16-pearl; + cursor: pointer; + } + + &.active { + background-color: $g3-castle; + color: $g18-cloud; + } + } +} + +.tabs--contents { + flex: 1 0 0; + background-color: $g3-castle; + border-radius: $radius; + min-height: 500px; + padding: $ix-marg-d; +} + +.tabs--contents-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: $ix-marg-d; +} + diff --git a/ui/src/clockface/components/tabs/Tabs.tsx b/ui/src/clockface/components/tabs/Tabs.tsx new file mode 100644 index 0000000000..71405a417f --- /dev/null +++ b/ui/src/clockface/components/tabs/Tabs.tsx @@ -0,0 +1,30 @@ +// Libraries +import React, {Component} from 'react' + +// Styles +import 'src/clockface/components/tabs/Tabs.scss' + +// Components +import TabContents from 'src/clockface/components/tabs/TabContents' +import TabsNav from 'src/clockface/components/tabs/TabsNav' +import NavigationTab from 'src/clockface/components/tabs/NavigationTab' +import TabContentsHeader from 'src/clockface/components/tabs/TabContentsHeader' + +interface Props { + children: JSX.Element[] +} + +class Tabs extends Component { + public static TabContents = TabContents + public static Nav = TabsNav + public static Tab = NavigationTab + public static TabContentsHeader = TabContentsHeader + + public render() { + const {children} = this.props + + return
{children}
+ } +} + +export default Tabs diff --git a/ui/src/clockface/components/tabs/TabsNav.tsx b/ui/src/clockface/components/tabs/TabsNav.tsx new file mode 100644 index 0000000000..8c83518834 --- /dev/null +++ b/ui/src/clockface/components/tabs/TabsNav.tsx @@ -0,0 +1,14 @@ +// Libraries +import React, {SFC} from 'react' + +interface Props { + children: JSX.Element | JSX.Element[] +} + +const TabsNav: SFC = ({children}) => ( +
+
{children}
+
+) + +export default TabsNav diff --git a/ui/src/clockface/index.ts b/ui/src/clockface/index.ts index 5742a05b01..b0153e2284 100644 --- a/ui/src/clockface/index.ts +++ b/ui/src/clockface/index.ts @@ -37,6 +37,7 @@ import Grid from 'src/clockface/components/grid_layout/Grid' import QuestionMarkTooltip from 'src/clockface/components/tooltips/QuestionMarkTooltip' import SpinnerContainer from 'src/clockface/components/spinners/SpinnerContainer' import TechnoSpinner from 'src/clockface/components/spinners/TechnoSpinner' +import Tabs from './components/tabs/Tabs' // Import Types import { @@ -107,6 +108,7 @@ export { Sort, SpinnerContainer, Stack, + Tabs, TechnoSpinner, WizardFullScreen, WizardProgressHeader, diff --git a/ui/src/organizations/components/Buckets.tsx b/ui/src/organizations/components/Buckets.tsx index 53d4618a65..a33b765dc9 100644 --- a/ui/src/organizations/components/Buckets.tsx +++ b/ui/src/organizations/components/Buckets.tsx @@ -3,7 +3,6 @@ import React, {PureComponent, ChangeEvent} from 'react' import _ from 'lodash' // Components -import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader' import FilterList from 'src/shared/components/Filter' import BucketList from 'src/organizations/components/BucketList' import {PrettyBucket} from 'src/organizations/components/BucketRow' @@ -16,6 +15,7 @@ import { OverlayTechnology, ComponentSize, EmptyState, + Tabs, } from 'src/clockface' // Actions @@ -67,7 +67,7 @@ export default class Buckets extends PureComponent { return ( <> - + { color={ComponentColor.Primary} onClick={this.handleOpenModal} /> - + searchTerm={searchTerm} searchKeys={['name', 'ruleString']} diff --git a/ui/src/organizations/components/Collectors.tsx b/ui/src/organizations/components/Collectors.tsx index 29302935ed..7a390d46d7 100644 --- a/ui/src/organizations/components/Collectors.tsx +++ b/ui/src/organizations/components/Collectors.tsx @@ -4,7 +4,6 @@ import React, {PureComponent, ChangeEvent} from 'react' import {connect} from 'react-redux' // Components -import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader' import CollectorList from 'src/organizations/components/CollectorList' import TelegrafExplainer from 'src/organizations/components/TelegrafExplainer' import TelegrafInstructionsOverlay from 'src/organizations/components/TelegrafInstructionsOverlay' @@ -19,6 +18,7 @@ import { Columns, Input, InputType, + Tabs, } from 'src/clockface' import CollectorsWizard from 'src/dataLoaders/components/collectorsWizard/CollectorsWizard' import FilterList from 'src/shared/components/Filter' @@ -90,7 +90,7 @@ export class Collectors extends PureComponent { return ( <> - + { onBlur={this.handleFilterBlur} /> {this.createButton} - + diff --git a/ui/src/organizations/components/Members.tsx b/ui/src/organizations/components/Members.tsx index c5753ef231..0b70b02de4 100644 --- a/ui/src/organizations/components/Members.tsx +++ b/ui/src/organizations/components/Members.tsx @@ -3,10 +3,9 @@ import React, {PureComponent, ChangeEvent} from 'react' import _ from 'lodash' // Components -import {ComponentSize, EmptyState, IconFont, Input} from 'src/clockface' +import {ComponentSize, EmptyState, IconFont, Input, Tabs} from 'src/clockface' import MemberList from 'src/organizations/components/MemberList' import FilterList from 'src/shared/components/Filter' -import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader' // Types import {ResourceOwner} from '@influxdata/influx' @@ -32,7 +31,7 @@ export default class Members extends PureComponent { return ( <> - + { onChange={this.handleFilterChange} onBlur={this.handleFilterChange} /> - + list={this.props.members} searchKeys={['name']} diff --git a/ui/src/organizations/components/OrgDashboardIndex.tsx b/ui/src/organizations/components/OrgDashboardIndex.tsx index 606d488eff..3e3eb16153 100644 --- a/ui/src/organizations/components/OrgDashboardIndex.tsx +++ b/ui/src/organizations/components/OrgDashboardIndex.tsx @@ -13,10 +13,10 @@ import { ComponentColor, IconFont, Input, + Tabs, } from 'src/clockface' import ImportDashboardOverlay from 'src/dashboards/components/ImportDashboardOverlay' import EditLabelsOverlay from 'src/shared/components/EditLabelsOverlay' -import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader' // Utils import {getDeep} from 'src/utils/wrappers' @@ -116,7 +116,7 @@ class OrgDashboardIndex extends PureComponent { return ( <> - + { text="Create Dashboard" titleText="Create a new dashboard" /> - + { + public render() { + const {tab, orgID} = this.props + + const route = `/organizations/${orgID}` + + return ( + + + + + + + + + ) + } +} + +export default OrganizationNavigation diff --git a/ui/src/organizations/components/OrganizationTabs.tsx b/ui/src/organizations/components/OrganizationTabs.tsx new file mode 100644 index 0000000000..72105d105b --- /dev/null +++ b/ui/src/organizations/components/OrganizationTabs.tsx @@ -0,0 +1,53 @@ +// Libraries +import React, {Component, ReactElement, ReactNode} from 'react' +import {withRouter, WithRouterProps} from 'react-router' + +// Components +import OrganizationNavigation from 'src/organizations/components/OrganizationNavigation' +import {Tabs} from 'src/clockface' + +// Decorators +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface OwnProps { + name: string + avatar?: string + description?: string + children: ReactNode[] | ReactNode + activeTabUrl: string + orgID: string +} + +type Props = OwnProps & WithRouterProps + +@ErrorHandling +class OrganizationTabs extends Component { + constructor(props) { + super(props) + } + + public render() { + return ( + + + {this.activeSectionComponent} + + ) + } + + private get activeSectionComponent(): JSX.Element[] { + const {children, activeTabUrl} = this.props + + // Using ReactElement as type to ensure children have props + return React.Children.map(children, (child: ReactElement) => { + if (child.props.url === activeTabUrl) { + return child.props.children + } + }) + } +} + +export default withRouter(OrganizationTabs) diff --git a/ui/src/organizations/components/Scrapers.tsx b/ui/src/organizations/components/Scrapers.tsx index 36f4a772ad..e02e10a3cd 100644 --- a/ui/src/organizations/components/Scrapers.tsx +++ b/ui/src/organizations/components/Scrapers.tsx @@ -6,7 +6,6 @@ import React, {PureComponent, ChangeEvent} from 'react' import {client} from 'src/utils/api' // Components -import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader' import ScraperList from 'src/organizations/components/ScraperList' import { Button, @@ -16,6 +15,7 @@ import { EmptyState, Input, InputType, + Tabs, } from 'src/clockface' import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard' @@ -55,7 +55,7 @@ export default class Scrapers extends PureComponent { return ( <> - + { onBlur={this.handleFilterBlur} /> {this.createScraperButton} - + {
- { @@ -265,7 +265,7 @@ class OrganizationView extends PureComponent { }} - +