Merges 2 tool bar add buttons that use dropdowns

This commit is contained in:
Alex Corey 2020-10-26 09:46:25 -04:00
parent 51600986c9
commit a2ca2729ba
14 changed files with 236 additions and 296 deletions

View File

@ -77,21 +77,28 @@ class WorkflowJobTemplates extends SchedulesMixin(NotificationsMixin(Base)) {
readNotificationTemplatesApprovals(id, params) {
return this.http.get(
`${this.baseUrl}${id}/notification_templates_approvals/`,
{ params }
{
params,
}
);
}
associateNotificationTemplatesApprovals(resourceId, notificationId) {
return this.http.post(
`${this.baseUrl}${resourceId}/notification_templates_approvals/`,
{ id: notificationId }
{
id: notificationId,
}
);
}
disassociateNotificationTemplatesApprovals(resourceId, notificationId) {
return this.http.post(
`${this.baseUrl}${resourceId}/notification_templates_approvals/`,
{ id: notificationId, disassociate: true }
{
id: notificationId,
disassociate: true,
}
);
}
}

View File

@ -1,15 +1,9 @@
import React, { useState, useRef, useEffect, Fragment } from 'react';
import { Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import PropTypes from 'prop-types';
import {
Dropdown,
DropdownPosition,
DropdownItem,
} from '@patternfly/react-core';
import { Dropdown, DropdownPosition } from '@patternfly/react-core';
import { ToolbarAddButton } from '../PaginatedDataList';
import { toTitleCase } from '../../util/strings';
import { useKebabifiedMenu } from '../../contexts/Kebabified';
function AddDropDownButton({ dropdownItems, i18n }) {
@ -31,15 +25,7 @@ function AddDropDownButton({ dropdownItems, i18n }) {
}, [isKebabified]);
if (isKebabified) {
return (
<Fragment>
{dropdownItems.map(item => (
<DropdownItem key={item.url} component={Link} to={item.url}>
{toTitleCase(`${i18n._(t`Add`)} ${item.label}`)}
</DropdownItem>
))}
</Fragment>
);
return <Fragment>{dropdownItems}</Fragment>;
}
return (
@ -50,31 +36,19 @@ function AddDropDownButton({ dropdownItems, i18n }) {
position={DropdownPosition.right}
toggle={
<ToolbarAddButton
aria-label={i18n._(t`Add`)}
showToggleIndicator
onClick={() => setIsOpen(!isOpen)}
/>
}
dropdownItems={dropdownItems.map(item => (
<Link
className="pf-c-dropdown__menu-item"
key={item.url}
to={item.url}
>
{item.label}
</Link>
))}
dropdownItems={dropdownItems}
/>
</div>
);
}
AddDropDownButton.propTypes = {
dropdownItems: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
})
).isRequired,
dropdownItems: PropTypes.arrayOf(PropTypes.element.isRequired).isRequired,
};
export { AddDropDownButton as _AddDropDownButton };

View File

@ -1,14 +1,12 @@
import React from 'react';
import { DropdownItem } from '@patternfly/react-core';
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
import AddDropDownButton from './AddDropDownButton';
describe('<AddDropDownButton />', () => {
const dropdownItems = [
{
key: 'inventory',
label: 'Inventory',
url: `inventory/inventory/add/`,
},
<DropdownItem key="add">Add</DropdownItem>,
<DropdownItem key="route">Route</DropdownItem>,
];
test('should be closed initially', () => {
const wrapper = mountWithContexts(
@ -23,7 +21,7 @@ describe('<AddDropDownButton />', () => {
);
wrapper.find('button').simulate('click');
expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true);
expect(wrapper.find('Link')).toHaveLength(dropdownItems.length);
expect(wrapper.find('DropdownItem')).toHaveLength(dropdownItems.length);
});
test('should close when button re-clicked', () => {

View File

@ -1,6 +1,8 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
import DataListToolbar from './DataListToolbar';
import AddDropDownButton from '../AddDropDownButton/AddDropDownButton';
describe('<DataListToolbar />', () => {
let toolbar;
@ -313,4 +315,44 @@ describe('<DataListToolbar />', () => {
search.prop('columns').filter(col => col.key === 'advanced').length
).toBe(1);
});
test('should properly render toolbar buttons when in advanced search mode', async () => {
const searchColumns = [{ name: 'Name', key: 'name', isDefault: true }];
const sortColumns = [{ name: 'Name', key: 'name' }];
const newToolbar = mountWithContexts(
<DataListToolbar
qsConfig={QS_CONFIG}
searchColumns={searchColumns}
sortColumns={sortColumns}
onSearch={onSearch}
onReplaceSearch={onReplaceSearch}
onSort={onSort}
onSelectAll={onSelectAll}
additionalControls={[
<AddDropDownButton
dropdownItems={[
<div key="add container" aria-label="add container">
Add Contaner
</div>,
<div key="add instance group" aria-label="add instance group">
Add Instance Group
</div>,
]}
/>,
]}
/>
);
await act(() =>
newToolbar.find('Search').prop('onShowAdvancedSearch')(true)
);
newToolbar.update();
expect(newToolbar.find('KebabToggle').length).toBe(1);
await act(() => newToolbar.find('KebabToggle').prop('onToggle')(true));
newToolbar.update();
expect(newToolbar.find('div[aria-label="add container"]').length).toBe(1);
expect(newToolbar.find('div[aria-label="add instance group"]').length).toBe(
1
);
});
});

View File

@ -1,14 +1,15 @@
import React, { Fragment, useEffect, useState, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { useLocation, Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Card } from '@patternfly/react-core';
import { Card, DropdownItem } from '@patternfly/react-core';
import {
JobTemplatesAPI,
UnifiedJobTemplatesAPI,
WorkflowJobTemplatesAPI,
} from '../../../api';
import { toTitleCase } from '../../../util/strings';
import AlertModal from '../../../components/AlertModal';
import DatalistToolbar from '../../../components/DataListToolbar';
import ErrorDetail from '../../../components/ErrorDetail';
@ -140,24 +141,31 @@ function DashboardTemplateList({ i18n }) {
jtActions && Object.prototype.hasOwnProperty.call(jtActions, 'POST');
const canAddWFJT =
wfjtActions && Object.prototype.hasOwnProperty.call(wfjtActions, 'POST');
const addButtonOptions = [];
if (canAddJT) {
addButtonOptions.push({
label: i18n._(t`Job Template`),
url: `/templates/job_template/add/`,
});
}
if (canAddWFJT) {
addButtonOptions.push({
label: i18n._(t`Workflow Template`),
url: `/templates/workflow_job_template/add/`,
});
}
const addTempate = toTitleCase(i18n._(t`Add Job Template`));
const addWFTemplate = toTitleCase(i18n._(t`Add Workflow Template`));
const addButton = (
<AddDropDownButton key="add" dropdownItems={addButtonOptions} />
<AddDropDownButton
key="add"
dropdownItems={[
<DropdownItem
key={addTempate}
component={Link}
to="/templates/job_template/add/"
aria-label={addTempate}
>
{addTempate}
</DropdownItem>,
<DropdownItem
component={Link}
to="/templates/workflow_job_template/add/"
key={addWFTemplate}
aria-label={addWFTemplate}
>
{addWFTemplate}
</DropdownItem>,
]}
/>
);
return (

View File

@ -1,13 +1,14 @@
import React, { useEffect, useCallback } from 'react';
import { useLocation, useRouteMatch } from 'react-router-dom';
import { useLocation, useRouteMatch, Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Card, PageSection } from '@patternfly/react-core';
import { Card, PageSection, DropdownItem } from '@patternfly/react-core';
import { InstanceGroupsAPI } from '../../../api';
import { getQSConfig, parseQueryString } from '../../../util/qs';
import useRequest, { useDeleteItems } from '../../../util/useRequest';
import useSelected from '../../../util/useSelected';
import { toTitleCase } from '../../../util/strings';
import PaginatedDataList, {
ToolbarDeleteButton,
} from '../../../components/PaginatedDataList';
@ -152,19 +153,31 @@ function InstanceGroupList({ i18n }) {
);
}
const addButtonOptions = [
{
label: i18n._(t`Instance group`),
url: '/instance_groups/add',
},
{
label: i18n._(t`Container group`),
url: '/instance_groups/container_group/add',
},
];
const addContainerGroup = toTitleCase(i18n._(t`Add Container Group`));
const addInstanceGroup = toTitleCase(i18n._(t`Add Instance Group`));
const addButton = (
<AddDropDownButton key="add" dropdownItems={addButtonOptions} />
<AddDropDownButton
key="add"
dropdownItems={[
<DropdownItem
to="/instance_groups/container_group/add"
component={Link}
key={addContainerGroup}
aria-label={addContainerGroup}
>
{addContainerGroup}
</DropdownItem>,
<DropdownItem
to="/instance_groups/add"
component={Link}
key={addInstanceGroup}
aria-label={addInstanceGroup}
>
{addInstanceGroup}
</DropdownItem>,
]}
/>
);
const getDetailUrl = item => {

View File

@ -1,7 +1,8 @@
import React, { useEffect, useCallback, useState } from 'react';
import { useHistory, useLocation, useParams } from 'react-router-dom';
import { useLocation, useParams, Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { DropdownItem } from '@patternfly/react-core';
import { getQSConfig, mergeParams, parseQueryString } from '../../../util/qs';
import { GroupsAPI, InventoriesAPI } from '../../../api';
@ -18,7 +19,8 @@ import AssociateModal from '../../../components/AssociateModal';
import DisassociateButton from '../../../components/DisassociateButton';
import AdHocCommands from '../../../components/AdHocCommands/AdHocCommands';
import InventoryGroupHostListItem from './InventoryGroupHostListItem';
import AddDropdown from '../shared/AddDropdown';
import AddDropDownButton from '../../../components/AddDropDownButton';
import { toTitleCase } from '../../../util/strings';
const QS_CONFIG = getQSConfig('host', {
page: 1,
@ -30,7 +32,6 @@ function InventoryGroupHostList({ i18n }) {
const [isModalOpen, setIsModalOpen] = useState(false);
const { id: inventoryId, groupId } = useParams();
const location = useLocation();
const history = useHistory();
const {
result: {
@ -143,26 +144,31 @@ function InventoryGroupHostList({ i18n }) {
const canAdd =
actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
const addFormUrl = `/inventories/inventory/${inventoryId}/groups/${groupId}/nested_hosts/add`;
const addButtonOptions = [];
const addExistingHost = toTitleCase(i18n._(t`Add Existing Host`));
const addNewHost = toTitleCase(i18n._(t`Add New Host`));
if (canAdd) {
addButtonOptions.push(
{
onAdd: () => setIsModalOpen(true),
title: i18n._(t`Add existing host`),
label: i18n._(t`host`),
key: 'existing',
},
{
onAdd: () => history.push(addFormUrl),
title: i18n._(t`Add new host`),
label: i18n._(t`host`),
key: 'new',
}
);
}
const addButton = <AddDropdown key="add" dropdownItems={addButtonOptions} />;
const addButton = (
<AddDropDownButton
key="add"
dropdownItems={[
<DropdownItem
onClick={() => setIsModalOpen(true)}
key={addExistingHost}
aria-label={addExistingHost}
>
{addExistingHost}
</DropdownItem>,
<DropdownItem
component={Link}
to={`${addFormUrl}`}
key={addNewHost}
aria-label={addNewHost}
>
{addNewHost}
</DropdownItem>,
]}
/>
);
return (
<>
<PaginatedDataList

View File

@ -97,7 +97,7 @@ describe('<InventoryGroupHostList />', () => {
});
test('should show add dropdown button according to permissions', async () => {
expect(wrapper.find('AddDropdown').length).toBe(1);
expect(wrapper.find('AddDropDownButton').length).toBe(1);
InventoriesAPI.readHostsOptions.mockResolvedValueOnce({
data: {
actions: {
@ -109,7 +109,7 @@ describe('<InventoryGroupHostList />', () => {
wrapper = mountWithContexts(<InventoryGroupHostList />);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(wrapper.find('AddDropdown').length).toBe(0);
expect(wrapper.find('AddDropDownButton').length).toBe(0);
});
test('expected api calls are made for multi-delete', async () => {
@ -156,9 +156,10 @@ describe('<InventoryGroupHostList />', () => {
test('should show associate host modal when adding an existing host', () => {
const dropdownToggle = wrapper.find(
'DropdownToggle button[aria-label="add"]'
'ToolbarAddButton button[aria-label="Add"]'
);
dropdownToggle.simulate('click');
wrapper
.find('DropdownItem[aria-label="Add existing host"]')
.simulate('click');
@ -175,7 +176,7 @@ describe('<InventoryGroupHostList />', () => {
results: [{ id: 123, name: 'foo', url: '/api/v2/hosts/123/' }],
},
});
wrapper.find('DropdownToggle button[aria-label="add"]').simulate('click');
wrapper.find('ToolbarAddButton button[aria-label="Add"]').simulate('click');
await act(async () => {
wrapper
.find('DropdownItem[aria-label="Add existing host"]')
@ -205,7 +206,7 @@ describe('<InventoryGroupHostList />', () => {
results: [{ id: 123, name: 'foo', url: '/api/v2/hosts/123/' }],
},
});
wrapper.find('DropdownToggle button[aria-label="add"]').simulate('click');
wrapper.find('ToolbarAddButton[aria-label="Add"]').simulate('click');
await act(async () => {
wrapper
.find('DropdownItem[aria-label="Add existing host"]')
@ -240,7 +241,9 @@ describe('<InventoryGroupHostList />', () => {
},
},
});
const history = createMemoryHistory();
const history = createMemoryHistory({
initialEntries: ['/inventories/inventory/1/groups/2/nested_hosts/add'],
});
await act(async () => {
wrapper = mountWithContexts(<InventoryGroupHostList />, {
context: {
@ -250,10 +253,11 @@ describe('<InventoryGroupHostList />', () => {
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
const dropdownToggle = wrapper.find(
'DropdownToggle button[aria-label="add"]'
'ToolbarAddButton button[aria-label="Add"]'
);
dropdownToggle.simulate('click');
wrapper.find('DropdownItem[aria-label="Add new host"]').simulate('click');
wrapper.update();
expect(history.location.pathname).toEqual(
'/inventories/inventory/1/groups/2/nested_hosts/add'
);

View File

@ -10,7 +10,12 @@ import { InventoriesAPI, GroupsAPI } from '../../../api';
import InventoryGroupsList from './InventoryGroupsList';
jest.mock('../../../api');
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({
id: 1,
}),
}));
const mockGroups = [
{
id: 1,

View File

@ -1,10 +1,11 @@
import React, { useState, useCallback, useEffect } from 'react';
import { useLocation, useRouteMatch } from 'react-router-dom';
import { useLocation, useRouteMatch, Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Card, PageSection } from '@patternfly/react-core';
import { Card, PageSection, DropdownItem } from '@patternfly/react-core';
import { InventoriesAPI } from '../../../api';
import useRequest, { useDeleteItems } from '../../../util/useRequest';
import { toTitleCase } from '../../../util/strings';
import AlertModal from '../../../components/AlertModal';
import DatalistToolbar from '../../../components/DataListToolbar';
import ErrorDetail from '../../../components/ErrorDetail';
@ -124,19 +125,28 @@ function InventoryList({ i18n }) {
}
}
};
const addInventory = toTitleCase(i18n._(t`Add Inventory`));
const addSmartInventory = toTitleCase(i18n._(t`Add Smart Inventory`));
const addButton = (
<AddDropDownButton
key="add"
dropdownItems={[
{
label: i18n._(t`Inventory`),
url: `${match.url}/inventory/add/`,
},
{
label: i18n._(t`Smart Inventory`),
url: `${match.url}/smart_inventory/add/`,
},
<DropdownItem
to={`${match.url}/inventory/add/`}
component={Link}
key={addInventory}
aria-label={addInventory}
>
{addInventory}
</DropdownItem>,
<DropdownItem
to={`${match.url}/smart_inventory/add/`}
component={Link}
key={addSmartInventory}
aria-label={addSmartInventory}
>
{addSmartInventory}
</DropdownItem>,
]}
/>
);

View File

@ -1,8 +1,9 @@
import React, { useCallback, useEffect, useState } from 'react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { useParams, useLocation, useHistory } from 'react-router-dom';
import { useParams, useLocation, Link } from 'react-router-dom';
import { DropdownItem } from '@patternfly/react-core';
import { GroupsAPI, InventoriesAPI } from '../../../api';
import useRequest from '../../../util/useRequest';
import { getQSConfig, parseQueryString, mergeParams } from '../../../util/qs';
@ -11,10 +12,11 @@ import useSelected from '../../../util/useSelected';
import DataListToolbar from '../../../components/DataListToolbar';
import PaginatedDataList from '../../../components/PaginatedDataList';
import InventoryGroupRelatedGroupListItem from './InventoryRelatedGroupListItem';
import AddDropdown from '../shared/AddDropdown';
import AddDropDownButton from '../../../components/AddDropDownButton';
import AdHocCommands from '../../../components/AdHocCommands/AdHocCommands';
import AssociateModal from '../../../components/AssociateModal';
import DisassociateButton from '../../../components/DisassociateButton';
import { toTitleCase } from '../../../util/strings';
const QS_CONFIG = getQSConfig('group', {
page: 1,
@ -25,7 +27,7 @@ function InventoryRelatedGroupList({ i18n }) {
const [isModalOpen, setIsModalOpen] = useState(false);
const { id: inventoryId, groupId } = useParams();
const location = useLocation();
const history = useHistory();
const {
request: fetchRelated,
result: {
@ -85,26 +87,31 @@ function InventoryRelatedGroupList({ i18n }) {
);
const addFormUrl = `/home`;
const addButtonOptions = [];
if (canAdd) {
addButtonOptions.push(
{
onAdd: () => setIsModalOpen(true),
title: i18n._(t`Add existing group`),
label: i18n._(t`group`),
key: 'existing',
},
{
onAdd: () => history.push(addFormUrl),
title: i18n._(t`Add new group`),
label: i18n._(t`group`),
key: 'new',
}
);
}
const addButton = <AddDropdown key="add" dropdownItems={addButtonOptions} />;
const addExistingGroup = toTitleCase(i18n._(t`Add Existing Group`));
const addNewGroup = toTitleCase(i18n._(t`Add New Group`));
const addButton = (
<AddDropDownButton
key="add"
dropdownItems={[
<DropdownItem
key={addExistingGroup}
onClick={() => setIsModalOpen(true)}
aria-label={addExistingGroup}
>
{addExistingGroup}
</DropdownItem>,
<DropdownItem
component={Link}
to={`${addFormUrl}`}
key={addNewGroup}
aria-label={addNewGroup}
>
{addNewGroup}
</DropdownItem>,
]}
/>
);
return (
<>
@ -173,17 +180,7 @@ function InventoryRelatedGroupList({ i18n }) {
onSelect={() => handleSelect(o)}
/>
)}
emptyStateControls={
canAdd && (
<AddDropdown
onAddExisting={() => setIsModalOpen(true)}
onAddNew={() => history.push(addFormUrl)}
newTitle={i18n._(t`Add new group`)}
existingTitle={i18n._(t`Add existing group`)}
label={i18n._(t`group`)}
/>
)
}
emptyStateControls={canAdd && addButton}
/>
{isModalOpen && (
<AssociateModal

View File

@ -1,88 +0,0 @@
import React, { useState, useRef, useEffect, Fragment } from 'react';
import { func, string, arrayOf, shape } from 'prop-types';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import {
Dropdown,
DropdownItem,
DropdownPosition,
DropdownToggle,
} from '@patternfly/react-core';
import { useKebabifiedMenu } from '../../../contexts/Kebabified';
function AddDropdown({ dropdownItems, i18n }) {
const { isKebabified } = useKebabifiedMenu();
const [isOpen, setIsOpen] = useState(false);
const element = useRef(null);
useEffect(() => {
const toggle = e => {
if (!isKebabified && (!element || !element.current.contains(e.target))) {
setIsOpen(false);
}
};
document.addEventListener('click', toggle, false);
return () => {
document.removeEventListener('click', toggle);
};
}, [isKebabified]);
if (isKebabified) {
return (
<Fragment>
{dropdownItems.map(item => (
<DropdownItem
key={item.key}
aria-label={item.title}
onClick={item.onAdd}
>
{item.title}
</DropdownItem>
))}
</Fragment>
);
}
return (
<div ref={element} key="add">
<Dropdown
isOpen={isOpen}
position={DropdownPosition.left}
toggle={
<DropdownToggle
id="add"
aria-label="add"
isPrimary
onToggle={() => setIsOpen(prevState => !prevState)}
>
{i18n._(t`Add`)}
</DropdownToggle>
}
dropdownItems={dropdownItems.map(item => (
<DropdownItem
className="pf-c-dropdown__menu-item"
key={item.key}
aria-label={item.title}
onClick={item.onAdd}
>
{item.title}
</DropdownItem>
))}
/>
</div>
);
}
AddDropdown.propTypes = {
dropdownItems: arrayOf(
shape({
label: string.isRequired,
onAdd: func.isRequired,
key: string.isRequired,
})
).isRequired,
};
export { AddDropdown as _AddDropdown };
export default withI18n()(AddDropdown);

View File

@ -1,43 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
import AddDropdown from './AddDropdown';
describe('<AddDropdown />', () => {
let wrapper;
let dropdownToggle;
const dropdownItems = [
{
onAdd: () => {},
title: 'Add existing group',
label: 'group',
key: 'existing',
},
{
onAdd: () => {},
title: 'Add new group',
label: 'group',
key: 'new',
},
];
beforeEach(() => {
wrapper = mountWithContexts(<AddDropdown dropdownItems={dropdownItems} />);
dropdownToggle = wrapper.find('DropdownToggle button');
});
test('should initially render a closed dropdown', () => {
expect(wrapper.find('DropdownItem').length).toBe(0);
});
test('should render two dropdown items', () => {
dropdownToggle.simulate('click');
expect(wrapper.find('DropdownItem').length).toBe(2);
});
test('should close when button re-clicked', () => {
dropdownToggle.simulate('click');
expect(wrapper.find('DropdownItem').length).toBe(2);
dropdownToggle.simulate('click');
expect(wrapper.find('DropdownItem').length).toBe(0);
});
});

View File

@ -1,9 +1,8 @@
import React, { Fragment, useEffect, useState, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { useLocation, Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Card } from '@patternfly/react-core';
import { Card, DropdownItem } from '@patternfly/react-core';
import {
JobTemplatesAPI,
UnifiedJobTemplatesAPI,
@ -17,6 +16,7 @@ import PaginatedDataList, {
} from '../../../components/PaginatedDataList';
import useRequest, { useDeleteItems } from '../../../util/useRequest';
import { getQSConfig, parseQueryString } from '../../../util/qs';
import { toTitleCase } from '../../../util/strings';
import useWsTemplates from '../../../util/useWsTemplates';
import AddDropDownButton from '../../../components/AddDropDownButton';
import TemplateListItem from './TemplateListItem';
@ -32,7 +32,6 @@ const QS_CONFIG = getQSConfig('template', {
function TemplateList({ i18n }) {
const location = useLocation();
const [selected, setSelected] = useState([]);
const {
@ -134,24 +133,32 @@ function TemplateList({ i18n }) {
jtActions && Object.prototype.hasOwnProperty.call(jtActions, 'POST');
const canAddWFJT =
wfjtActions && Object.prototype.hasOwnProperty.call(wfjtActions, 'POST');
const addButtonOptions = [];
if (canAddJT) {
addButtonOptions.push({
label: i18n._(t`Job Template`),
url: `/templates/job_template/add/`,
});
}
if (canAddWFJT) {
addButtonOptions.push({
label: i18n._(t`Workflow Template`),
url: `/templates/workflow_job_template/add/`,
});
}
// spreading Set() returns only unique keys
const addTempate = toTitleCase(i18n._(t`Add Job Template`));
const addWFTemplate = toTitleCase(i18n._(t`Add Workflow Template`));
const addButton = (
<AddDropDownButton key="add" dropdownItems={addButtonOptions} />
<AddDropDownButton
key="add"
dropdownItems={[
<DropdownItem
key={addTempate}
component={Link}
to="/templates/job_template/add/"
aria-label={addTempate}
>
{addTempate}
</DropdownItem>,
<DropdownItem
component={Link}
to="/templates/workflow_job_template/add/"
key={addWFTemplate}
aria-label={addWFTemplate}
>
{addWFTemplate}
</DropdownItem>,
]}
/>
);
return (