From 5270e11a49b47830cdace8e9f2ccc25435714cc2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 20 Apr 2018 15:44:46 -0700 Subject: [PATCH 1/7] Introduce subsections component --- ui/src/shared/components/SubSections.tsx | 74 +++++++++++++++++++++ ui/src/style/chronograf.scss | 1 + ui/src/style/layout/page-subsections.scss | 64 +++++++++++++++++++ ui/src/style/pages/admin.scss | 78 ----------------------- 4 files changed, 139 insertions(+), 78 deletions(-) create mode 100644 ui/src/shared/components/SubSections.tsx create mode 100644 ui/src/style/layout/page-subsections.scss diff --git a/ui/src/shared/components/SubSections.tsx b/ui/src/shared/components/SubSections.tsx new file mode 100644 index 0000000000..e1c7692df4 --- /dev/null +++ b/ui/src/shared/components/SubSections.tsx @@ -0,0 +1,74 @@ +import React, {Component, ReactNode} from 'react' +import uuid from 'uuid' +import {withRouter, InjectedRouter} from 'react-router' + +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface Section { + url: string + name: string + component: ReactNode + enabled: boolean +} + +interface Props { + sections: Section[] + activeSection: string + sourceID: string + router: InjectedRouter + parentUrl: string +} + +@ErrorHandling +@withRouter +class SubSections extends Component { + constructor(props) { + super(props) + } + + public render() { + const {sections} = this.props + + return ( +
+
+
+ {sections.map( + section => + section.enabled && ( +
+ {section.name} +
+ ) + )} +
+
+
+ {this.activeSection} +
+
+ ) + } + + private get activeSection(): ReactNode { + const {sections, activeSection} = this.props + const {component} = sections.find(section => section.url === activeSection) + return component + } + + public getTabClass(sectionName: string) { + const {activeSection} = this.props + return `subsection--tab ${sectionName === activeSection ? 'active' : ''}` + } + + public handleTabClick = url => () => { + const {router, sourceID, parentUrl} = this.props + router.push(`/sources/${sourceID}/${parentUrl}/${url}`) + } +} + +export default SubSections diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index 5c9a38dd37..d9594e948a 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -27,6 +27,7 @@ // Layout @import 'layout/page'; @import 'layout/page-header'; +@import 'layout/page-subsections'; @import 'layout/sidebar'; @import 'layout/overlay'; diff --git a/ui/src/style/layout/page-subsections.scss b/ui/src/style/layout/page-subsections.scss new file mode 100644 index 0000000000..b912a739fe --- /dev/null +++ b/ui/src/style/layout/page-subsections.scss @@ -0,0 +1,64 @@ +/* + Page Sub-Sections + ---------------------------------------------------------------------------- +*/ + +$subsection-font: 17px; + +.subsection { + .panel { + border-top-left-radius: 0; + } + .panel-heading { + height: 60px; + padding-top: 0; + padding-bottom: 0; + } + .panel-heading + .panel-body { + padding-top: 0; + } + .panel-body { + min-height: 500px; + } + .panel-title { + font-size: $subsection-font; + } +} + +.subsection--tabs { + display: flex; + flex-direction: column; + align-items: stretch; +} + +.subsection--tab { + border-radius: $radius 0 0 $radius; + padding: 0 8px 0 16px; + height: $chronograf-page-header-height; + white-space: nowrap; + line-height: $chronograf-page-header-height; + text-align: left; + font-size: $subsection-font; + font-weight: 500; + color: $g11-sidewalk; + @include no-user-select(); + transition: background-color 0.25s ease, color 0.25s ease; + + &:hover, + &.active { + cursor: pointer; + color: $g18-cloud; + background-color: $g3-castle; + } +} + +@media screen and (min-width: $grid--breakpoint-md) { + .subsection { + .subsection--nav { + padding-right: 0; + } + .subsection--content { + padding-left: 0; + } + } +} \ No newline at end of file diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index 1731246c4f..1ad355ecd1 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -3,84 +3,6 @@ ---------------------------------------------------------------------------- */ -/* - Admin Tabs - ---------------------------------------------------------------------------- -*/ -.admin-tabs .btn-group { - margin: 0; - width: 100%; - display: flex; - align-items: stretch; - - .tab { - font-weight: 500 !important; - border-radius: $radius $radius 0 0 !important; - transition: background-color 0.25s ease, color 0.25s ease !important; - border: 0 !important; - text-align: left; - height: 60px !important; - line-height: 60px !important; - padding: 0 30px !important; - font-size: 17px; - background-color: transparent !important; - color: $g11-sidewalk !important; - - &:hover, - &:active, - &:active:hover { - background-color: $g3-castle !important; - color: $g15-platinum !important; - } - &.active { - background-color: $g3-castle !important; - color: $g18-cloud !important; - } - } -} -.admin-tabs--content { - .panel { - border-top-left-radius: 0; - } - .panel-heading { - height: 60px; - } - .panel-title { - font-size: 17px; - font-weight: 400 !important; - color: $g12-forge; - padding: 6px 0; - } - .panel-body { - min-height: 300px; - } - .panel-heading + .panel-body { - padding-top: 0; - } -} - -/* - Responsive Layout for Admin Tabs on Small Screens - ---------------------------------------------------------------------------- -*/ - -@media screen and (min-width: 992px) { - .admin-tabs { - padding-right: 0; - - .btn-group { - flex-direction: column; - } - .btn-group .tab { - border-radius: $radius 0 0 $radius !important; - padding: 0 0 0 16px !important; - } - & + div { - padding-left: 0; - } - } -} - /* Admin Table ---------------------------------------------------------------------------- From bfba09029162f133febbc9db35f8b2a0a9c976c9 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 20 Apr 2018 15:45:55 -0700 Subject: [PATCH 2/7] Replace InfluxDB AdminTabs component with SubSections component --- ui/src/admin/components/AdminTabs.js | 140 ------------------- ui/src/admin/containers/AdminInfluxDBPage.js | 107 ++++++++++---- ui/src/index.tsx | 2 +- ui/src/side_nav/containers/SideNav.tsx | 6 +- 4 files changed, 82 insertions(+), 173 deletions(-) delete mode 100644 ui/src/admin/components/AdminTabs.js diff --git a/ui/src/admin/components/AdminTabs.js b/ui/src/admin/components/AdminTabs.js deleted file mode 100644 index 1296be1b21..0000000000 --- a/ui/src/admin/components/AdminTabs.js +++ /dev/null @@ -1,140 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'shared/components/Tabs' -import UsersTable from 'src/admin/components/UsersTable' -import RolesTable from 'src/admin/components/RolesTable' -import QueriesPage from 'src/admin/containers/QueriesPage' -import DatabaseManagerPage from 'src/admin/containers/DatabaseManagerPage' - -const AdminTabs = ({ - users, - roles, - permissions, - source, - hasRoles, - isEditingUsers, - isEditingRoles, - onClickCreate, - onEditUser, - onSaveUser, - onCancelEditUser, - onEditRole, - onSaveRole, - onCancelEditRole, - onDeleteRole, - onDeleteUser, - onFilterRoles, - onFilterUsers, - onUpdateRoleUsers, - onUpdateRolePermissions, - onUpdateUserRoles, - onUpdateUserPermissions, - onUpdateUserPassword, -}) => { - let tabs = [ - { - type: 'Databases', - component: , - }, - { - type: 'Users', - component: ( - - ), - }, - { - type: 'Roles', - component: ( - - ), - }, - { - type: 'Queries', - component: , - }, - ] - - if (!hasRoles) { - tabs = tabs.filter(t => t.type !== 'Roles') - } - - return ( - - - {tabs.map((t, i) => {tabs[i].type})} - - - {tabs.map((t, i) => ( - {t.component} - ))} - - - ) -} - -const {arrayOf, bool, func, shape, string} = PropTypes - -AdminTabs.propTypes = { - users: arrayOf( - shape({ - name: string.isRequired, - roles: arrayOf( - shape({ - name: string, - }) - ), - }) - ), - roles: arrayOf(shape()), - source: shape(), - permissions: arrayOf(string), - isEditingUsers: bool, - isEditingRoles: bool, - onClickCreate: func.isRequired, - onEditUser: func.isRequired, - onSaveUser: func.isRequired, - onCancelEditUser: func.isRequired, - onEditRole: func.isRequired, - onSaveRole: func.isRequired, - onCancelEditRole: func.isRequired, - onDeleteRole: func.isRequired, - onDeleteUser: func.isRequired, - onFilterRoles: func.isRequired, - onFilterUsers: func.isRequired, - onUpdateRoleUsers: func.isRequired, - onUpdateRolePermissions: func.isRequired, - hasRoles: bool.isRequired, - onUpdateUserPermissions: func, - onUpdateUserRoles: func, - onUpdateUserPassword: func, -} - -export default AdminTabs diff --git a/ui/src/admin/containers/AdminInfluxDBPage.js b/ui/src/admin/containers/AdminInfluxDBPage.js index 5f95977285..d39060fbce 100644 --- a/ui/src/admin/containers/AdminInfluxDBPage.js +++ b/ui/src/admin/containers/AdminInfluxDBPage.js @@ -25,9 +25,13 @@ import { filterRoles as filterRolesAction, } from 'src/admin/actions/influxdb' -import AdminTabs from 'src/admin/components/AdminTabs' +import UsersTable from 'src/admin/components/UsersTable' +import RolesTable from 'src/admin/components/RolesTable' +import QueriesPage from 'src/admin/containers/QueriesPage' +import DatabaseManagerPage from 'src/admin/containers/DatabaseManagerPage' import SourceIndicator from 'shared/components/SourceIndicator' import FancyScrollbar from 'shared/components/FancyScrollbar' +import SubSections from 'shared/components/SubSections' import {ErrorHandling} from 'src/shared/decorators/errors' import {notify as notifyAction} from 'shared/actions/notifications' @@ -141,7 +145,7 @@ class AdminInfluxDBPage extends Component { this.props.updateUserPassword(user, password) } - render() { + getAdminSubSections = () => { const { users, roles, @@ -154,6 +158,69 @@ class AdminInfluxDBPage extends Component { const globalPermissions = permissions.find(p => p.scope === 'all') const allowed = globalPermissions ? globalPermissions.allowed : [] + return [ + { + url: 'databases', + name: 'Databases', + enabled: true, + component: , + }, + { + url: 'users', + name: 'Users', + enabled: true, + component: ( + u.isEditing)} + onSave={this.handleSaveUser} + onCancel={this.handleCancelEditUser} + onClickCreate={this.handleClickCreate} + onEdit={this.handleEditUser} + onDelete={this.handleDeleteUser} + onFilter={filterUsers} + onUpdatePermissions={this.handleUpdateUserPermissions} + onUpdateRoles={this.handleUpdateUserRoles} + onUpdatePassword={this.handleUpdateUserPassword} + /> + ), + }, + { + url: 'roles', + name: 'Roles', + enabled: hasRoles, + component: ( + r.isEditing)} + onClickCreate={this.handleClickCreate} + onEdit={this.handleEditRole} + onSave={this.handleSaveRole} + onCancel={this.handleCancelEditRole} + onDelete={this.handleDeleteRole} + onFilter={filterRoles} + onUpdateRoleUsers={this.handleUpdateRoleUsers} + onUpdateRolePermissions={this.handleUpdateRolePermissions} + /> + ), + }, + { + url: 'queries', + name: 'Queries', + enabled: true, + component: , + }, + ] + } + + render() { + const {users, source, params} = this.props + return (
@@ -169,33 +236,12 @@ class AdminInfluxDBPage extends Component { {users ? (
-
- u.isEditing)} - isEditingRoles={roles.some(r => r.isEditing)} - onUpdateRoleUsers={this.handleUpdateRoleUsers} - onUpdateUserRoles={this.handleUpdateUserRoles} - onUpdateUserPassword={this.handleUpdateUserPassword} - onUpdateRolePermissions={this.handleUpdateRolePermissions} - onUpdateUserPermissions={this.handleUpdateUserPermissions} - /> -
+
) : (
@@ -239,6 +285,9 @@ AdminInfluxDBPage.propTypes = { updateUserRoles: func, updateUserPassword: func, notify: func.isRequired, + params: shape({ + tab: string, + }).isRequired, } const mapStateToProps = ({adminInfluxDB: {users, roles, permissions}}) => ({ diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 10ffc7dec0..5f8572b98f 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -147,7 +147,7 @@ class Root extends PureComponent<{}, State> { component={KapacitorPage} /> - + diff --git a/ui/src/side_nav/containers/SideNav.tsx b/ui/src/side_nav/containers/SideNav.tsx index dc17ddbb07..d08d3ca460 100644 --- a/ui/src/side_nav/containers/SideNav.tsx +++ b/ui/src/side_nav/containers/SideNav.tsx @@ -101,11 +101,11 @@ class SideNav extends PureComponent { replaceWithIfNotUsingAuth={ @@ -123,7 +123,7 @@ class SideNav extends PureComponent { Chronograf - + InfluxDB From 7d8568af48fc1a40d742c1aad8260be9529065a4 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 20 Apr 2018 16:24:44 -0700 Subject: [PATCH 3/7] Refactor sidenav to allow more specificity for highlighting nav items --- ui/src/side_nav/components/NavItems.tsx | 12 +++++------- ui/src/side_nav/containers/SideNav.tsx | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ui/src/side_nav/components/NavItems.tsx b/ui/src/side_nav/components/NavItems.tsx index 927f0ff59c..db51eeb4c9 100644 --- a/ui/src/side_nav/components/NavItems.tsx +++ b/ui/src/side_nav/components/NavItems.tsx @@ -1,6 +1,7 @@ import React, {PureComponent, SFC, ReactNode, ReactElement} from 'react' import {Link} from 'react-router' import classnames from 'classnames' +import _ from 'lodash' interface NavListItemProps { link: string @@ -62,17 +63,14 @@ interface NavBlockProps { icon: string location?: string className?: string - matcher?: string + highlightWhen: string[] } class NavBlock extends PureComponent { public render() { - const {location, className} = this.props - const isActive = React.Children.toArray(this.props.children).find( - (child: ReactElement) => { - return location.startsWith(child.props.link) // if location is undefined, this will fail silently - } - ) + const {location, className, highlightWhen} = this.props + const {length} = _.intersection(_.split(location, '/'), highlightWhen) + const isActive = !!length const children = React.Children.map( this.props.children, diff --git a/ui/src/side_nav/containers/SideNav.tsx b/ui/src/side_nav/containers/SideNav.tsx index d08d3ca460..1f53c64a52 100644 --- a/ui/src/side_nav/containers/SideNav.tsx +++ b/ui/src/side_nav/containers/SideNav.tsx @@ -62,19 +62,26 @@ class SideNav extends PureComponent {
- + { { requiredRole={ADMIN_ROLE} replaceWithIfNotUsingAuth={ { } > { Date: Fri, 20 Apr 2018 16:49:38 -0700 Subject: [PATCH 4/7] Set autocomplete using string instead of boolean Fixing this in response to a browser error I noticed in this branch after changing a bunch of things --- ui/src/admin/components/DatabaseRow.js | 6 +++--- ui/src/admin/components/DatabaseTableHeader.js | 4 ++-- ui/src/admin/components/RoleEditingRow.js | 2 +- ui/src/admin/components/UserEditName.js | 2 +- ui/src/admin/components/UserNewPassword.js | 2 +- .../dashboards/components/template_variables/RowValues.js | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/src/admin/components/DatabaseRow.js b/ui/src/admin/components/DatabaseRow.js index 9a0e7ff2a5..4ea6470922 100644 --- a/ui/src/admin/components/DatabaseRow.js +++ b/ui/src/admin/components/DatabaseRow.js @@ -150,7 +150,7 @@ class DatabaseRow extends Component { ref={r => (this.name = r)} autoFocus={true} spellCheck={false} - autoComplete={false} + autoComplete="false" /> ) : ( name @@ -167,7 +167,7 @@ class DatabaseRow extends Component { ref={r => (this.duration = r)} autoFocus={!isNew} spellCheck={false} - autoComplete={false} + autoComplete="false" /> {isRFDisplayed ? ( @@ -182,7 +182,7 @@ class DatabaseRow extends Component { onKeyDown={this.handleKeyDown} ref={r => (this.replication = r)} spellCheck={false} - autoComplete={false} + autoComplete="false" /> ) : null} diff --git a/ui/src/admin/components/DatabaseTableHeader.js b/ui/src/admin/components/DatabaseTableHeader.js index b56e55e815..d37def718c 100644 --- a/ui/src/admin/components/DatabaseTableHeader.js +++ b/ui/src/admin/components/DatabaseTableHeader.js @@ -98,7 +98,7 @@ const Header = ({ onChange={onDatabaseDeleteConfirm(database)} onKeyDown={onDatabaseDeleteConfirm(database)} autoFocus={true} - autoComplete={false} + autoComplete="false" spellCheck={false} /> ( onKeyDown={onKeyDown(database)} autoFocus={true} spellCheck={false} - autoComplete={false} + autoComplete="false" /> ) diff --git a/ui/src/admin/components/UserEditName.js b/ui/src/admin/components/UserEditName.js index 357ae65f55..34b3a8b3cf 100644 --- a/ui/src/admin/components/UserEditName.js +++ b/ui/src/admin/components/UserEditName.js @@ -38,7 +38,7 @@ class UserEditName extends Component { onKeyPress={this.handleKeyPress(user)} autoFocus={true} spellCheck={false} - autoComplete={false} + autoComplete="false" /> ) diff --git a/ui/src/admin/components/UserNewPassword.js b/ui/src/admin/components/UserNewPassword.js index 3d50067cad..a24aeef3e7 100644 --- a/ui/src/admin/components/UserNewPassword.js +++ b/ui/src/admin/components/UserNewPassword.js @@ -34,7 +34,7 @@ class UserNewPassword extends Component { onChange={this.handleEdit(user)} onKeyPress={this.handleKeyPress(user)} spellCheck={false} - autoComplete={false} + autoComplete="false" /> ) : ( '--' diff --git a/ui/src/dashboards/components/template_variables/RowValues.js b/ui/src/dashboards/components/template_variables/RowValues.js index dc77afb51d..bc0a915de0 100644 --- a/ui/src/dashboards/components/template_variables/RowValues.js +++ b/ui/src/dashboards/components/template_variables/RowValues.js @@ -20,7 +20,7 @@ const RowValues = ({ onStartEdit={onStartEdit} autoFocusTarget={autoFocusTarget} spellCheck={false} - autoComplete={false} + autoComplete="false" /> ) } From 4880f143479136d76ebb81872528b8eaaae31cd5 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 20 Apr 2018 19:48:09 -0700 Subject: [PATCH 5/7] Convert tabs to subcomponent --- ui/src/shared/components/SubSections.tsx | 43 ++++++++------------- ui/src/shared/components/SubSectionsTab.tsx | 25 ++++++++++++ ui/src/types/shared.ts | 9 +++++ 3 files changed, 51 insertions(+), 26 deletions(-) create mode 100644 ui/src/shared/components/SubSectionsTab.tsx diff --git a/ui/src/shared/components/SubSections.tsx b/ui/src/shared/components/SubSections.tsx index e1c7692df4..f6fe764226 100644 --- a/ui/src/shared/components/SubSections.tsx +++ b/ui/src/shared/components/SubSections.tsx @@ -2,17 +2,12 @@ import React, {Component, ReactNode} from 'react' import uuid from 'uuid' import {withRouter, InjectedRouter} from 'react-router' +import SubSectionsTab from 'src/shared/components/SubSectionsTab' import {ErrorHandling} from 'src/shared/decorators/errors' - -interface Section { - url: string - name: string - component: ReactNode - enabled: boolean -} +import {PageSection} from 'src/types/shared' interface Props { - sections: Section[] + sections: PageSection[] activeSection: string sourceID: string router: InjectedRouter @@ -20,55 +15,51 @@ interface Props { } @ErrorHandling -@withRouter class SubSections extends Component { constructor(props) { super(props) } public render() { - const {sections} = this.props + const {sections, activeSection} = this.props return (
-
+
{sections.map( section => section.enabled && ( -
- {section.name} -
+ section={section} + handleClick={this.handleTabClick(section.url)} + activeSection={activeSection} + /> ) )}
-
- {this.activeSection} +
+ {this.activeSectionComponent}
) } - private get activeSection(): ReactNode { + private get activeSectionComponent(): ReactNode { const {sections, activeSection} = this.props const {component} = sections.find(section => section.url === activeSection) return component } - public getTabClass(sectionName: string) { - const {activeSection} = this.props - return `subsection--tab ${sectionName === activeSection ? 'active' : ''}` - } - public handleTabClick = url => () => { const {router, sourceID, parentUrl} = this.props router.push(`/sources/${sourceID}/${parentUrl}/${url}`) } } -export default SubSections +export default withRouter(SubSections) diff --git a/ui/src/shared/components/SubSectionsTab.tsx b/ui/src/shared/components/SubSectionsTab.tsx new file mode 100644 index 0000000000..46b6cba278 --- /dev/null +++ b/ui/src/shared/components/SubSectionsTab.tsx @@ -0,0 +1,25 @@ +import React, {SFC} from 'react' +import {PageSection} from 'src/types/shared' + +interface TabProps { + handleClick: () => void + section: PageSection + activeSection: string +} + +const SubSectionsTab: SFC = ({ + handleClick, + section, + activeSection, +}) => ( +
+ {section.name} +
+) + +export default SubSectionsTab diff --git a/ui/src/types/shared.ts b/ui/src/types/shared.ts index 2e2f6d6fb6..57370a09bf 100644 --- a/ui/src/types/shared.ts +++ b/ui/src/types/shared.ts @@ -1,3 +1,5 @@ +import {ReactNode} from 'react' + export type DropdownItem = | { text: string @@ -9,3 +11,10 @@ export interface DropdownAction { text: string handler: () => void } + +export interface PageSection { + url: string + name: string + component: ReactNode + enabled: boolean +} From 1136e750f49cb41673325a1896dd8d8362f33f1f Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 20 Apr 2018 20:34:41 -0700 Subject: [PATCH 6/7] Fix highlight of active tab --- ui/src/shared/components/SubSectionsTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/components/SubSectionsTab.tsx b/ui/src/shared/components/SubSectionsTab.tsx index 46b6cba278..399a46a41d 100644 --- a/ui/src/shared/components/SubSectionsTab.tsx +++ b/ui/src/shared/components/SubSectionsTab.tsx @@ -14,7 +14,7 @@ const SubSectionsTab: SFC = ({ }) => (
From e7a8fb305bae18b7a831045a66b982b56b595491 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 20 Apr 2018 20:34:55 -0700 Subject: [PATCH 7/7] Write render tests for component --- .../shared/components/SubSections.test.tsx | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 ui/test/shared/components/SubSections.test.tsx diff --git a/ui/test/shared/components/SubSections.test.tsx b/ui/test/shared/components/SubSections.test.tsx new file mode 100644 index 0000000000..229d4938c6 --- /dev/null +++ b/ui/test/shared/components/SubSections.test.tsx @@ -0,0 +1,86 @@ +import {shallow} from 'enzyme' +import React from 'react' +import SubSections from 'src/shared/components/SubSections' +import SubSectionsTab from 'src/shared/components/SubSectionsTab' + +const Guava = () => { + return
+} + +const Mango = () => { + return
+} + +const Pineapple = () => { + return
+} + +const guavaURL = 'guava' +const mangoURL = 'mango' +const pineappleURL = 'pineapple' + +const defaultProps = { + router: { + push: () => {}, + replace: () => {}, + go: () => {}, + goBack: () => {}, + goForward: () => {}, + setRouteLeaveHook: () => {}, + isActive: () => {}, + }, + sourceID: 'fruitstand', + parentUrl: 'fred-the-fruit-guy', + activeSection: guavaURL, + sections: [ + { + url: guavaURL, + name: 'Guava', + component: , + enabled: true, + }, + { + url: mangoURL, + name: 'Mango', + component: , + enabled: true, + }, + { + url: pineappleURL, + name: 'Pineapple', + component: , + enabled: false, + }, + ], +} + +const setup = (override?: {}) => { + const props = { + ...defaultProps, + ...override, + } + + return shallow() +} + +describe('SubSections', () => { + describe('render', () => { + it('renders the currently active tab', () => { + const wrapper = setup() + const content = wrapper.dive().find({'data-test': 'subsectionContent'}) + + expect(content.find(Guava).exists()).toBe(true) + }) + + it('only renders enabled tabs', () => { + const wrapper = setup() + const nav = wrapper.dive().find({'data-test': 'subsectionNav'}) + + const tabs = nav.find(SubSectionsTab) + + tabs.forEach(tab => { + expect(tab.exists()).toBe(tab.props().section.enabled) + }) + }) + }) +})