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

View File

@ -1,22 +1,22 @@
import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import TemplateAddButton from './TemplateAddButton';
import AddDropDownButton from './AddDropDownButton';
describe('<TemplateAddButton />', () => {
describe('<AddDropDownButton />', () => {
test('should be closed initially', () => {
const wrapper = mountWithContexts(<TemplateAddButton />);
const wrapper = mountWithContexts(<AddDropDownButton />);
expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(false);
});
test('should render two links', () => {
const wrapper = mountWithContexts(<TemplateAddButton />);
const wrapper = mountWithContexts(<AddDropDownButton />);
wrapper.find('button').simulate('click');
expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true);
expect(wrapper.find('Link')).toHaveLength(2);
});
test('should close when button re-clicked', () => {
const wrapper = mountWithContexts(<TemplateAddButton />);
const wrapper = mountWithContexts(<AddDropDownButton />);
wrapper.find('button').simulate('click');
expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true);
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 { withRouter, Link } from 'react-router-dom';
import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Card,
PageSection,
Dropdown,
DropdownItem,
DropdownPosition,
} from '@patternfly/react-core';
import { Card, PageSection } from '@patternfly/react-core';
import { InventoriesAPI } from '@api';
import AlertModal from '@components/AlertModal';
@ -17,10 +11,10 @@ import DatalistToolbar from '@components/DataListToolbar';
import ErrorDetail from '@components/ErrorDetail';
import PaginatedDataList, {
ToolbarDeleteButton,
ToolbarAddButton,
} from '@components/PaginatedDataList';
import { getQSConfig, parseQueryString } from '@util/qs';
import { getQSConfig, parseQueryString } from '@util/qs';
import AddDropDownButton from '@components/AddDropDownButton';
import InventoryListItem from './InventoryListItem';
// 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() {
this.setState({ deletionError: null });
}
@ -90,16 +80,13 @@ class InventoriesList extends Component {
handleAddToggle(e) {
const { isAddOpen } = this.state;
document.addEventListener('click', this.handleAddToggle, false);
if (this.node && this.node.contains(e.target) && isAddOpen) {
document.removeEventListener('click', this.handleAddToggle, false);
this.setState({ isAddOpen: false });
} else if (this.node && this.node.contains(e.target) && !isAddOpen) {
this.setState({ isAddOpen: true });
} else {
this.setState({ isAddOpen: false });
document.removeEventListener('click', this.handleAddToggle, false);
}
}
@ -222,33 +209,13 @@ class InventoriesList extends Component {
pluralizedItemName="Inventories"
/>,
canAdd && (
<div
ref={node => {
this.node = node;
}}
<AddDropDownButton
key="add"
>
<Dropdown
isPlain
isOpen={isAddOpen}
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>
topUrl={`${match.url}/inventory/add/`}
bottomdUrl={`${match.url}/smart_inventory/add/`}
topLabel={i18n._(t`Inventory`)}
bottomLabel={i18n._(t`Smart Inventory`)}
/>
),
]}
/>
@ -269,31 +236,13 @@ class InventoriesList extends Component {
)}
emptyStateControls={
canAdd && (
<div
ref={node => {
this.node = node;
}}
<AddDropDownButton
key="add"
>
<Dropdown
isPlain
isOpen={isAddOpen}
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>
topUrl={`${match.url}/inventory/add/`}
bottomUrl={`${match.url}/smart_inventory/add/`}
topLabel={i18n._(t`Inventory`)}
bottomLabel={i18n._(t`Smart Inventory`)}
/>
)
}
/>

View File

@ -284,7 +284,7 @@ describe('<InventoriesList />', () => {
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 />);
await waitForElement(
wrapper,

View File

@ -13,8 +13,8 @@ import PaginatedDataList, {
} from '@components/PaginatedDataList';
import { getQSConfig, parseQueryString } from '@util/qs';
import AddDropDownButton from '@components/AddDropDownButton';
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
// 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}
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)}
/>
)}
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>
<AlertModal