Adds AddDropDownButton removes TemplateDropDown Button

Both Inventory List and Template List use the same add button that has a drop down.
I decided to make a component that both can use.
This also addresses a typo in a InventoryList test.
This commit is contained in:
Alex Corey
2019-11-08 13:14:37 -05:00
parent 1564dfc80f
commit 052f101a70
6 changed files with 55 additions and 89 deletions

View File

@@ -1,11 +1,10 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { Link, withRouter } from 'react-router-dom'; import { Link, withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Dropdown, DropdownPosition } from '@patternfly/react-core'; import { Dropdown, DropdownPosition } from '@patternfly/react-core';
import { ToolbarAddButton } from '@components/PaginatedDataList'; import { ToolbarAddButton } from '@components/PaginatedDataList';
function TemplateAddButton({ match, i18n }) { function AddDropDownButton({ topUrl, bottomUrl, topLabel, bottomLabel }) {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const element = useRef(null); const element = useRef(null);
@@ -30,19 +29,15 @@ function TemplateAddButton({ match, i18n }) {
position={DropdownPosition.right} position={DropdownPosition.right}
toggle={<ToolbarAddButton onClick={() => setIsOpen(!isOpen)} />} toggle={<ToolbarAddButton onClick={() => setIsOpen(!isOpen)} />}
dropdownItems={[ dropdownItems={[
<Link <Link key="top" className="pf-c-dropdown__menu-item" to={`${topUrl}`}>
key="job" {`${topLabel}`}
className="pf-c-dropdown__menu-item"
to={`${match.url}/job_template/add/`}
>
{i18n._(t`Job Template`)}
</Link>, </Link>,
<Link <Link
key="workflow" key="bottom"
className="pf-c-dropdown__menu-item" className="pf-c-dropdown__menu-item"
to={`${match.url}_workflow/add/`} to={`${bottomUrl}`}
> >
{i18n._(t`Workflow Template`)} {`${bottomLabel}`}
</Link>, </Link>,
]} ]}
/> />
@@ -50,5 +45,5 @@ function TemplateAddButton({ match, i18n }) {
); );
} }
export { TemplateAddButton as _TemplateAddButton }; export { AddDropDownButton as _AddDropDownButton };
export default withI18n()(withRouter(TemplateAddButton)); export default withI18n()(withRouter(AddDropDownButton));

View File

@@ -1,22 +1,22 @@
import React from 'react'; import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { mountWithContexts } from '@testUtils/enzymeHelpers';
import TemplateAddButton from './TemplateAddButton'; import AddDropDownButton from './AddDropDownButton';
describe('<TemplateAddButton />', () => { describe('<AddDropDownButton />', () => {
test('should be closed initially', () => { test('should be closed initially', () => {
const wrapper = mountWithContexts(<TemplateAddButton />); const wrapper = mountWithContexts(<AddDropDownButton />);
expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(false); expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(false);
}); });
test('should render two links', () => { test('should render two links', () => {
const wrapper = mountWithContexts(<TemplateAddButton />); const wrapper = mountWithContexts(<AddDropDownButton />);
wrapper.find('button').simulate('click'); wrapper.find('button').simulate('click');
expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true); expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true);
expect(wrapper.find('Link')).toHaveLength(2); expect(wrapper.find('Link')).toHaveLength(2);
}); });
test('should close when button re-clicked', () => { test('should close when button re-clicked', () => {
const wrapper = mountWithContexts(<TemplateAddButton />); const wrapper = mountWithContexts(<AddDropDownButton />);
wrapper.find('button').simulate('click'); wrapper.find('button').simulate('click');
expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true); expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true);
wrapper.find('button').simulate('click'); wrapper.find('button').simulate('click');

View File

@@ -0,0 +1,4 @@
export {
default
}
from './AddDropDownButton'

View File

@@ -1,15 +1,9 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { withRouter, Link } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import { Card, PageSection } from '@patternfly/react-core';
Card,
PageSection,
Dropdown,
DropdownItem,
DropdownPosition,
} from '@patternfly/react-core';
import { InventoriesAPI } from '@api'; import { InventoriesAPI } from '@api';
import AlertModal from '@components/AlertModal'; import AlertModal from '@components/AlertModal';
@@ -17,10 +11,10 @@ import DatalistToolbar from '@components/DataListToolbar';
import ErrorDetail from '@components/ErrorDetail'; import ErrorDetail from '@components/ErrorDetail';
import PaginatedDataList, { import PaginatedDataList, {
ToolbarDeleteButton, ToolbarDeleteButton,
ToolbarAddButton,
} from '@components/PaginatedDataList'; } from '@components/PaginatedDataList';
import { getQSConfig, parseQueryString } from '@util/qs';
import { getQSConfig, parseQueryString } from '@util/qs';
import AddDropDownButton from '@components/AddDropDownButton';
import InventoryListItem from './InventoryListItem'; import InventoryListItem from './InventoryListItem';
// The type value in const QS_CONFIG below does not have a space between job_inventory and // The type value in const QS_CONFIG below does not have a space between job_inventory and
@@ -65,10 +59,6 @@ class InventoriesList extends Component {
} }
} }
componentWillUnmount() {
document.removeEventListener('click', this.handleAddToggle, false);
}
handleDeleteErrorClose() { handleDeleteErrorClose() {
this.setState({ deletionError: null }); this.setState({ deletionError: null });
} }
@@ -90,16 +80,13 @@ class InventoriesList extends Component {
handleAddToggle(e) { handleAddToggle(e) {
const { isAddOpen } = this.state; const { isAddOpen } = this.state;
document.addEventListener('click', this.handleAddToggle, false);
if (this.node && this.node.contains(e.target) && isAddOpen) { if (this.node && this.node.contains(e.target) && isAddOpen) {
document.removeEventListener('click', this.handleAddToggle, false);
this.setState({ isAddOpen: false }); this.setState({ isAddOpen: false });
} else if (this.node && this.node.contains(e.target) && !isAddOpen) { } else if (this.node && this.node.contains(e.target) && !isAddOpen) {
this.setState({ isAddOpen: true }); this.setState({ isAddOpen: true });
} else { } else {
this.setState({ isAddOpen: false }); this.setState({ isAddOpen: false });
document.removeEventListener('click', this.handleAddToggle, false);
} }
} }
@@ -222,33 +209,13 @@ class InventoriesList extends Component {
pluralizedItemName="Inventories" pluralizedItemName="Inventories"
/>, />,
canAdd && ( canAdd && (
<div <AddDropDownButton
ref={node => {
this.node = node;
}}
key="add" key="add"
> topUrl={`${match.url}/inventory/add/`}
<Dropdown bottomdUrl={`${match.url}/smart_inventory/add/`}
isPlain topLabel={i18n._(t`Inventory`)}
isOpen={isAddOpen} bottomLabel={i18n._(t`Smart Inventory`)}
position={DropdownPosition.right} />
toggle={
<ToolbarAddButton onClick={this.handleAddToggle} />
}
dropdownItems={[
<DropdownItem key="inventory">
<Link to={`${match.url}/inventory/add/`}>
{i18n._(t`Inventory`)}
</Link>
</DropdownItem>,
<DropdownItem key="smart_inventory">
<Link to={`${match.url}/smart_inventory/add/`}>
{i18n._(t`Smart Inventory`)}
</Link>
</DropdownItem>,
]}
/>
</div>
), ),
]} ]}
/> />
@@ -269,31 +236,13 @@ class InventoriesList extends Component {
)} )}
emptyStateControls={ emptyStateControls={
canAdd && ( canAdd && (
<div <AddDropDownButton
ref={node => {
this.node = node;
}}
key="add" key="add"
> topUrl={`${match.url}/inventory/add/`}
<Dropdown bottomUrl={`${match.url}/smart_inventory/add/`}
isPlain topLabel={i18n._(t`Inventory`)}
isOpen={isAddOpen} bottomLabel={i18n._(t`Smart Inventory`)}
position={DropdownPosition.right} />
toggle={<ToolbarAddButton onClick={this.handleAddToggle} />}
dropdownItems={[
<DropdownItem key="inventory">
<Link to={`${match.url}/inventory/add/`}>
{i18n._(t`Inventory`)}
</Link>
</DropdownItem>,
<DropdownItem key="smart_inventory">
<Link to={`${match.url}/smart_inventory/add/`}>
{i18n._(t`Smart Inventory`)}
</Link>
</DropdownItem>,
]}
/>
</div>
) )
} }
/> />

View File

@@ -284,7 +284,7 @@ describe('<InventoriesList />', () => {
done(); done();
}); });
test('Add button shown for users without ability to POST', async done => { test('Add button shown for users with ability to POST', async done => {
const wrapper = mountWithContexts(<InventoriesList />); const wrapper = mountWithContexts(<InventoriesList />);
await waitForElement( await waitForElement(
wrapper, wrapper,

View File

@@ -13,8 +13,8 @@ import PaginatedDataList, {
} from '@components/PaginatedDataList'; } from '@components/PaginatedDataList';
import { getQSConfig, parseQueryString } from '@util/qs'; import { getQSConfig, parseQueryString } from '@util/qs';
import AddDropDownButton from '@components/AddDropDownButton';
import TemplateListItem from './TemplateListItem'; import TemplateListItem from './TemplateListItem';
import TemplateAddButton from './TemplateAddButton';
// The type value in const QS_CONFIG below does not have a space between job_template and // The type value in const QS_CONFIG below does not have a space between job_template and
// workflow_job_template so the params sent to the API match what the api expects. // workflow_job_template so the params sent to the API match what the api expects.
@@ -206,7 +206,15 @@ class TemplatesList extends Component {
itemsToDelete={selected} itemsToDelete={selected}
pluralizedItemName="Templates" pluralizedItemName="Templates"
/>, />,
canAdd && <TemplateAddButton key="add" />, canAdd && (
<AddDropDownButton
key="add"
topUrl={`${match.url}/job_template/add/`}
bottomUrl={`${match.url}_workflow/add/`}
topLabel={i18n._(t`Job Template`)}
bottomLabel={i18n._(t`Workflow Template`)}
/>
),
]} ]}
/> />
)} )}
@@ -220,7 +228,17 @@ class TemplatesList extends Component {
isSelected={selected.some(row => row.id === template.id)} isSelected={selected.some(row => row.id === template.id)}
/> />
)} )}
emptyStateControls={canAdd && <TemplateAddButton />} emptyStateControls={
canAdd && (
<AddDropDownButton
key="add"
topUrl={`${match.url}/job_template/add/`}
bottomUrl={`${match.url}_workflow/add/`}
topLabel={i18n._(t`Job Template`)}
bottomLabel={i18n._(t`Workflow Template`)}
/>
)
}
/> />
</Card> </Card>
<AlertModal <AlertModal