Merge pull request #11800 from influxdata/refactor/organization-tabs
refactor(ui): Separate tab navigation from tabs in organization viewpull/11795/head
commit
2a45302288
|
@ -129,7 +129,8 @@
|
|||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.tabbed-page-content {
|
||||
.tabbed-page-content,
|
||||
.tabs--contents {
|
||||
.index-list--cell {
|
||||
background-color: $g4-onyx;
|
||||
}
|
||||
|
|
|
@ -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<Props> = ({title, active, url}) => (
|
||||
<Link className={classnames('tabs--tab', {active})} to={url}>
|
||||
{title}
|
||||
</Link>
|
||||
)
|
||||
|
||||
export default NavigationTab
|
|
@ -0,0 +1,12 @@
|
|||
// Libraries
|
||||
import React, {SFC} from 'react'
|
||||
|
||||
interface Props {
|
||||
children: JSX.Element[]
|
||||
}
|
||||
|
||||
const TabContents: SFC<Props> = ({children}) => (
|
||||
<div className="tabs--contents">{children}</div>
|
||||
)
|
||||
|
||||
export default TabContents
|
|
@ -0,0 +1,12 @@
|
|||
// Libraries
|
||||
import React, {SFC} from 'react'
|
||||
|
||||
interface Props {
|
||||
children: JSX.Element[] | JSX.Element
|
||||
}
|
||||
|
||||
const TabContentsHeader: SFC<Props> = ({children}) => (
|
||||
<div className="tabs--contents-header">{children}</div>
|
||||
)
|
||||
|
||||
export default TabContentsHeader
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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<Props> {
|
||||
public static TabContents = TabContents
|
||||
public static Nav = TabsNav
|
||||
public static Tab = NavigationTab
|
||||
public static TabContentsHeader = TabContentsHeader
|
||||
|
||||
public render() {
|
||||
const {children} = this.props
|
||||
|
||||
return <div className="tabs">{children}</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default Tabs
|
|
@ -0,0 +1,14 @@
|
|||
// Libraries
|
||||
import React, {SFC} from 'react'
|
||||
|
||||
interface Props {
|
||||
children: JSX.Element | JSX.Element[]
|
||||
}
|
||||
|
||||
const TabsNav: SFC<Props> = ({children}) => (
|
||||
<div className="tabs--nav">
|
||||
<div className="tabs--nav-tabs">{children}</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default TabsNav
|
|
@ -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,
|
||||
|
|
|
@ -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<Props, State> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<Tabs.TabContentsHeader>
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder="Filter Buckets..."
|
||||
|
@ -82,7 +82,7 @@ export default class Buckets extends PureComponent<Props, State> {
|
|||
color={ComponentColor.Primary}
|
||||
onClick={this.handleOpenModal}
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
</Tabs.TabContentsHeader>
|
||||
<FilterList<PrettyBucket>
|
||||
searchTerm={searchTerm}
|
||||
searchKeys={['name', 'ruleString']}
|
||||
|
|
|
@ -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<Props, State> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<Tabs.TabContentsHeader>
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder="Filter telegraf configs by bucket..."
|
||||
|
@ -101,7 +101,7 @@ export class Collectors extends PureComponent<Props, State> {
|
|||
onBlur={this.handleFilterBlur}
|
||||
/>
|
||||
{this.createButton}
|
||||
</TabbedPageHeader>
|
||||
</Tabs.TabContentsHeader>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Column widthSM={Columns.Twelve}>
|
||||
|
|
|
@ -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<Props, State> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<Tabs.TabContentsHeader>
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder="Filter tasks..."
|
||||
|
@ -41,7 +40,7 @@ export default class Members extends PureComponent<Props, State> {
|
|||
onChange={this.handleFilterChange}
|
||||
onBlur={this.handleFilterChange}
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
</Tabs.TabContentsHeader>
|
||||
<FilterList<ResourceOwner>
|
||||
list={this.props.members}
|
||||
searchKeys={['name']}
|
||||
|
|
|
@ -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<Props, State> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<Tabs.TabContentsHeader>
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder="Filter tasks..."
|
||||
|
@ -132,7 +132,7 @@ class OrgDashboardIndex extends PureComponent<Props, State> {
|
|||
text="Create Dashboard"
|
||||
titleText="Create a new dashboard"
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
</Tabs.TabContentsHeader>
|
||||
<DashboardsIndexContents
|
||||
dashboards={dashboards}
|
||||
orgs={orgs}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
import {Tabs} from 'src/clockface'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Props {
|
||||
tab: string
|
||||
orgID: string
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class OrganizationNavigation extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {tab, orgID} = this.props
|
||||
|
||||
const route = `/organizations/${orgID}`
|
||||
|
||||
return (
|
||||
<Tabs.Nav>
|
||||
<Tabs.Tab
|
||||
title={'Members'}
|
||||
id={'members'}
|
||||
url={`${route}/members_tab`}
|
||||
active={'members_tab' === tab}
|
||||
/>
|
||||
<Tabs.Tab
|
||||
title={'Buckets'}
|
||||
id={'buckets'}
|
||||
url={`${route}/buckets_tab`}
|
||||
active={'buckets_tab' === tab}
|
||||
/>
|
||||
<Tabs.Tab
|
||||
title={'Dashboards'}
|
||||
id={'dashboards'}
|
||||
url={`${route}/dashboards_tab`}
|
||||
active={'dashboards_tab' === tab}
|
||||
/>
|
||||
<Tabs.Tab
|
||||
title={'Tasks'}
|
||||
id={'tasks'}
|
||||
url={`${route}/tasks_tab`}
|
||||
active={'tasks_tab' === tab}
|
||||
/>
|
||||
<Tabs.Tab
|
||||
title={'Telegraf'}
|
||||
id={'telegrafs'}
|
||||
url={`${route}/telegrafs_tab`}
|
||||
active={'telegrafs_tab' === tab}
|
||||
/>
|
||||
<Tabs.Tab
|
||||
title={'Scrapers'}
|
||||
id={'scrapers'}
|
||||
url={`${route}/scrapers_tab`}
|
||||
active={'scrapers_tab' === tab}
|
||||
/>
|
||||
</Tabs.Nav>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default OrganizationNavigation
|
|
@ -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<Props> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Tabs>
|
||||
<OrganizationNavigation
|
||||
tab={this.props.activeTabUrl}
|
||||
orgID={this.props.orgID}
|
||||
/>
|
||||
<Tabs.TabContents>{this.activeSectionComponent}</Tabs.TabContents>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
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<any>) => {
|
||||
if (child.props.url === activeTabUrl) {
|
||||
return child.props.children
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter<OwnProps>(OrganizationTabs)
|
|
@ -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<Props, State> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<Tabs.TabContentsHeader>
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder="Filter scrapers by bucket..."
|
||||
|
@ -66,7 +66,7 @@ export default class Scrapers extends PureComponent<Props, State> {
|
|||
onBlur={this.handleFilterBlur}
|
||||
/>
|
||||
{this.createScraperButton}
|
||||
</TabbedPageHeader>
|
||||
</Tabs.TabContentsHeader>
|
||||
<ScraperList
|
||||
scrapers={this.configurations}
|
||||
emptyState={this.emptyState}
|
||||
|
|
|
@ -27,7 +27,6 @@ import * as notifyActions from 'src/shared/actions/notifications'
|
|||
// Components
|
||||
import {Page} from 'src/pageLayout'
|
||||
import {SpinnerContainer, TechnoSpinner} from 'src/clockface'
|
||||
import TabbedPage from 'src/shared/components/tabbed_page/TabbedPage'
|
||||
import TabbedPageSection from 'src/shared/components/tabbed_page/TabbedPageSection'
|
||||
import Members from 'src/organizations/components/Members'
|
||||
import Buckets from 'src/organizations/components/Buckets'
|
||||
|
@ -36,6 +35,8 @@ import Collectors from 'src/organizations/components/Collectors'
|
|||
import Scrapers from 'src/organizations/components/Scrapers'
|
||||
import GetOrgResources from 'src/organizations/components/GetOrgResources'
|
||||
import RenamablePageTitle from 'src/pageLayout/components/RenamablePageTitle'
|
||||
import OrgDashboardIndex from 'src/organizations/components/OrgDashboardIndex'
|
||||
import OrganizationTabs from 'src/organizations/components/OrganizationTabs'
|
||||
|
||||
// Types
|
||||
import {AppState, Dashboard} from 'src/types/v2'
|
||||
|
@ -51,7 +52,6 @@ import * as NotificationsActions from 'src/types/actions/notifications'
|
|||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {Task} from 'src/tasks/containers/TasksPage'
|
||||
import OrgDashboardIndex from '../components/OrgDashboardIndex'
|
||||
|
||||
interface StateProps {
|
||||
org: Organization
|
||||
|
@ -95,10 +95,10 @@ class OrganizationView extends PureComponent<Props> {
|
|||
</Page.Header>
|
||||
<Page.Contents fullWidth={false} scrollable={true}>
|
||||
<div className="col-xs-12">
|
||||
<TabbedPage
|
||||
<OrganizationTabs
|
||||
name={org.name}
|
||||
parentUrl={`/organizations/${org.id}`}
|
||||
activeTabUrl={params.tab}
|
||||
orgID={org.id}
|
||||
>
|
||||
<TabbedPageSection
|
||||
id="org-view-tab--members"
|
||||
|
@ -191,8 +191,8 @@ class OrganizationView extends PureComponent<Props> {
|
|||
</GetOrgResources>
|
||||
</TabbedPageSection>
|
||||
<TabbedPageSection
|
||||
id="org-view-tab--collectors"
|
||||
url="collectors_tab"
|
||||
id="org-view-tab--telegrafs"
|
||||
url="telegrafs_tab"
|
||||
title="Telegraf"
|
||||
>
|
||||
<GetOrgResources<Telegraf[]>
|
||||
|
@ -265,7 +265,7 @@ class OrganizationView extends PureComponent<Props> {
|
|||
}}
|
||||
</GetOrgResources>
|
||||
</TabbedPageSection>
|
||||
</TabbedPage>
|
||||
</OrganizationTabs>
|
||||
</div>
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
|
|
Loading…
Reference in New Issue