mirror of
https://github.com/ansible/awx.git
synced 2026-03-26 13:25:02 -02:30
Merge pull request #7880 from jlmitch5/kebabifyAdvancedSearch
kebabify additional controls when advanced search is displayed Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -1,25 +1,46 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect, Fragment } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Dropdown, DropdownPosition } from '@patternfly/react-core';
|
import {
|
||||||
|
Dropdown,
|
||||||
|
DropdownPosition,
|
||||||
|
DropdownItem,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
import { ToolbarAddButton } from '../PaginatedDataList';
|
import { ToolbarAddButton } from '../PaginatedDataList';
|
||||||
|
import { toTitleCase } from '../../util/strings';
|
||||||
|
import { useKebabifiedMenu } from '../../contexts/Kebabified';
|
||||||
|
|
||||||
function AddDropDownButton({ dropdownItems }) {
|
function AddDropDownButton({ dropdownItems, i18n }) {
|
||||||
|
const { isKebabified } = useKebabifiedMenu();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const element = useRef(null);
|
const element = useRef(null);
|
||||||
|
|
||||||
const toggle = e => {
|
|
||||||
if (!element || !element.current.contains(e.target)) {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const toggle = e => {
|
||||||
|
if (!isKebabified && (!element || !element.current.contains(e.target))) {
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
document.addEventListener('click', toggle, false);
|
document.addEventListener('click', toggle, false);
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('click', toggle);
|
document.removeEventListener('click', toggle);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [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 (
|
return (
|
||||||
<div ref={element} key="add">
|
<div ref={element} key="add">
|
||||||
@@ -52,4 +73,4 @@ AddDropDownButton.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export { AddDropDownButton as _AddDropDownButton };
|
export { AddDropDownButton as _AddDropDownButton };
|
||||||
export default AddDropDownButton;
|
export default withI18n()(AddDropDownButton);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
@@ -9,103 +9,128 @@ import {
|
|||||||
ToolbarGroup,
|
ToolbarGroup,
|
||||||
ToolbarItem,
|
ToolbarItem,
|
||||||
ToolbarToggleGroup,
|
ToolbarToggleGroup,
|
||||||
|
Dropdown,
|
||||||
|
KebabToggle,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { SearchIcon } from '@patternfly/react-icons';
|
import { SearchIcon } from '@patternfly/react-icons';
|
||||||
import ExpandCollapse from '../ExpandCollapse';
|
import ExpandCollapse from '../ExpandCollapse';
|
||||||
import Search from '../Search';
|
import Search from '../Search';
|
||||||
import Sort from '../Sort';
|
import Sort from '../Sort';
|
||||||
|
|
||||||
import { SearchColumns, SortColumns, QSConfig } from '../../types';
|
import { SearchColumns, SortColumns, QSConfig } from '../../types';
|
||||||
|
import { KebabifiedProvider } from '../../contexts/Kebabified';
|
||||||
|
|
||||||
class DataListToolbar extends React.Component {
|
function DataListToolbar({
|
||||||
render() {
|
itemCount,
|
||||||
const {
|
clearAllFilters,
|
||||||
itemCount,
|
searchColumns,
|
||||||
clearAllFilters,
|
searchableKeys,
|
||||||
searchColumns,
|
relatedSearchableKeys,
|
||||||
searchableKeys,
|
sortColumns,
|
||||||
relatedSearchableKeys,
|
showSelectAll,
|
||||||
sortColumns,
|
isAllSelected,
|
||||||
showSelectAll,
|
isCompact,
|
||||||
isAllSelected,
|
onSort,
|
||||||
isCompact,
|
onSearch,
|
||||||
onSort,
|
onReplaceSearch,
|
||||||
onSearch,
|
onRemove,
|
||||||
onReplaceSearch,
|
onCompact,
|
||||||
onRemove,
|
onExpand,
|
||||||
onCompact,
|
onSelectAll,
|
||||||
onExpand,
|
additionalControls,
|
||||||
onSelectAll,
|
i18n,
|
||||||
additionalControls,
|
qsConfig,
|
||||||
i18n,
|
pagination,
|
||||||
qsConfig,
|
}) {
|
||||||
pagination,
|
const showExpandCollapse = onCompact && onExpand;
|
||||||
} = this.props;
|
const [kebabIsOpen, setKebabIsOpen] = useState(false);
|
||||||
|
const [advancedSearchShown, setAdvancedSearchShown] = useState(false);
|
||||||
|
|
||||||
const showExpandCollapse = onCompact && onExpand;
|
const onShowAdvancedSearch = shown => {
|
||||||
return (
|
setAdvancedSearchShown(shown);
|
||||||
<Toolbar
|
setKebabIsOpen(false);
|
||||||
id={`${qsConfig.namespace}-list-toolbar`}
|
};
|
||||||
clearAllFilters={clearAllFilters}
|
|
||||||
collapseListedFiltersBreakpoint="lg"
|
return (
|
||||||
>
|
<Toolbar
|
||||||
<ToolbarContent>
|
id={`${qsConfig.namespace}-list-toolbar`}
|
||||||
{showSelectAll && (
|
clearAllFilters={clearAllFilters}
|
||||||
<ToolbarGroup>
|
collapseListedFiltersBreakpoint="lg"
|
||||||
<ToolbarItem>
|
>
|
||||||
<Checkbox
|
<ToolbarContent>
|
||||||
isChecked={isAllSelected}
|
{showSelectAll && (
|
||||||
onChange={onSelectAll}
|
<ToolbarGroup>
|
||||||
aria-label={i18n._(t`Select all`)}
|
|
||||||
id="select-all"
|
|
||||||
/>
|
|
||||||
</ToolbarItem>
|
|
||||||
</ToolbarGroup>
|
|
||||||
)}
|
|
||||||
<ToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg">
|
|
||||||
<ToolbarItem>
|
<ToolbarItem>
|
||||||
<Search
|
<Checkbox
|
||||||
qsConfig={qsConfig}
|
isChecked={isAllSelected}
|
||||||
columns={[
|
onChange={onSelectAll}
|
||||||
...searchColumns,
|
aria-label={i18n._(t`Select all`)}
|
||||||
{ name: i18n._(t`Advanced`), key: 'advanced' },
|
id="select-all"
|
||||||
]}
|
|
||||||
searchableKeys={searchableKeys}
|
|
||||||
relatedSearchableKeys={relatedSearchableKeys}
|
|
||||||
onSearch={onSearch}
|
|
||||||
onReplaceSearch={onReplaceSearch}
|
|
||||||
onRemove={onRemove}
|
|
||||||
/>
|
/>
|
||||||
</ToolbarItem>
|
</ToolbarItem>
|
||||||
<ToolbarItem>
|
</ToolbarGroup>
|
||||||
<Sort qsConfig={qsConfig} columns={sortColumns} onSort={onSort} />
|
)}
|
||||||
</ToolbarItem>
|
<ToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg">
|
||||||
</ToolbarToggleGroup>
|
<ToolbarItem>
|
||||||
{showExpandCollapse && (
|
<Search
|
||||||
<ToolbarGroup>
|
qsConfig={qsConfig}
|
||||||
<Fragment>
|
columns={[
|
||||||
<ToolbarItem>
|
...searchColumns,
|
||||||
<ExpandCollapse
|
{ name: i18n._(t`Advanced`), key: 'advanced' },
|
||||||
isCompact={isCompact}
|
]}
|
||||||
onCompact={onCompact}
|
searchableKeys={searchableKeys}
|
||||||
onExpand={onExpand}
|
relatedSearchableKeys={relatedSearchableKeys}
|
||||||
/>
|
onSearch={onSearch}
|
||||||
</ToolbarItem>
|
onReplaceSearch={onReplaceSearch}
|
||||||
</Fragment>
|
onShowAdvancedSearch={onShowAdvancedSearch}
|
||||||
</ToolbarGroup>
|
onRemove={onRemove}
|
||||||
)}
|
/>
|
||||||
|
</ToolbarItem>
|
||||||
|
<ToolbarItem>
|
||||||
|
<Sort qsConfig={qsConfig} columns={sortColumns} onSort={onSort} />
|
||||||
|
</ToolbarItem>
|
||||||
|
</ToolbarToggleGroup>
|
||||||
|
{showExpandCollapse && (
|
||||||
|
<ToolbarGroup>
|
||||||
|
<Fragment>
|
||||||
|
<ToolbarItem>
|
||||||
|
<ExpandCollapse
|
||||||
|
isCompact={isCompact}
|
||||||
|
onCompact={onCompact}
|
||||||
|
onExpand={onExpand}
|
||||||
|
/>
|
||||||
|
</ToolbarItem>
|
||||||
|
</Fragment>
|
||||||
|
</ToolbarGroup>
|
||||||
|
)}
|
||||||
|
{advancedSearchShown && (
|
||||||
|
<ToolbarItem>
|
||||||
|
<Dropdown
|
||||||
|
toggle={<KebabToggle onToggle={setKebabIsOpen} />}
|
||||||
|
isOpen={kebabIsOpen}
|
||||||
|
isPlain
|
||||||
|
dropdownItems={additionalControls.map(control => {
|
||||||
|
return (
|
||||||
|
<KebabifiedProvider value={{ isKebabified: true }}>
|
||||||
|
{control}
|
||||||
|
</KebabifiedProvider>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</ToolbarItem>
|
||||||
|
)}
|
||||||
|
{!advancedSearchShown && (
|
||||||
<ToolbarGroup>
|
<ToolbarGroup>
|
||||||
{additionalControls.map(control => (
|
{additionalControls.map(control => (
|
||||||
<ToolbarItem key={control.key}>{control}</ToolbarItem>
|
<ToolbarItem key={control.key}>{control}</ToolbarItem>
|
||||||
))}
|
))}
|
||||||
</ToolbarGroup>
|
</ToolbarGroup>
|
||||||
{pagination && itemCount > 0 && (
|
)}
|
||||||
<ToolbarItem variant="pagination">{pagination}</ToolbarItem>
|
{!advancedSearchShown && pagination && itemCount > 0 && (
|
||||||
)}
|
<ToolbarItem variant="pagination">{pagination}</ToolbarItem>
|
||||||
</ToolbarContent>
|
)}
|
||||||
</Toolbar>
|
</ToolbarContent>
|
||||||
);
|
</Toolbar>
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataListToolbar.propTypes = {
|
DataListToolbar.propTypes = {
|
||||||
|
|||||||
@@ -1,16 +1,32 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { string, func } from 'prop-types';
|
import { string, func } from 'prop-types';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Button, Tooltip } from '@patternfly/react-core';
|
import { Button, DropdownItem, Tooltip } from '@patternfly/react-core';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
import { useKebabifiedMenu } from '../../contexts/Kebabified';
|
||||||
|
|
||||||
function ToolbarAddButton({ linkTo, onClick, i18n, isDisabled }) {
|
function ToolbarAddButton({ linkTo, onClick, i18n, isDisabled }) {
|
||||||
|
const { isKebabified } = useKebabifiedMenu();
|
||||||
|
|
||||||
if (!linkTo && !onClick) {
|
if (!linkTo && !onClick) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'ToolbarAddButton requires either `linkTo` or `onClick` prop'
|
'ToolbarAddButton requires either `linkTo` or `onClick` prop'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (isKebabified) {
|
||||||
|
return (
|
||||||
|
<DropdownItem
|
||||||
|
key="add"
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
component={linkTo ? Link : Button}
|
||||||
|
to={linkTo}
|
||||||
|
onClick={!onClick ? undefined : onClick}
|
||||||
|
>
|
||||||
|
{i18n._(t`Add`)}
|
||||||
|
</DropdownItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
if (linkTo) {
|
if (linkTo) {
|
||||||
return (
|
return (
|
||||||
<Tooltip content={i18n._(t`Add`)} position="top">
|
<Tooltip content={i18n._(t`Add`)} position="top">
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import {
|
|||||||
shape,
|
shape,
|
||||||
checkPropTypes,
|
checkPropTypes,
|
||||||
} from 'prop-types';
|
} from 'prop-types';
|
||||||
import { Button, Tooltip } from '@patternfly/react-core';
|
import { Button, DropdownItem, Tooltip } from '@patternfly/react-core';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import AlertModal from '../AlertModal';
|
import AlertModal from '../AlertModal';
|
||||||
|
import { Kebabified } from '../../contexts/Kebabified';
|
||||||
|
|
||||||
const requireNameOrUsername = props => {
|
const requireNameOrUsername = props => {
|
||||||
const { name, username } = props;
|
const { name, username } = props;
|
||||||
@@ -138,54 +139,69 @@ class ToolbarDeleteButton extends React.Component {
|
|||||||
// we can delete the extra <div> around the <DeleteButton> below.
|
// we can delete the extra <div> around the <DeleteButton> below.
|
||||||
// See: https://github.com/patternfly/patternfly-react/issues/1894
|
// See: https://github.com/patternfly/patternfly-react/issues/1894
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Kebabified>
|
||||||
<Tooltip content={this.renderTooltip()} position="top">
|
{({ isKebabified }) => (
|
||||||
<div>
|
<Fragment>
|
||||||
<Button
|
{isKebabified ? (
|
||||||
variant="secondary"
|
<DropdownItem
|
||||||
aria-label={i18n._(t`Delete`)}
|
key="add"
|
||||||
onClick={this.handleConfirmDelete}
|
isDisabled={isDisabled}
|
||||||
isDisabled={isDisabled}
|
component="Button"
|
||||||
>
|
onClick={this.handleConfirmDelete}
|
||||||
{i18n._(t`Delete`)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
{isModalOpen && (
|
|
||||||
<AlertModal
|
|
||||||
variant="danger"
|
|
||||||
title={modalTitle}
|
|
||||||
isOpen={isModalOpen}
|
|
||||||
onClose={this.handleCancelDelete}
|
|
||||||
actions={[
|
|
||||||
<Button
|
|
||||||
key="delete"
|
|
||||||
variant="danger"
|
|
||||||
aria-label={i18n._(t`confirm delete`)}
|
|
||||||
onClick={this.handleDelete}
|
|
||||||
>
|
>
|
||||||
{i18n._(t`Delete`)}
|
{i18n._(t`Delete`)}
|
||||||
</Button>,
|
</DropdownItem>
|
||||||
<Button
|
) : (
|
||||||
key="cancel"
|
<Tooltip content={this.renderTooltip()} position="top">
|
||||||
variant="secondary"
|
<div>
|
||||||
aria-label={i18n._(t`cancel delete`)}
|
<Button
|
||||||
onClick={this.handleCancelDelete}
|
variant="secondary"
|
||||||
|
aria-label={i18n._(t`Delete`)}
|
||||||
|
onClick={this.handleConfirmDelete}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
>
|
||||||
|
{i18n._(t`Delete`)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{isModalOpen && (
|
||||||
|
<AlertModal
|
||||||
|
variant="danger"
|
||||||
|
title={modalTitle}
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
onClose={this.handleCancelDelete}
|
||||||
|
actions={[
|
||||||
|
<Button
|
||||||
|
key="delete"
|
||||||
|
variant="danger"
|
||||||
|
aria-label={i18n._(t`confirm delete`)}
|
||||||
|
onClick={this.handleDelete}
|
||||||
|
>
|
||||||
|
{i18n._(t`Delete`)}
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="cancel"
|
||||||
|
variant="secondary"
|
||||||
|
aria-label={i18n._(t`cancel delete`)}
|
||||||
|
onClick={this.handleCancelDelete}
|
||||||
|
>
|
||||||
|
{i18n._(t`Cancel`)}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{i18n._(t`Cancel`)}
|
<div>{i18n._(t`This action will delete the following:`)}</div>
|
||||||
</Button>,
|
{itemsToDelete.map(item => (
|
||||||
]}
|
<span key={item.id}>
|
||||||
>
|
<strong>{item.name || item.username}</strong>
|
||||||
<div>{i18n._(t`This action will delete the following:`)}</div>
|
<br />
|
||||||
{itemsToDelete.map(item => (
|
</span>
|
||||||
<span key={item.id}>
|
))}
|
||||||
<strong>{item.name || item.username}</strong>
|
</AlertModal>
|
||||||
<br />
|
)}
|
||||||
</span>
|
</Fragment>
|
||||||
))}
|
|
||||||
</AlertModal>
|
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Kebabified>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ function Search({
|
|||||||
location,
|
location,
|
||||||
searchableKeys,
|
searchableKeys,
|
||||||
relatedSearchableKeys,
|
relatedSearchableKeys,
|
||||||
|
onShowAdvancedSearch,
|
||||||
}) {
|
}) {
|
||||||
const [isSearchDropdownOpen, setIsSearchDropdownOpen] = useState(false);
|
const [isSearchDropdownOpen, setIsSearchDropdownOpen] = useState(false);
|
||||||
const [searchKey, setSearchKey] = useState(
|
const [searchKey, setSearchKey] = useState(
|
||||||
@@ -62,7 +63,7 @@ function Search({
|
|||||||
const { key: actualSearchKey } = columns.find(
|
const { key: actualSearchKey } = columns.find(
|
||||||
({ name }) => name === target.innerText
|
({ name }) => name === target.innerText
|
||||||
);
|
);
|
||||||
|
onShowAdvancedSearch(actualSearchKey === 'advanced');
|
||||||
setIsFilterDropdownOpen(false);
|
setIsFilterDropdownOpen(false);
|
||||||
setSearchKey(actualSearchKey);
|
setSearchKey(actualSearchKey);
|
||||||
};
|
};
|
||||||
@@ -301,6 +302,7 @@ Search.propTypes = {
|
|||||||
columns: SearchColumns.isRequired,
|
columns: SearchColumns.isRequired,
|
||||||
onSearch: PropTypes.func,
|
onSearch: PropTypes.func,
|
||||||
onRemove: PropTypes.func,
|
onRemove: PropTypes.func,
|
||||||
|
onShowAdvancedSearch: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
Search.defaultProps = {
|
Search.defaultProps = {
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ describe('<Search />', () => {
|
|||||||
collapseListedFiltersBreakpoint="lg"
|
collapseListedFiltersBreakpoint="lg"
|
||||||
>
|
>
|
||||||
<ToolbarContent>
|
<ToolbarContent>
|
||||||
<Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} />
|
<Search
|
||||||
|
qsConfig={QS_CONFIG}
|
||||||
|
columns={columns}
|
||||||
|
onSearch={onSearch}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
@@ -64,7 +69,12 @@ describe('<Search />', () => {
|
|||||||
collapseListedFiltersBreakpoint="lg"
|
collapseListedFiltersBreakpoint="lg"
|
||||||
>
|
>
|
||||||
<ToolbarContent>
|
<ToolbarContent>
|
||||||
<Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} />
|
<Search
|
||||||
|
qsConfig={QS_CONFIG}
|
||||||
|
columns={columns}
|
||||||
|
onSearch={onSearch}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
@@ -83,6 +93,50 @@ describe('<Search />', () => {
|
|||||||
expect(onSearch).toBeCalledWith('description__icontains', 'test-321');
|
expect(onSearch).toBeCalledWith('description__icontains', 'test-321');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('changing key select to and from advanced causes onShowAdvancedSearch callback to be invoked', () => {
|
||||||
|
const columns = [
|
||||||
|
{ name: 'Name', key: 'name__icontains', isDefault: true },
|
||||||
|
{ name: 'Description', key: 'description__icontains' },
|
||||||
|
{ name: 'Advanced', key: 'advanced' },
|
||||||
|
];
|
||||||
|
const onSearch = jest.fn();
|
||||||
|
const onShowAdvancedSearch = jest.fn();
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<Toolbar
|
||||||
|
id={`${QS_CONFIG.namespace}-list-toolbar`}
|
||||||
|
clearAllFilters={() => {}}
|
||||||
|
collapseListedFiltersBreakpoint="lg"
|
||||||
|
>
|
||||||
|
<ToolbarContent>
|
||||||
|
<Search
|
||||||
|
qsConfig={QS_CONFIG}
|
||||||
|
columns={columns}
|
||||||
|
onSearch={onSearch}
|
||||||
|
onShowAdvancedSearch={onShowAdvancedSearch}
|
||||||
|
/>
|
||||||
|
</ToolbarContent>
|
||||||
|
</Toolbar>
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
wrapper
|
||||||
|
.find('Select[aria-label="Simple key select"]')
|
||||||
|
.invoke('onSelect')({ target: { innerText: 'Advanced' } });
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(onShowAdvancedSearch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onShowAdvancedSearch).toBeCalledWith(true);
|
||||||
|
jest.clearAllMocks();
|
||||||
|
act(() => {
|
||||||
|
wrapper
|
||||||
|
.find('Select[aria-label="Simple key select"]')
|
||||||
|
.invoke('onSelect')({ target: { innerText: 'Description' } });
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(onShowAdvancedSearch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onShowAdvancedSearch).toBeCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
test('attempt to search with empty string', () => {
|
test('attempt to search with empty string', () => {
|
||||||
const searchButton = 'button[aria-label="Search submit button"]';
|
const searchButton = 'button[aria-label="Search submit button"]';
|
||||||
const searchTextInput = 'input[aria-label="Search text input"]';
|
const searchTextInput = 'input[aria-label="Search text input"]';
|
||||||
@@ -95,7 +149,12 @@ describe('<Search />', () => {
|
|||||||
collapseListedFiltersBreakpoint="lg"
|
collapseListedFiltersBreakpoint="lg"
|
||||||
>
|
>
|
||||||
<ToolbarContent>
|
<ToolbarContent>
|
||||||
<Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} />
|
<Search
|
||||||
|
qsConfig={QS_CONFIG}
|
||||||
|
columns={columns}
|
||||||
|
onSearch={onSearch}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
@@ -119,7 +178,12 @@ describe('<Search />', () => {
|
|||||||
collapseListedFiltersBreakpoint="lg"
|
collapseListedFiltersBreakpoint="lg"
|
||||||
>
|
>
|
||||||
<ToolbarContent>
|
<ToolbarContent>
|
||||||
<Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} />
|
<Search
|
||||||
|
qsConfig={QS_CONFIG}
|
||||||
|
columns={columns}
|
||||||
|
onSearch={onSearch}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
@@ -150,7 +214,11 @@ describe('<Search />', () => {
|
|||||||
collapseListedFiltersBreakpoint="lg"
|
collapseListedFiltersBreakpoint="lg"
|
||||||
>
|
>
|
||||||
<ToolbarContent>
|
<ToolbarContent>
|
||||||
<Search qsConfig={QS_CONFIG} columns={columns} />
|
<Search
|
||||||
|
qsConfig={QS_CONFIG}
|
||||||
|
columns={columns}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>,
|
</Toolbar>,
|
||||||
{ context: { router: { history } } }
|
{ context: { router: { history } } }
|
||||||
@@ -197,6 +265,7 @@ describe('<Search />', () => {
|
|||||||
qsConfig={qsConfigNew}
|
qsConfig={qsConfigNew}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onRemove={onRemove}
|
onRemove={onRemove}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
/>
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>,
|
</Toolbar>,
|
||||||
@@ -243,6 +312,7 @@ describe('<Search />', () => {
|
|||||||
qsConfig={qsConfigNew}
|
qsConfig={qsConfigNew}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onRemove={onRemove}
|
onRemove={onRemove}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
/>
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>,
|
</Toolbar>,
|
||||||
@@ -277,7 +347,11 @@ describe('<Search />', () => {
|
|||||||
collapseListedFiltersBreakpoint="lg"
|
collapseListedFiltersBreakpoint="lg"
|
||||||
>
|
>
|
||||||
<ToolbarContent>
|
<ToolbarContent>
|
||||||
<Search qsConfig={QS_CONFIG} columns={columns} />
|
<Search
|
||||||
|
qsConfig={QS_CONFIG}
|
||||||
|
columns={columns}
|
||||||
|
onShowAdvancedSearch={jest.fn}
|
||||||
|
/>
|
||||||
</ToolbarContent>
|
</ToolbarContent>
|
||||||
</Toolbar>,
|
</Toolbar>,
|
||||||
{ context: { router: { history } } }
|
{ context: { router: { history } } }
|
||||||
|
|||||||
8
awx/ui_next/src/contexts/Kebabified.jsx
Normal file
8
awx/ui_next/src/contexts/Kebabified.jsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const KebabifiedContext = React.createContext({});
|
||||||
|
|
||||||
|
export const KebabifiedProvider = KebabifiedContext.Provider;
|
||||||
|
export const Kebabified = KebabifiedContext.Consumer;
|
||||||
|
export const useKebabifiedMenu = () => useContext(KebabifiedContext);
|
||||||
Reference in New Issue
Block a user