mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 10:30:03 -03:30
Merges 2 tool bar add buttons that use dropdowns
This commit is contained in:
parent
51600986c9
commit
a2ca2729ba
@ -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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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>,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
@ -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 (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user