From 178d519f6e4b44aa86d96ba22dfadaf0e0d1248e Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Fri, 12 Apr 2019 10:44:24 -0400 Subject: [PATCH 1/7] Remove unwanted committed file --- .../Organization/Organization.test.jsx | 48 +++++++- src/app.scss | 19 ++++ .../screens/Organization/Organization.jsx | 106 +++++++++++++----- 3 files changed, 141 insertions(+), 32 deletions(-) diff --git a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx index ac147e2631..b57c95d6f4 100644 --- a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx +++ b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx @@ -1,20 +1,64 @@ import React from 'react'; import { mount } from 'enzyme'; +<<<<<<< HEAD +import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom'; +import { I18nProvider } from '@lingui/react'; import { I18nProvider } from '@lingui/react'; +import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization'; import Organization, { _Organization } from '../../../../../src/pages/Organizations/screens/Organization/Organization'; + +======= import { MemoryRouter } from 'react-router-dom'; import { I18nProvider } from '@lingui/react'; -import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization'; +import Organization, { _Organization } from '../../../../../src/pages/Organizations/screens/Organization/Organization'; +>>>>>>> addresses PR issues describe('', () => { test('initially renders succesfully', () => { + const spy = jest.spyOn(_Organization.prototype, 'checkLocation'); mount( - ); + expect(spy).toHaveBeenCalled(); + }); + + test('handleTabSelect renders the correct tab', async () => { + const currentTab = 'organizations/19/access'; + const spy = jest.spyOn(_Organization.prototype, 'handleTabSelect'); + const wrapper = mount( + + + + + + ).find('Organization'); + wrapper.find('button').at(2).simulate('click'); + setImmediate(async () => { + wrapper.setState({ activeTabKey: 1 }); + }); + wrapper.update(); + expect(spy).toBeCalled(); + expect(wrapper.state('activeTabKey')).toBe(1); + }); + + test('checkLocation renders proper state when new tab is selected', async () => { + const currentTab = 'organizations/19/access'; + const wrapper = mount( + + + + + + ).find('Organization'); + setImmediate(async () => { + wrapper.setState({ activeTabKey: 1 }); + }); + wrapper.find('button').at(3).simulate('click'); + expect(wrapper.state('activeTabKey')).toBe(2); }); }); diff --git a/src/app.scss b/src/app.scss index 20df8aa24b..2f83a57641 100644 --- a/src/app.scss +++ b/src/app.scss @@ -283,6 +283,25 @@ margin-bottom: 10px; } +.orgListAlert-actionBtn{ + margin:0 10px; +} + +.orgListDetete-progressBar{ + padding-right: 32px; +} + +.orgListDelete-progressBar-noShow{ + display: none; + padding-right: 32px; +} + +.OrgsTab-closeButton { + color: black; + float:right; + position: relative; + top: -60px; +} .awx-c-form-action-group { float: right; display: block; diff --git a/src/pages/Organizations/screens/Organization/Organization.jsx b/src/pages/Organizations/screens/Organization/Organization.jsx index 2ba53ece98..b4887548ab 100644 --- a/src/pages/Organizations/screens/Organization/Organization.jsx +++ b/src/pages/Organizations/screens/Organization/Organization.jsx @@ -5,20 +5,21 @@ import { Switch, Route, withRouter, - Redirect + Redirect, + Link } from 'react-router-dom'; import { Card, CardHeader, - PageSection + PageSection, + Tab, + Tabs } from '@patternfly/react-core'; - +import { + TimesIcon +} from '@patternfly/react-icons'; import { withNetwork } from '../../../../contexts/Network'; - -import Tabs from '../../../../components/Tabs/Tabs'; -import Tab from '../../../../components/Tabs/Tab'; import NotifyAndRedirect from '../../../../components/NotifyAndRedirect'; - import OrganizationAccess from './OrganizationAccess'; import OrganizationDetail from './OrganizationDetail'; import OrganizationEdit from './OrganizationEdit'; @@ -33,9 +34,17 @@ class Organization extends Component { organization: null, error: false, loading: true, + tabElements: [ + { name: i18nMark('Details'), link: `${props.match.url}/details`, id: 0 }, + { name: i18nMark('Access'), link: `${props.match.url}/access`, id: 1 }, + { name: i18nMark('Teams'), link: `${props.match.url}/teams`, id: 2 }, + { name: i18nMark('Notifications'), link: `${props.match.url}/notifications`, id: 3 }, + ], }; this.fetchOrganization = this.fetchOrganization.bind(this); + this.handleTabSelect = this.handleTabSelect.bind(this); + this.checkLocation = this.checkLocation.bind(this); } componentDidMount () { @@ -62,48 +71,80 @@ class Organization extends Component { setBreadcrumb(data); this.setState({ organization: data, loading: false }); } catch (error) { +<<<<<<< HEAD handleHttpError(error) || this.setState({ error: true, loading: false }); +======= + this.setState({ error: true }); + } finally { + this.setState({ loading: false }); + this.checkLocation(); +>>>>>>> addresses PR issues } } + checkLocation () { + const { location } = this.props; + const { tabElements } = this.state; + const activeTab = tabElements.filter(tabElement => tabElement.link === location.pathname); + this.setState({ activeTabKey: activeTab[0].id }); + } + + handleTabSelect (event, eventKey) { + const { history } = this.props; + const { tabElements } = this.state; + + const tab = tabElements.find(tabElement => tabElement.id === eventKey); + history.push(tab.link); + const activeTab = tabElements.filter(selectedTab => selectedTab.link === tab.link) + .map(selectedTab => selectedTab.id); + this.setState({ activeTabKey: activeTab[0] }); + } + render () { const { location, match, history } = this.props; - const { + activeTabKey, organization, error, - loading + loading, + tabElements } = this.state; - const tabElements = [ - { name: i18nMark('Details'), link: `${match.url}/details` }, - { name: i18nMark('Access'), link: `${match.url}/access` }, - { name: i18nMark('Teams'), link: `${match.url}/teams` }, - { name: i18nMark('Notifications'), link: `${match.url}/notifications` }, - ]; - let cardHeader = ( {({ i18n }) => ( - - {tabElements.map(tabElement => ( - - {tabElement.name} - - ))} - + <> + { + this.handleTabSelect(event, eventKey); + }} + > + {tabElements.map(tabElement => ( + + ))} + + + + + )} @@ -185,5 +226,10 @@ class Organization extends Component { ); } } +<<<<<<< HEAD export default withNetwork(withRouter(Organization)); +======= +export { Organization as _Organization }; +export default withRouter(Organization); +>>>>>>> addresses PR issues From 2daf202e52f1756db30959ef301402227001180e Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Mon, 15 Apr 2019 13:31:44 -0400 Subject: [PATCH 2/7] addresses PR issues --- .../screens/Organization/Organization.test.jsx | 6 ------ .../Organizations/screens/Organization/Organization.jsx | 8 -------- 2 files changed, 14 deletions(-) diff --git a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx index b57c95d6f4..d7746c5838 100644 --- a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx +++ b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx @@ -1,15 +1,9 @@ import React from 'react'; import { mount } from 'enzyme'; -<<<<<<< HEAD -import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom'; -import { I18nProvider } from '@lingui/react'; import { I18nProvider } from '@lingui/react'; -import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization'; import Organization, { _Organization } from '../../../../../src/pages/Organizations/screens/Organization/Organization'; -======= import { MemoryRouter } from 'react-router-dom'; import { I18nProvider } from '@lingui/react'; import Organization, { _Organization } from '../../../../../src/pages/Organizations/screens/Organization/Organization'; ->>>>>>> addresses PR issues describe('', () => { test('initially renders succesfully', () => { diff --git a/src/pages/Organizations/screens/Organization/Organization.jsx b/src/pages/Organizations/screens/Organization/Organization.jsx index b4887548ab..b3a92c77cb 100644 --- a/src/pages/Organizations/screens/Organization/Organization.jsx +++ b/src/pages/Organizations/screens/Organization/Organization.jsx @@ -71,14 +71,11 @@ class Organization extends Component { setBreadcrumb(data); this.setState({ organization: data, loading: false }); } catch (error) { -<<<<<<< HEAD handleHttpError(error) || this.setState({ error: true, loading: false }); -======= this.setState({ error: true }); } finally { this.setState({ loading: false }); this.checkLocation(); ->>>>>>> addresses PR issues } } @@ -226,10 +223,5 @@ class Organization extends Component { ); } } -<<<<<<< HEAD - export default withNetwork(withRouter(Organization)); -======= export { Organization as _Organization }; -export default withRouter(Organization); ->>>>>>> addresses PR issues From 76a7a76e81e5fd049771b319439032c591b2f40c Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Tue, 16 Apr 2019 15:03:22 -0400 Subject: [PATCH 3/7] refactoring and updating tests --- __tests__/components/RoutedTabs.test.jsx | 79 +++++++++++++++++++ .../Organization/Organization.test.jsx | 41 +--------- src/components/Tabs/RoutedTabs.jsx | 55 +++++++++++++ src/components/Tabs/Tab.jsx | 33 -------- src/components/Tabs/Tabs.jsx | 53 ------------- src/components/Tabs/tabs.scss | 71 ----------------- .../screens/Organization/Organization.jsx | 61 +++----------- 7 files changed, 147 insertions(+), 246 deletions(-) create mode 100644 __tests__/components/RoutedTabs.test.jsx create mode 100644 src/components/Tabs/RoutedTabs.jsx delete mode 100644 src/components/Tabs/Tab.jsx delete mode 100644 src/components/Tabs/Tabs.jsx delete mode 100644 src/components/Tabs/tabs.scss diff --git a/__tests__/components/RoutedTabs.test.jsx b/__tests__/components/RoutedTabs.test.jsx new file mode 100644 index 0000000000..05bc5aeee7 --- /dev/null +++ b/__tests__/components/RoutedTabs.test.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { mount } from 'enzyme'; + +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import { I18nProvider } from '@lingui/react'; +import RoutedTabs from '../../src/components/Tabs/RoutedTabs'; +import { DonateIcon } from '@patternfly/react-icons'; + +let wrapper; + +afterEach(() => { + jest.clearAllMocks(); +}); +const tabs = [ + { name: 'Details', link: 'organizations/19/details', id: 0 }, + { name: 'Access', link: 'organizations/19/access', id: 1 }, + { name: 'Teams', link: 'organizations/19/teams', id: 2 } +]; + +const history = createMemoryHistory({ + history: { + location: { + pathname: '/organizations/19/details' + } + } +}); + +describe('', () => { + test('RoutedTabs renders successfully', () => { + wrapper = mount( + + + + + + ).find('RoutedTabs'); + }); + + test('the correct tab is rendered', async () => { + const currentTab = 'organizations/1/details'; + wrapper = mount( + + + + + + ).find('RoutedTabs'); + wrapper.find('button').at(2).simulate('click'); + wrapper.update(); + expect(history.location.pathname).toEqual('/organizations/19/access'); + }); + + test('Given a URL the correct tab is displayed', async (done) => { + const currentTab = createMemoryHistory({ + initialEntries: ['/organizations/19/teams'], + }); + wrapper = mount( + + + + + + ).find('RoutedTabs'); + setImmediate(() => { + wrapper.find('Tabs').prop('onSelect')({}, 2); + const selectedTab = wrapper.find('li').get(2).props.className; + expect(selectedTab).toBe('pf-c-tabs__item pf-m-current'); + done(); + }); + }); +}); diff --git a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx index d7746c5838..a3b59a9e0c 100644 --- a/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx +++ b/__tests__/pages/Organizations/screens/Organization/Organization.test.jsx @@ -3,56 +3,19 @@ import { mount } from 'enzyme'; import { MemoryRouter } from 'react-router-dom'; import { I18nProvider } from '@lingui/react'; -import Organization, { _Organization } from '../../../../../src/pages/Organizations/screens/Organization/Organization'; +import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization'; describe('', () => { test('initially renders succesfully', () => { - const spy = jest.spyOn(_Organization.prototype, 'checkLocation'); mount( - <_Organization + ); - expect(spy).toHaveBeenCalled(); - }); - - test('handleTabSelect renders the correct tab', async () => { - const currentTab = 'organizations/19/access'; - const spy = jest.spyOn(_Organization.prototype, 'handleTabSelect'); - const wrapper = mount( - - - - - - ).find('Organization'); - wrapper.find('button').at(2).simulate('click'); - setImmediate(async () => { - wrapper.setState({ activeTabKey: 1 }); - }); - wrapper.update(); - expect(spy).toBeCalled(); - expect(wrapper.state('activeTabKey')).toBe(1); - }); - - test('checkLocation renders proper state when new tab is selected', async () => { - const currentTab = 'organizations/19/access'; - const wrapper = mount( - - - - - - ).find('Organization'); - setImmediate(async () => { - wrapper.setState({ activeTabKey: 1 }); - }); - wrapper.find('button').at(3).simulate('click'); - expect(wrapper.state('activeTabKey')).toBe(2); }); }); diff --git a/src/components/Tabs/RoutedTabs.jsx b/src/components/Tabs/RoutedTabs.jsx new file mode 100644 index 0000000000..820eace467 --- /dev/null +++ b/src/components/Tabs/RoutedTabs.jsx @@ -0,0 +1,55 @@ +import React from 'react'; + +import { + withRouter +} from 'react-router-dom'; + +import { + Tab, + Tabs +} from '@patternfly/react-core'; + +class RoutedTabs extends React.Component { + constructor (props) { + super(props); + + this.handleTabSelect = this.handleTabSelect.bind(this); + } + + getActiveTabId () { + const { history, tabsArray } = this.props; + const matchTab = tabsArray.find(selectedTab => selectedTab.link === history.location.pathname); + return matchTab ? matchTab.id : 0; + } + + handleTabSelect (event, eventKey) { + const { history, tabsArray } = this.props; + + const tab = tabsArray.find(tabElement => tabElement.id === eventKey); + history.push(tab.link); + } + + render () { + const { tabsArray } = this.props; + return ( + + {tabsArray.map(tabElement => ( + + ))} + + ); + } +} + +export { RoutedTabs as _RoutedTabs }; +export default withRouter(RoutedTabs); diff --git a/src/components/Tabs/Tab.jsx b/src/components/Tabs/Tab.jsx deleted file mode 100644 index 6dd9409391..0000000000 --- a/src/components/Tabs/Tab.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { NavLink } from 'react-router-dom'; -import './tabs.scss'; - -const Tab = ({ children, link, replace }) => ( -
  • - - {children} - -
  • -); - -Tab.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node - ]).isRequired, - link: PropTypes.string, - replace: PropTypes.bool, -}; - -Tab.defaultProps = { - link: null, - replace: false, -}; - -export default Tab; diff --git a/src/components/Tabs/Tabs.jsx b/src/components/Tabs/Tabs.jsx deleted file mode 100644 index abea04f8b8..0000000000 --- a/src/components/Tabs/Tabs.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router-dom'; -import { Button, Tooltip } from '@patternfly/react-core'; -import { TimesIcon } from '@patternfly/react-icons'; -import './tabs.scss'; - -const Tabs = ({ children, labelText, closeButton }) => ( -
    -
      - {children} -
    - {closeButton - && ( - - - - - - ) - } -
    -); - -Tabs.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node - ]).isRequired, - labelText: PropTypes.string, - closeButton: PropTypes.shape({ - text: PropTypes.string, - link: PropTypes.string, - }), -}; - -Tabs.defaultProps = { - labelText: null, - closeButton: null, -}; - -export default Tabs; diff --git a/src/components/Tabs/tabs.scss b/src/components/Tabs/tabs.scss deleted file mode 100644 index 651be6acba..0000000000 --- a/src/components/Tabs/tabs.scss +++ /dev/null @@ -1,71 +0,0 @@ -.pf-c-card__header { - --pf-c-card__header--PaddingBottom: 0; - --pf-c-card__header--PaddingX: 0; - --pf-c-card__header--PaddingRight: 0; - --pf-c-card__header--PaddingLeft: 0; - --pf-c-card__header--PaddingTop: 0; -} - -.pf-c-tabs { - --pf-global--link--Color: #484848; - --pf-global--link--Color--hover: #484848; - --pf-global--link--TextDecoration--hover: none; - - align-items: center; - flex-direction: row; - justify-content: space-between; - - &:before { - border-bottom: 1px solid var(--pf-c-tabs__item--BorderColor); - border-top: 1px solid var(--pf-c-tabs__item--BorderColor); - bottom: 0; - content: " "; - left: 0; - position: absolute; - right: 0; - top: 0; - } - - .pf-c-tabs__button { - --pf-c-tabs__button--PaddingLeft: 20px; - --pf-c-tabs__button--PaddingRight: 20px; - display: block; - font-weight: 700; - - &:after { - content: ''; - bottom: 0; - left: 0; - position: absolute; - right: 0; - top: 0; - } - } - - .pf-c-tabs__item:first-child .pf-c-tabs__button:before { - border-left: 0; - } - - .pf-c-tabs__item:not(.pf-m-current):hover - .pf-c-tabs__button::after { - border-top: none; - } - - .pf-c-tabs__item:hover - .pf-c-tabs__button:not(.pf-m-current)::after { - border-bottom: 3px solid var(--pf-global--Color--dark-200); - border-top: none; - } - - .pf-c-tabs__button.pf-m-current { - color: var(--pf-c-tabs__item--m-current--Color); - } - - .pf-c-tabs__button.pf-m-current::after { - content: ''; - border-bottom: 3px solid var(--pf-c-tabs__item--m-current--Color); - border-top: none; - margin-left: 1px; - } -} - diff --git a/src/pages/Organizations/screens/Organization/Organization.jsx b/src/pages/Organizations/screens/Organization/Organization.jsx index b3a92c77cb..1df548b796 100644 --- a/src/pages/Organizations/screens/Organization/Organization.jsx +++ b/src/pages/Organizations/screens/Organization/Organization.jsx @@ -12,8 +12,6 @@ import { Card, CardHeader, PageSection, - Tab, - Tabs } from '@patternfly/react-core'; import { TimesIcon @@ -25,6 +23,7 @@ import OrganizationDetail from './OrganizationDetail'; import OrganizationEdit from './OrganizationEdit'; import OrganizationNotifications from './OrganizationNotifications'; import OrganizationTeams from './OrganizationTeams'; +import RoutedTabs from '../../../../components/Tabs/RoutedTabs'; class Organization extends Component { constructor (props) { @@ -34,17 +33,9 @@ class Organization extends Component { organization: null, error: false, loading: true, - tabElements: [ - { name: i18nMark('Details'), link: `${props.match.url}/details`, id: 0 }, - { name: i18nMark('Access'), link: `${props.match.url}/access`, id: 1 }, - { name: i18nMark('Teams'), link: `${props.match.url}/teams`, id: 2 }, - { name: i18nMark('Notifications'), link: `${props.match.url}/notifications`, id: 3 }, - ], }; this.fetchOrganization = this.fetchOrganization.bind(this); - this.handleTabSelect = this.handleTabSelect.bind(this); - this.checkLocation = this.checkLocation.bind(this); } componentDidMount () { @@ -75,28 +66,9 @@ class Organization extends Component { this.setState({ error: true }); } finally { this.setState({ loading: false }); - this.checkLocation(); } } - checkLocation () { - const { location } = this.props; - const { tabElements } = this.state; - const activeTab = tabElements.filter(tabElement => tabElement.link === location.pathname); - this.setState({ activeTabKey: activeTab[0].id }); - } - - handleTabSelect (event, eventKey) { - const { history } = this.props; - const { tabElements } = this.state; - - const tab = tabElements.find(tabElement => tabElement.id === eventKey); - history.push(tab.link); - const activeTab = tabElements.filter(selectedTab => selectedTab.link === tab.link) - .map(selectedTab => selectedTab.id); - this.setState({ activeTabKey: activeTab[0] }); - } - render () { const { location, @@ -104,36 +76,25 @@ class Organization extends Component { history } = this.props; const { - activeTabKey, organization, error, loading, - tabElements } = this.state; let cardHeader = ( {({ i18n }) => ( - <> - + { - this.handleTabSelect(event, eventKey); - }} - > - {tabElements.map(tabElement => ( - - ))} - + tabsArray={[ + { name: i18nMark('Details'), link: `${match.url}/details`, id: 0 }, + { name: i18nMark('Access'), link: `${match.url}/access`, id: 1 }, + { name: i18nMark('Teams'), link: `${match.url}/teams`, id: 2 }, + { name: i18nMark('Notifications'), link: `${match.url}/notifications`, id: 3 }, + ]} + /> - + )} From ca6153c95502edcfa89159c756abb6a8bf766ebf Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Wed, 17 Apr 2019 13:47:23 -0400 Subject: [PATCH 4/7] RoutedTabs is now a functional component --- __tests__/components/RoutedTabs.test.jsx | 46 +++++------- src/app.scss | 13 ---- src/components/Tabs/RoutedTabs.jsx | 75 ++++++++----------- .../screens/Organization/Organization.jsx | 63 ++++++++-------- 4 files changed, 83 insertions(+), 114 deletions(-) diff --git a/__tests__/components/RoutedTabs.test.jsx b/__tests__/components/RoutedTabs.test.jsx index 05bc5aeee7..87053a30a9 100644 --- a/__tests__/components/RoutedTabs.test.jsx +++ b/__tests__/components/RoutedTabs.test.jsx @@ -5,27 +5,24 @@ import { Router } from 'react-router-dom'; import { createMemoryHistory } from 'history'; import { I18nProvider } from '@lingui/react'; import RoutedTabs from '../../src/components/Tabs/RoutedTabs'; -import { DonateIcon } from '@patternfly/react-icons'; let wrapper; -afterEach(() => { - jest.clearAllMocks(); +let history; + +beforeEach(() => { + history = createMemoryHistory({ + initialEntries: ['/organizations/19/details'], + }); }); + const tabs = [ { name: 'Details', link: 'organizations/19/details', id: 0 }, { name: 'Access', link: 'organizations/19/access', id: 1 }, - { name: 'Teams', link: 'organizations/19/teams', id: 2 } + { name: 'Teams', link: 'organizations/19/teams', id: 2 }, + { name: 'Notification', link: 'organizations/19/notification', id: 3 } ]; -const history = createMemoryHistory({ - history: { - location: { - pathname: '/organizations/19/details' - } - } -}); - describe('', () => { test('RoutedTabs renders successfully', () => { wrapper = mount( @@ -39,41 +36,34 @@ describe('', () => { ).find('RoutedTabs'); }); - test('the correct tab is rendered', async () => { - const currentTab = 'organizations/1/details'; + test('Given a URL the correct tab is displayed', async (done) => { wrapper = mount( ).find('RoutedTabs'); - wrapper.find('button').at(2).simulate('click'); - wrapper.update(); + wrapper.find('Tabs').prop('onSelect')({}, 1); expect(history.location.pathname).toEqual('/organizations/19/access'); + done(); }); - test('Given a URL the correct tab is displayed', async (done) => { - const currentTab = createMemoryHistory({ - initialEntries: ['/organizations/19/teams'], - }); + test('the correct tab is rendered', async () => { wrapper = mount( - + ).find('RoutedTabs'); - setImmediate(() => { - wrapper.find('Tabs').prop('onSelect')({}, 2); - const selectedTab = wrapper.find('li').get(2).props.className; - expect(selectedTab).toBe('pf-c-tabs__item pf-m-current'); - done(); - }); + const selectedTab = wrapper.find('section').get(2).props.hidden; + wrapper.find('button').at(2).simulate('click'); + expect(selectedTab).toBe(false); }); }); + diff --git a/src/app.scss b/src/app.scss index 2f83a57641..dfe664495c 100644 --- a/src/app.scss +++ b/src/app.scss @@ -283,19 +283,6 @@ margin-bottom: 10px; } -.orgListAlert-actionBtn{ - margin:0 10px; -} - -.orgListDetete-progressBar{ - padding-right: 32px; -} - -.orgListDelete-progressBar-noShow{ - display: none; - padding-right: 32px; -} - .OrgsTab-closeButton { color: black; float:right; diff --git a/src/components/Tabs/RoutedTabs.jsx b/src/components/Tabs/RoutedTabs.jsx index 820eace467..3293e64087 100644 --- a/src/components/Tabs/RoutedTabs.jsx +++ b/src/components/Tabs/RoutedTabs.jsx @@ -1,55 +1,44 @@ import React from 'react'; -import { - withRouter -} from 'react-router-dom'; - import { Tab, Tabs } from '@patternfly/react-core'; -class RoutedTabs extends React.Component { - constructor (props) { - super(props); +export default function RoutedTabs (props) { + const { history, tabsArray } = props; + const getActiveTabId = () => { + if (history && history.location.pathname) { + const matchTab = tabsArray.find(selectedTab => selectedTab.link + === history.location.pathname); + return matchTab.id; + } + return 0; + }; - this.handleTabSelect = this.handleTabSelect.bind(this); + function handleTabSelect (event, eventKey) { + if (history && history.location.pathname) { + const tab = tabsArray.find(tabElement => tabElement.id === eventKey); + history.push(tab.link); + } } - getActiveTabId () { - const { history, tabsArray } = this.props; - const matchTab = tabsArray.find(selectedTab => selectedTab.link === history.location.pathname); - return matchTab ? matchTab.id : 0; - } - - handleTabSelect (event, eventKey) { - const { history, tabsArray } = this.props; - - const tab = tabsArray.find(tabElement => tabElement.id === eventKey); - history.push(tab.link); - } - - render () { - const { tabsArray } = this.props; - return ( - - {tabsArray.map(tabElement => ( - - ))} - - ); - } + return ( + + {tabsArray.map(tabElement => ( + + ))} + + ); } -export { RoutedTabs as _RoutedTabs }; -export default withRouter(RoutedTabs); diff --git a/src/pages/Organizations/screens/Organization/Organization.jsx b/src/pages/Organizations/screens/Organization/Organization.jsx index 1df548b796..9a6b895451 100644 --- a/src/pages/Organizations/screens/Organization/Organization.jsx +++ b/src/pages/Organizations/screens/Organization/Organization.jsx @@ -63,9 +63,6 @@ class Organization extends Component { this.setState({ organization: data, loading: false }); } catch (error) { handleHttpError(error) || this.setState({ error: true, loading: false }); - this.setState({ error: true }); - } finally { - this.setState({ loading: false }); } } @@ -78,35 +75,41 @@ class Organization extends Component { const { organization, error, - loading, + loading } = this.state; - let cardHeader = ( - - - {({ i18n }) => ( - - - - - - - )} - - - ); + loading ? '' + : ( + + + {({ i18n }) => ( + + + + + + + )} + + + )); + if (!match) { + cardHeader = null; + } if (location.pathname.endsWith('edit')) { cardHeader = null; From 25db22e0720ae02e9c89e30cbec88209efa506ef Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Wed, 17 Apr 2019 15:45:42 -0400 Subject: [PATCH 5/7] fix RoutedTabs tests Signed-off-by: Alex Corey --- __tests__/components/RoutedTabs.test.jsx | 94 +++++++++++------------- src/components/Tabs/RoutedTabs.jsx | 52 ++++++++----- 2 files changed, 76 insertions(+), 70 deletions(-) diff --git a/__tests__/components/RoutedTabs.test.jsx b/__tests__/components/RoutedTabs.test.jsx index 87053a30a9..01788a77ba 100644 --- a/__tests__/components/RoutedTabs.test.jsx +++ b/__tests__/components/RoutedTabs.test.jsx @@ -1,69 +1,63 @@ import React from 'react'; -import { mount } from 'enzyme'; - +import { mount, shallow } from 'enzyme'; import { Router } from 'react-router-dom'; import { createMemoryHistory } from 'history'; import { I18nProvider } from '@lingui/react'; -import RoutedTabs from '../../src/components/Tabs/RoutedTabs'; +import RoutedTabs, { _RoutedTabs } from '../../src/components/Tabs/RoutedTabs'; let wrapper; - let history; -beforeEach(() => { - history = createMemoryHistory({ - initialEntries: ['/organizations/19/details'], - }); -}); - const tabs = [ - { name: 'Details', link: 'organizations/19/details', id: 0 }, - { name: 'Access', link: 'organizations/19/access', id: 1 }, - { name: 'Teams', link: 'organizations/19/teams', id: 2 }, - { name: 'Notification', link: 'organizations/19/notification', id: 3 } + { name: 'Details', link: '/organizations/19/details', id: 1 }, + { name: 'Access', link: '/organizations/19/access', id: 2 }, + { name: 'Teams', link: '/organizations/19/teams', id: 3 }, + { name: 'Notification', link: '/organizations/19/notification', id: 4 } ]; describe('', () => { + beforeEach(() => { + history = createMemoryHistory({ + initialEntries: ['/organizations/19/teams'], + }); + }); + test('RoutedTabs renders successfully', () => { - wrapper = mount( - - - - - - ).find('RoutedTabs'); + wrapper = shallow( + <_RoutedTabs + tabsArray={tabs} + history={history} + /> + ); + expect(wrapper.find('Tab')).toHaveLength(4); }); - test('Given a URL the correct tab is displayed', async (done) => { + test('Given a URL the correct tab is active', async () => { wrapper = mount( - - - - - - ).find('RoutedTabs'); - wrapper.find('Tabs').prop('onSelect')({}, 1); + + + + ); + + expect(history.location.pathname).toEqual('/organizations/19/teams'); + expect(wrapper.find('Tabs').prop('activeKey')).toBe(3); + }); + + test('should update history when new tab selected', async () => { + wrapper = mount( + + + + ); + + wrapper.find('Tabs').prop('onSelect')({}, 2); + wrapper.update(); + expect(history.location.pathname).toEqual('/organizations/19/access'); - done(); - }); - - test('the correct tab is rendered', async () => { - wrapper = mount( - - - - - - ).find('RoutedTabs'); - const selectedTab = wrapper.find('section').get(2).props.hidden; - wrapper.find('button').at(2).simulate('click'); - expect(selectedTab).toBe(false); + expect(wrapper.find('Tabs').prop('activeKey')).toBe(2); }); }); - diff --git a/src/components/Tabs/RoutedTabs.jsx b/src/components/Tabs/RoutedTabs.jsx index 3293e64087..4100e766b6 100644 --- a/src/components/Tabs/RoutedTabs.jsx +++ b/src/components/Tabs/RoutedTabs.jsx @@ -1,25 +1,23 @@ import React from 'react'; +import { shape, string, number, arrayOf } from 'prop-types'; +import { Tab, Tabs } from '@patternfly/react-core'; +import { withRouter } from 'react-router-dom'; -import { - Tab, - Tabs -} from '@patternfly/react-core'; - -export default function RoutedTabs (props) { +function RoutedTabs (props) { const { history, tabsArray } = props; + const getActiveTabId = () => { - if (history && history.location.pathname) { - const matchTab = tabsArray.find(selectedTab => selectedTab.link - === history.location.pathname); - return matchTab.id; + const match = tabsArray.find(tab => tab.link === history.location.pathname); + if (match) { + return match.id; } return 0; }; function handleTabSelect (event, eventKey) { - if (history && history.location.pathname) { - const tab = tabsArray.find(tabElement => tabElement.id === eventKey); - history.push(tab.link); + const match = tabsArray.find(tab => tab.id === eventKey); + if (match) { + history.push(match.link); } } @@ -28,17 +26,31 @@ export default function RoutedTabs (props) { activeKey={getActiveTabId()} onSelect={handleTabSelect} > - {tabsArray.map(tabElement => ( + {tabsArray.map(tab => ( ))} ); } +RoutedTabs.propTypes = { + history: shape({ + location: shape({ + pathname: string.isRequired + }).isRequired, + }).isRequired, + tabsArray: arrayOf(shape({ + id: number.isRequired, + link: string.isRequired, + name: string.isRequired, + })).isRequired, +}; +export { RoutedTabs as _RoutedTabs }; +export default withRouter(RoutedTabs); From 2067718c0e314a7999159989bffa6ccc9514af70 Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Wed, 17 Apr 2019 16:28:31 -0400 Subject: [PATCH 6/7] fixed tests --- __tests__/components/RoutedTabs.test.jsx | 1 - package-lock.json | 57 ++++++++++++++++-------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/__tests__/components/RoutedTabs.test.jsx b/__tests__/components/RoutedTabs.test.jsx index 01788a77ba..9fcd896ef9 100644 --- a/__tests__/components/RoutedTabs.test.jsx +++ b/__tests__/components/RoutedTabs.test.jsx @@ -2,7 +2,6 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { Router } from 'react-router-dom'; import { createMemoryHistory } from 'history'; -import { I18nProvider } from '@lingui/react'; import RoutedTabs, { _RoutedTabs } from '../../src/components/Tabs/RoutedTabs'; let wrapper; diff --git a/package-lock.json b/package-lock.json index 47dafb051b..c58f5d78e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2170,7 +2170,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "requires": { "ansi-wrap": "^0.1.0" @@ -3174,12 +3174,12 @@ }, "babel-plugin-syntax-class-properties": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=" }, "babel-plugin-syntax-flow": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=" }, "babel-plugin-syntax-jsx": { @@ -5370,7 +5370,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -6537,7 +6537,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -6924,7 +6924,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6945,12 +6946,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6965,17 +6968,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -7092,7 +7098,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -7104,6 +7111,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7118,6 +7126,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7125,12 +7134,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7149,6 +7160,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -7229,7 +7241,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -7241,6 +7254,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -7326,7 +7340,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -7362,6 +7377,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7381,6 +7397,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7424,12 +7441,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -10500,7 +10519,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=" }, "kleur": { @@ -10935,7 +10954,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -13673,7 +13692,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } From 8fa9535b98cf7fc8f77a541bae482d52424523b9 Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Thu, 18 Apr 2019 13:07:10 -0400 Subject: [PATCH 7/7] styling changes --- src/app.scss | 6 ++++-- .../screens/Organization/Organization.jsx | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/app.scss b/src/app.scss index dfe664495c..7f05497dc5 100644 --- a/src/app.scss +++ b/src/app.scss @@ -285,9 +285,11 @@ .OrgsTab-closeButton { color: black; - float:right; + float: right; position: relative; - top: -60px; + top: -25px; + margin: 0 10px; + right: 10px; } .awx-c-form-action-group { float: right; diff --git a/src/pages/Organizations/screens/Organization/Organization.jsx b/src/pages/Organizations/screens/Organization/Organization.jsx index 9a6b895451..aba494809e 100644 --- a/src/pages/Organizations/screens/Organization/Organization.jsx +++ b/src/pages/Organizations/screens/Organization/Organization.jsx @@ -72,15 +72,23 @@ class Organization extends Component { match, history } = this.props; + const { organization, error, loading } = this.state; + + const tabsPaddingOverride = { + padding: '0' + }; + let cardHeader = ( loading ? '' : ( - + {({ i18n }) => (