mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
update search and sort column configuration
This commit is contained in:
@@ -142,21 +142,33 @@ class AddResourceRole extends React.Component {
|
|||||||
} = this.state;
|
} = this.state;
|
||||||
const { onClose, roles, i18n } = this.props;
|
const { onClose, roles, i18n } = this.props;
|
||||||
|
|
||||||
const userColumns = [
|
const userSearchColumns = [
|
||||||
{
|
{
|
||||||
name: i18n._(t`Username`),
|
name: i18n._(t`Username`),
|
||||||
key: 'username',
|
key: 'username',
|
||||||
isSortable: true,
|
isDefault: true
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const teamColumns = [
|
const userSortColumns = [
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Username`),
|
||||||
|
key: 'username',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const teamSearchColumns = [
|
||||||
|
{
|
||||||
|
name: i18n._(t`name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const teamSortColumns = [
|
||||||
|
{
|
||||||
|
name: i18n._(t`name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -207,7 +219,8 @@ class AddResourceRole extends React.Component {
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
{selectedResource === 'users' && (
|
{selectedResource === 'users' && (
|
||||||
<SelectResourceStep
|
<SelectResourceStep
|
||||||
columns={userColumns}
|
searchColumns={userSearchColumns}
|
||||||
|
sortColumns={userSortColumns}
|
||||||
displayKey="username"
|
displayKey="username"
|
||||||
onRowClick={this.handleResourceCheckboxClick}
|
onRowClick={this.handleResourceCheckboxClick}
|
||||||
onSearch={readUsers}
|
onSearch={readUsers}
|
||||||
@@ -218,7 +231,8 @@ class AddResourceRole extends React.Component {
|
|||||||
)}
|
)}
|
||||||
{selectedResource === 'teams' && (
|
{selectedResource === 'teams' && (
|
||||||
<SelectResourceStep
|
<SelectResourceStep
|
||||||
columns={teamColumns}
|
searchColumns={teamSearchColumns}
|
||||||
|
sortColumns={teamSortColumns}
|
||||||
onRowClick={this.handleResourceCheckboxClick}
|
onRowClick={this.handleResourceCheckboxClick}
|
||||||
onSearch={readTeams}
|
onSearch={readTeams}
|
||||||
selectedLabel={i18n._(t`Selected`)}
|
selectedLabel={i18n._(t`Selected`)}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { withRouter } 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 { SearchColumns, SortColumns } from '@types';
|
||||||
import PaginatedDataList from '../PaginatedDataList';
|
import PaginatedDataList from '../PaginatedDataList';
|
||||||
import DataListToolbar from '../DataListToolbar';
|
import DataListToolbar from '../DataListToolbar';
|
||||||
import CheckboxListItem from '../CheckboxListItem';
|
import CheckboxListItem from '../CheckboxListItem';
|
||||||
@@ -23,7 +24,7 @@ class SelectResourceStep extends React.Component {
|
|||||||
this.qsConfig = getQSConfig('resource', {
|
this.qsConfig = getQSConfig('resource', {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 5,
|
page_size: 5,
|
||||||
order_by: props.sortedColumnKey,
|
order_by: `${props.sortColumns.filter(col => col.key === 'name').length ? 'name' : 'username'}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +51,8 @@ class SelectResourceStep extends React.Component {
|
|||||||
const { data } = await onSearch(queryParams);
|
const { data } = await onSearch(queryParams);
|
||||||
const { count, results } = data;
|
const { count, results } = data;
|
||||||
|
|
||||||
|
debugger;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
resources: results,
|
resources: results,
|
||||||
count,
|
count,
|
||||||
@@ -69,7 +72,8 @@ class SelectResourceStep extends React.Component {
|
|||||||
const { isInitialized, isLoading, count, error, resources } = this.state;
|
const { isInitialized, isLoading, count, error, resources } = this.state;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
columns,
|
searchColumns,
|
||||||
|
sortColumns,
|
||||||
displayKey,
|
displayKey,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
selectedLabel,
|
selectedLabel,
|
||||||
@@ -99,8 +103,9 @@ class SelectResourceStep extends React.Component {
|
|||||||
items={resources}
|
items={resources}
|
||||||
itemCount={count}
|
itemCount={count}
|
||||||
qsConfig={this.qsConfig}
|
qsConfig={this.qsConfig}
|
||||||
toolbarColumns={columns}
|
|
||||||
onRowClick={onRowClick}
|
onRowClick={onRowClick}
|
||||||
|
toolbarSearchColumns={searchColumns}
|
||||||
|
toolbarSortColumns={sortColumns}
|
||||||
renderItem={item => (
|
renderItem={item => (
|
||||||
<CheckboxListItem
|
<CheckboxListItem
|
||||||
isSelected={selectedResourceRows.some(i => i.id === item.id)}
|
isSelected={selectedResourceRows.some(i => i.id === item.id)}
|
||||||
@@ -123,21 +128,22 @@ class SelectResourceStep extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SelectResourceStep.propTypes = {
|
SelectResourceStep.propTypes = {
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
searchColumns: SearchColumns,
|
||||||
|
sortColumns: SortColumns,
|
||||||
displayKey: PropTypes.string,
|
displayKey: PropTypes.string,
|
||||||
onRowClick: PropTypes.func,
|
onRowClick: PropTypes.func,
|
||||||
onSearch: PropTypes.func.isRequired,
|
onSearch: PropTypes.func.isRequired,
|
||||||
selectedLabel: PropTypes.string,
|
selectedLabel: PropTypes.string,
|
||||||
selectedResourceRows: PropTypes.arrayOf(PropTypes.object),
|
selectedResourceRows: PropTypes.arrayOf(PropTypes.object),
|
||||||
sortedColumnKey: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SelectResourceStep.defaultProps = {
|
SelectResourceStep.defaultProps = {
|
||||||
|
searchColumns: null,
|
||||||
|
sortColumns: null,
|
||||||
displayKey: 'name',
|
displayKey: 'name',
|
||||||
onRowClick: () => {},
|
onRowClick: () => {},
|
||||||
selectedLabel: null,
|
selectedLabel: null,
|
||||||
selectedResourceRows: [],
|
selectedResourceRows: [],
|
||||||
sortedColumnKey: 'name',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { SelectResourceStep as _SelectResourceStep };
|
export { SelectResourceStep as _SelectResourceStep };
|
||||||
|
|||||||
@@ -6,8 +6,19 @@ import { sleep } from '../../../testUtils/testUtils';
|
|||||||
import SelectResourceStep from './SelectResourceStep';
|
import SelectResourceStep from './SelectResourceStep';
|
||||||
|
|
||||||
describe('<SelectResourceStep />', () => {
|
describe('<SelectResourceStep />', () => {
|
||||||
const columns = [
|
const searchColumns = [
|
||||||
{ name: 'Username', key: 'username', isSortable: true, isSearchable: true },
|
{
|
||||||
|
name: 'Username',
|
||||||
|
key: 'username',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const sortColumns = [
|
||||||
|
{
|
||||||
|
name: 'Username',
|
||||||
|
key: 'username'
|
||||||
|
},
|
||||||
];
|
];
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
@@ -15,11 +26,11 @@ describe('<SelectResourceStep />', () => {
|
|||||||
test('initially renders without crashing', () => {
|
test('initially renders without crashing', () => {
|
||||||
shallow(
|
shallow(
|
||||||
<SelectResourceStep
|
<SelectResourceStep
|
||||||
columns={columns}
|
searchColumns={searchColumns}
|
||||||
|
sortColumns={sortColumns}
|
||||||
displayKey="username"
|
displayKey="username"
|
||||||
onRowClick={() => {}}
|
onRowClick={() => {}}
|
||||||
onSearch={() => {}}
|
onSearch={() => {}}
|
||||||
sortedColumnKey="username"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -36,11 +47,11 @@ describe('<SelectResourceStep />', () => {
|
|||||||
});
|
});
|
||||||
mountWithContexts(
|
mountWithContexts(
|
||||||
<SelectResourceStep
|
<SelectResourceStep
|
||||||
columns={columns}
|
searchColumns={searchColumns}
|
||||||
|
sortColumns={sortColumns}
|
||||||
displayKey="username"
|
displayKey="username"
|
||||||
onRowClick={() => {}}
|
onRowClick={() => {}}
|
||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
sortedColumnKey="username"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
expect(handleSearch).toHaveBeenCalledWith({
|
expect(handleSearch).toHaveBeenCalledWith({
|
||||||
@@ -68,12 +79,12 @@ describe('<SelectResourceStep />', () => {
|
|||||||
});
|
});
|
||||||
const wrapper = await mountWithContexts(
|
const wrapper = await mountWithContexts(
|
||||||
<SelectResourceStep
|
<SelectResourceStep
|
||||||
columns={columns}
|
searchColumns={searchColumns}
|
||||||
|
sortColumns={sortColumns}
|
||||||
displayKey="username"
|
displayKey="username"
|
||||||
onRowClick={() => {}}
|
onRowClick={() => {}}
|
||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
selectedResourceRows={selectedResourceRows}
|
selectedResourceRows={selectedResourceRows}
|
||||||
sortedColumnKey="username"
|
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
context: { router: { history, route: { location: history.location } } },
|
context: { router: { history, route: { location: history.location } } },
|
||||||
@@ -102,12 +113,12 @@ describe('<SelectResourceStep />', () => {
|
|||||||
};
|
};
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<SelectResourceStep
|
<SelectResourceStep
|
||||||
columns={columns}
|
searchColumns={searchColumns}
|
||||||
|
sortColumns={sortColumns}
|
||||||
displayKey="username"
|
displayKey="username"
|
||||||
onRowClick={handleRowClick}
|
onRowClick={handleRowClick}
|
||||||
onSearch={() => ({ data })}
|
onSearch={() => ({ data })}
|
||||||
selectedResourceRows={[]}
|
selectedResourceRows={[]}
|
||||||
sortedColumnKey="username"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ import {
|
|||||||
ToolbarGroup as PFToolbarGroup,
|
ToolbarGroup as PFToolbarGroup,
|
||||||
ToolbarItem,
|
ToolbarItem,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
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 VerticalSeparator from '../VerticalSeparator';
|
import VerticalSeparator from '../VerticalSeparator';
|
||||||
|
|
||||||
import { QSConfig } from '@types';
|
import { SearchColumns, SortColumns, QSConfig } from '@types';
|
||||||
|
|
||||||
const AWXToolbar = styled.div`
|
const AWXToolbar = styled.div`
|
||||||
--awx-toolbar--BackgroundColor: var(--pf-global--BackgroundColor--light-100);
|
--awx-toolbar--BackgroundColor: var(--pf-global--BackgroundColor--light-100);
|
||||||
@@ -86,7 +85,8 @@ const AdditionalControlsWrapper = styled.div`
|
|||||||
class DataListToolbar extends React.Component {
|
class DataListToolbar extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
columns,
|
searchColumns,
|
||||||
|
sortColumns,
|
||||||
showSelectAll,
|
showSelectAll,
|
||||||
isAllSelected,
|
isAllSelected,
|
||||||
isCompact,
|
isCompact,
|
||||||
@@ -96,8 +96,6 @@ class DataListToolbar extends React.Component {
|
|||||||
onCompact,
|
onCompact,
|
||||||
onExpand,
|
onExpand,
|
||||||
onSelectAll,
|
onSelectAll,
|
||||||
sortOrder,
|
|
||||||
sortedColumnKey,
|
|
||||||
additionalControls,
|
additionalControls,
|
||||||
i18n,
|
i18n,
|
||||||
qsConfig,
|
qsConfig,
|
||||||
@@ -124,9 +122,8 @@ class DataListToolbar extends React.Component {
|
|||||||
<ToolbarItem css="flex-grow: 1;">
|
<ToolbarItem css="flex-grow: 1;">
|
||||||
<Search
|
<Search
|
||||||
qsConfig={qsConfig}
|
qsConfig={qsConfig}
|
||||||
columns={columns}
|
columns={searchColumns}
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
sortedColumnKey={sortedColumnKey}
|
|
||||||
/>
|
/>
|
||||||
</ToolbarItem>
|
</ToolbarItem>
|
||||||
<VerticalSeparator />
|
<VerticalSeparator />
|
||||||
@@ -134,10 +131,9 @@ class DataListToolbar extends React.Component {
|
|||||||
<ColumnRight fillWidth={fillWidth}>
|
<ColumnRight fillWidth={fillWidth}>
|
||||||
<ToolbarItem>
|
<ToolbarItem>
|
||||||
<Sort
|
<Sort
|
||||||
columns={columns}
|
qsConfig={qsConfig}
|
||||||
|
columns={sortColumns}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
sortOrder={sortOrder}
|
|
||||||
sortedColumnKey={sortedColumnKey}
|
|
||||||
/>
|
/>
|
||||||
</ToolbarItem>
|
</ToolbarItem>
|
||||||
{showExpandCollapse && (
|
{showExpandCollapse && (
|
||||||
@@ -165,7 +161,8 @@ class DataListToolbar extends React.Component {
|
|||||||
|
|
||||||
DataListToolbar.propTypes = {
|
DataListToolbar.propTypes = {
|
||||||
qsConfig: QSConfig.isRequired,
|
qsConfig: QSConfig.isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
searchColumns: SearchColumns.isRequired,
|
||||||
|
sortColumns: SortColumns.isRequired,
|
||||||
showSelectAll: PropTypes.bool,
|
showSelectAll: PropTypes.bool,
|
||||||
isAllSelected: PropTypes.bool,
|
isAllSelected: PropTypes.bool,
|
||||||
isCompact: PropTypes.bool,
|
isCompact: PropTypes.bool,
|
||||||
@@ -175,8 +172,6 @@ DataListToolbar.propTypes = {
|
|||||||
onSearch: PropTypes.func,
|
onSearch: PropTypes.func,
|
||||||
onSelectAll: PropTypes.func,
|
onSelectAll: PropTypes.func,
|
||||||
onSort: PropTypes.func,
|
onSort: PropTypes.func,
|
||||||
sortOrder: PropTypes.string,
|
|
||||||
sortedColumnKey: PropTypes.string,
|
|
||||||
additionalControls: PropTypes.arrayOf(PropTypes.node),
|
additionalControls: PropTypes.arrayOf(PropTypes.node),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -190,8 +185,6 @@ DataListToolbar.defaultProps = {
|
|||||||
onSearch: null,
|
onSearch: null,
|
||||||
onSelectAll: null,
|
onSelectAll: null,
|
||||||
onSort: null,
|
onSort: null,
|
||||||
sortOrder: 'ascending',
|
|
||||||
sortedColumnKey: 'name',
|
|
||||||
additionalControls: [],
|
additionalControls: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,13 @@ describe('<DataListToolbar />', () => {
|
|||||||
const onSort = jest.fn();
|
const onSort = jest.fn();
|
||||||
const onSelectAll = jest.fn();
|
const onSelectAll = jest.fn();
|
||||||
|
|
||||||
test('it triggers the expected callbacks', () => {
|
test('it triggers the expected callbacks', () => {
|
||||||
const columns = [
|
const searchColumns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isSearchable: true },
|
{ name: 'Name', key: 'name', isDefault: true }
|
||||||
|
];
|
||||||
|
const sortColumns = [
|
||||||
|
{ name: 'Name', key: 'name' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const search = 'button[aria-label="Search submit button"]';
|
const search = 'button[aria-label="Search submit button"]';
|
||||||
const searchTextInput = 'input[aria-label="Search text input"]';
|
const searchTextInput = 'input[aria-label="Search text input"]';
|
||||||
const selectAll = 'input[aria-label="Select all"]';
|
const selectAll = 'input[aria-label="Select all"]';
|
||||||
@@ -38,9 +40,8 @@ describe('<DataListToolbar />', () => {
|
|||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
isAllSelected={false}
|
isAllSelected={false}
|
||||||
showExpandCollapse
|
showExpandCollapse
|
||||||
sortedColumnKey="name"
|
searchColumns={searchColumns}
|
||||||
sortOrder="ascending"
|
sortColumns={sortColumns}
|
||||||
columns={columns}
|
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
onSelectAll={onSelectAll}
|
onSelectAll={onSelectAll}
|
||||||
@@ -74,19 +75,28 @@ describe('<DataListToolbar />', () => {
|
|||||||
const searchDropdownMenuItems =
|
const searchDropdownMenuItems =
|
||||||
'DropdownMenu > ul[aria-labelledby="awx-search"]';
|
'DropdownMenu > ul[aria-labelledby="awx-search"]';
|
||||||
|
|
||||||
const multipleColumns = [
|
const NEW_QS_CONFIG = {
|
||||||
{ name: 'Foo', key: 'foo', isSortable: true, isSearchable: true },
|
namespace: 'organization',
|
||||||
{ name: 'Bar', key: 'bar', isSortable: true, isSearchable: true },
|
dateFields: ['modified', 'created'],
|
||||||
{ name: 'Bakery', key: 'bakery', isSortable: true },
|
defaultParams: { page: 1, page_size: 5, order_by: 'foo' },
|
||||||
{ name: 'Baz', key: 'baz' },
|
integerFields: ['page', 'page_size'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchColumns = [
|
||||||
|
{ name: 'Foo', key: 'foo', isDefault: true },
|
||||||
|
{ name: 'Bar', key: 'bar' }
|
||||||
|
];
|
||||||
|
const sortColumns = [
|
||||||
|
{ name: 'Foo', key: 'foo' },
|
||||||
|
{ name: 'Bar', key: 'bar' },
|
||||||
|
{ name: 'Bakery', key: 'Bakery' }
|
||||||
];
|
];
|
||||||
|
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={NEW_QS_CONFIG}
|
||||||
sortedColumnKey="foo"
|
searchColumns={searchColumns}
|
||||||
sortOrder="ascending"
|
sortColumns={sortColumns}
|
||||||
columns={multipleColumns}
|
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -106,10 +116,9 @@ describe('<DataListToolbar />', () => {
|
|||||||
searchDropdownItems.at(0).simulate('click', mockedSortEvent);
|
searchDropdownItems.at(0).simulate('click', mockedSortEvent);
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={NEW_QS_CONFIG}
|
||||||
sortedColumnKey="foo"
|
searchColumns={searchColumns}
|
||||||
sortOrder="descending"
|
sortColumns={sortColumns}
|
||||||
columns={multipleColumns}
|
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -145,24 +154,57 @@ describe('<DataListToolbar />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it displays correct sort icon', () => {
|
test('it displays correct sort icon', () => {
|
||||||
|
const NUM_QS_CONFIG = {
|
||||||
|
namespace: 'organization',
|
||||||
|
dateFields: ['modified', 'created'],
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: 'id' },
|
||||||
|
integerFields: ['page', 'page_size', 'id'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const NUM_DESC_QS_CONFIG = {
|
||||||
|
namespace: 'organization',
|
||||||
|
dateFields: ['modified', 'created'],
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: '-id' },
|
||||||
|
integerFields: ['page', 'page_size', 'id'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const ALPH_QS_CONFIG = {
|
||||||
|
namespace: 'organization',
|
||||||
|
dateFields: ['modified', 'created'],
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
|
||||||
|
integerFields: ['page', 'page_size', 'id'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const ALPH_DESC_QS_CONFIG = {
|
||||||
|
namespace: 'organization',
|
||||||
|
dateFields: ['modified', 'created'],
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: '-name' },
|
||||||
|
integerFields: ['page', 'page_size', 'id'],
|
||||||
|
};
|
||||||
|
|
||||||
const downNumericIconSelector = 'SortNumericDownIcon';
|
const downNumericIconSelector = 'SortNumericDownIcon';
|
||||||
const upNumericIconSelector = 'SortNumericUpIcon';
|
const upNumericIconSelector = 'SortNumericUpIcon';
|
||||||
const downAlphaIconSelector = 'SortAlphaDownIcon';
|
const downAlphaIconSelector = 'SortAlphaDownIcon';
|
||||||
const upAlphaIconSelector = 'SortAlphaUpIcon';
|
const upAlphaIconSelector = 'SortAlphaUpIcon';
|
||||||
|
|
||||||
const numericColumns = [
|
const numericColumns = [
|
||||||
{ name: 'ID', key: 'id', isSortable: true, isNumeric: true },
|
{ name: 'ID', key: 'id', isDefault: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
const alphaColumns = [
|
const alphaColumns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isNumeric: false },
|
{ name: 'Name', key: 'name', isDefault: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const searchColumns = [
|
||||||
|
{ name: 'Name', key: 'name', isDefault: true },
|
||||||
|
{ name: 'ID', key: 'id' }
|
||||||
];
|
];
|
||||||
|
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={NUM_DESC_QS_CONFIG}
|
||||||
sortedColumnKey="id"
|
searchColumns={searchColumns}
|
||||||
sortOrder="descending"
|
sortColumns={numericColumns}
|
||||||
columns={numericColumns}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -171,10 +213,9 @@ describe('<DataListToolbar />', () => {
|
|||||||
|
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={NUM_QS_CONFIG}
|
||||||
sortedColumnKey="id"
|
searchColumns={searchColumns}
|
||||||
sortOrder="ascending"
|
sortColumns={numericColumns}
|
||||||
columns={numericColumns}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -183,10 +224,9 @@ describe('<DataListToolbar />', () => {
|
|||||||
|
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={ALPH_DESC_QS_CONFIG}
|
||||||
sortedColumnKey="name"
|
searchColumns={searchColumns}
|
||||||
sortOrder="descending"
|
sortColumns={alphaColumns}
|
||||||
columns={alphaColumns}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -195,10 +235,9 @@ describe('<DataListToolbar />', () => {
|
|||||||
|
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={ALPH_QS_CONFIG}
|
||||||
sortedColumnKey="name"
|
searchColumns={searchColumns}
|
||||||
sortOrder="ascending"
|
sortColumns={alphaColumns}
|
||||||
columns={alphaColumns}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -207,14 +246,18 @@ describe('<DataListToolbar />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should render additionalControls', () => {
|
test('should render additionalControls', () => {
|
||||||
const columns = [
|
const searchColumns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isSearchable: true },
|
{ name: 'Name', key: 'name', isDefault: true }
|
||||||
|
];
|
||||||
|
const sortColumns = [
|
||||||
|
{ name: 'Name', key: 'name', isDefault: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
columns={columns}
|
searchColumns={searchColumns}
|
||||||
|
sortColumns={sortColumns}
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
onSelectAll={onSelectAll}
|
onSelectAll={onSelectAll}
|
||||||
@@ -232,18 +275,19 @@ describe('<DataListToolbar />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it triggers the expected callbacks', () => {
|
test('it triggers the expected callbacks', () => {
|
||||||
const columns = [
|
const searchColumns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isSearchable: true },
|
{ name: 'Name', key: 'name', isDefault: true }
|
||||||
|
];
|
||||||
|
const sortColumns = [
|
||||||
|
{ name: 'Name', key: 'name' }
|
||||||
];
|
];
|
||||||
|
|
||||||
toolbar = mountWithContexts(
|
toolbar = mountWithContexts(
|
||||||
<DataListToolbar
|
<DataListToolbar
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
isAllSelected
|
isAllSelected
|
||||||
showExpandCollapse
|
showExpandCollapse
|
||||||
sortedColumnKey="name"
|
searchColumns={searchColumns}
|
||||||
sortOrder="ascending"
|
sortColumns={sortColumns}
|
||||||
columns={columns}
|
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
onSelectAll={onSelectAll}
|
onSelectAll={onSelectAll}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes, { arrayOf, shape, string, bool } from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import DataListToolbar from '@components/DataListToolbar';
|
import DataListToolbar from '@components/DataListToolbar';
|
||||||
import FilterTags from '@components/FilterTags';
|
import FilterTags from '@components/FilterTags';
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ import {
|
|||||||
replaceParams,
|
replaceParams,
|
||||||
removeParams,
|
removeParams,
|
||||||
} from '@util/qs';
|
} from '@util/qs';
|
||||||
import { QSConfig } from '@types';
|
import { QSConfig, SearchColumns, SortColumns } from '@types';
|
||||||
|
|
||||||
const EmptyStateControlsWrapper = styled.div`
|
const EmptyStateControlsWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -36,15 +35,6 @@ class ListHeader extends React.Component {
|
|||||||
this.handleRemoveAll = this.handleRemoveAll.bind(this);
|
this.handleRemoveAll = this.handleRemoveAll.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSortOrder() {
|
|
||||||
const { qsConfig, location } = this.props;
|
|
||||||
const queryParams = parseQueryString(qsConfig, location.search);
|
|
||||||
if (queryParams.order_by && queryParams.order_by.startsWith('-')) {
|
|
||||||
return [queryParams.order_by.substr(1), 'descending'];
|
|
||||||
}
|
|
||||||
return [queryParams.order_by, 'ascending'];
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSearch(key, value) {
|
handleSearch(key, value) {
|
||||||
const { location, qsConfig } = this.props;
|
const { location, qsConfig } = this.props;
|
||||||
const oldParams = parseQueryString(qsConfig, location.search);
|
const oldParams = parseQueryString(qsConfig, location.search);
|
||||||
@@ -83,12 +73,12 @@ class ListHeader extends React.Component {
|
|||||||
const {
|
const {
|
||||||
emptyStateControls,
|
emptyStateControls,
|
||||||
itemCount,
|
itemCount,
|
||||||
columns,
|
searchColumns,
|
||||||
|
sortColumns,
|
||||||
renderToolbar,
|
renderToolbar,
|
||||||
qsConfig,
|
qsConfig,
|
||||||
location,
|
location,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const [orderBy, sortOrder] = this.getSortOrder();
|
|
||||||
const params = parseQueryString(qsConfig, location.search);
|
const params = parseQueryString(qsConfig, location.search);
|
||||||
const isEmpty = itemCount === 0 && Object.keys(params).length === 0;
|
const isEmpty = itemCount === 0 && Object.keys(params).length === 0;
|
||||||
return (
|
return (
|
||||||
@@ -108,9 +98,8 @@ class ListHeader extends React.Component {
|
|||||||
) : (
|
) : (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{renderToolbar({
|
{renderToolbar({
|
||||||
sortedColumnKey: orderBy,
|
searchColumns,
|
||||||
sortOrder,
|
sortColumns,
|
||||||
columns,
|
|
||||||
onSearch: this.handleSearch,
|
onSearch: this.handleSearch,
|
||||||
onSort: this.handleSort,
|
onSort: this.handleSort,
|
||||||
qsConfig,
|
qsConfig,
|
||||||
@@ -131,14 +120,8 @@ class ListHeader extends React.Component {
|
|||||||
ListHeader.propTypes = {
|
ListHeader.propTypes = {
|
||||||
itemCount: PropTypes.number.isRequired,
|
itemCount: PropTypes.number.isRequired,
|
||||||
qsConfig: QSConfig.isRequired,
|
qsConfig: QSConfig.isRequired,
|
||||||
columns: arrayOf(
|
searchColumns: SearchColumns.isRequired,
|
||||||
shape({
|
sortColumns: SortColumns.isRequired,
|
||||||
name: string.isRequired,
|
|
||||||
key: string.isRequired,
|
|
||||||
isSortable: bool,
|
|
||||||
isSearchable: bool,
|
|
||||||
})
|
|
||||||
).isRequired,
|
|
||||||
renderToolbar: PropTypes.func,
|
renderToolbar: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import ListHeader from './ListHeader';
|
|||||||
describe('ListHeader', () => {
|
describe('ListHeader', () => {
|
||||||
const qsConfig = {
|
const qsConfig = {
|
||||||
namespace: 'item',
|
namespace: 'item',
|
||||||
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
|
defaultParams: { page: 1, page_size: 5, order_by: 'foo' },
|
||||||
integerFields: [],
|
integerFields: [],
|
||||||
};
|
};
|
||||||
const renderToolbarFn = jest.fn();
|
const renderToolbarFn = jest.fn();
|
||||||
@@ -17,8 +17,11 @@ describe('ListHeader', () => {
|
|||||||
<ListHeader
|
<ListHeader
|
||||||
itemCount={50}
|
itemCount={50}
|
||||||
qsConfig={qsConfig}
|
qsConfig={qsConfig}
|
||||||
columns={[
|
searchColumns={[
|
||||||
{ name: 'foo', key: 'foo', isSearchable: true, isSortable: true },
|
{ name: 'foo', key: 'foo', isDefault: true},
|
||||||
|
]}
|
||||||
|
sortColumns={[
|
||||||
|
{ name: 'foo', key: 'foo', isDefault: true},
|
||||||
]}
|
]}
|
||||||
renderToolbar={renderToolbarFn}
|
renderToolbar={renderToolbarFn}
|
||||||
/>
|
/>
|
||||||
@@ -35,26 +38,20 @@ describe('ListHeader', () => {
|
|||||||
<ListHeader
|
<ListHeader
|
||||||
itemCount={7}
|
itemCount={7}
|
||||||
qsConfig={qsConfig}
|
qsConfig={qsConfig}
|
||||||
columns={[
|
searchColumns={[
|
||||||
{ name: 'name', key: 'name', isSearchable: true, isSortable: true },
|
{ name: 'foo', key: 'foo', isDefault: true},
|
||||||
|
]}
|
||||||
|
sortColumns={[
|
||||||
|
{ name: 'foo', key: 'foo', isDefault: true},
|
||||||
]}
|
]}
|
||||||
/>,
|
/>,
|
||||||
{ context: { router: { history } } }
|
{ context: { router: { history } } }
|
||||||
);
|
);
|
||||||
|
|
||||||
const toolbar = wrapper.find('DataListToolbar');
|
const toolbar = wrapper.find('DataListToolbar');
|
||||||
expect(toolbar.prop('sortedColumnKey')).toEqual('name');
|
toolbar.prop('onSort')('foo', 'descending');
|
||||||
expect(toolbar.prop('sortOrder')).toEqual('ascending');
|
expect(history.location.search).toEqual('?item.order_by=-foo');
|
||||||
toolbar.prop('onSort')('name', 'descending');
|
toolbar.prop('onSort')('foo', 'ascending');
|
||||||
expect(history.location.search).toEqual('?item.order_by=-name');
|
|
||||||
await sleep(0);
|
|
||||||
wrapper.update();
|
|
||||||
|
|
||||||
expect(toolbar.prop('sortedColumnKey')).toEqual('name');
|
|
||||||
// TODO: this assertion required updating queryParams prop. Consider
|
|
||||||
// fixing after #147 is done:
|
|
||||||
// expect(toolbar.prop('sortOrder')).toEqual('descending');
|
|
||||||
toolbar.prop('onSort')('name', 'ascending');
|
|
||||||
// since order_by = name is the default, that should be strip out of the search
|
// since order_by = name is the default, that should be strip out of the search
|
||||||
expect(history.location.search).toEqual('');
|
expect(history.location.search).toEqual('');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -64,26 +64,24 @@ function InstanceGroupsLookup(props) {
|
|||||||
value={state.selectedItems}
|
value={state.selectedItems}
|
||||||
options={instanceGroups}
|
options={instanceGroups}
|
||||||
optionCount={count}
|
optionCount={count}
|
||||||
columns={[
|
searchColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: false,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: false,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
sortColumns={[{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name'
|
||||||
|
}]}
|
||||||
multiple={state.multiple}
|
multiple={state.multiple}
|
||||||
header={i18n._(t`Instance Groups`)}
|
header={i18n._(t`Instance Groups`)}
|
||||||
name="instanceGroups"
|
name="instanceGroups"
|
||||||
|
|||||||
@@ -68,21 +68,24 @@ function InventoryLookup({
|
|||||||
value={state.selectedItems}
|
value={state.selectedItems}
|
||||||
options={inventories}
|
options={inventories}
|
||||||
optionCount={count}
|
optionCount={count}
|
||||||
columns={[
|
searchColumns={[
|
||||||
{ name: i18n._(t`Name`), key: 'name', isSortable: true },
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: false,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: false,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
sortColumns={[{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name'
|
||||||
|
}]}
|
||||||
multiple={state.multiple}
|
multiple={state.multiple}
|
||||||
header={i18n._(t`Inventory`)}
|
header={i18n._(t`Inventory`)}
|
||||||
name="inventory"
|
name="inventory"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import SelectedList from '../../SelectedList';
|
|||||||
import PaginatedDataList from '../../PaginatedDataList';
|
import PaginatedDataList from '../../PaginatedDataList';
|
||||||
import CheckboxListItem from '../../CheckboxListItem';
|
import CheckboxListItem from '../../CheckboxListItem';
|
||||||
import DataListToolbar from '../../DataListToolbar';
|
import DataListToolbar from '../../DataListToolbar';
|
||||||
import { QSConfig } from '@types';
|
import { QSConfig, SearchColumns, SortColumns } from '@types';
|
||||||
|
|
||||||
function OptionsList({
|
function OptionsList({
|
||||||
value,
|
value,
|
||||||
@@ -80,7 +80,8 @@ OptionsList.propTypes = {
|
|||||||
value: arrayOf(Item).isRequired,
|
value: arrayOf(Item).isRequired,
|
||||||
options: arrayOf(Item).isRequired,
|
options: arrayOf(Item).isRequired,
|
||||||
optionCount: number.isRequired,
|
optionCount: number.isRequired,
|
||||||
columns: arrayOf(shape({})),
|
searchColumns: SearchColumns.isRequired,
|
||||||
|
sortColumns: SortColumns.isRequired,
|
||||||
multiple: bool,
|
multiple: bool,
|
||||||
qsConfig: QSConfig.isRequired,
|
qsConfig: QSConfig.isRequired,
|
||||||
selectItem: func.isRequired,
|
selectItem: func.isRequired,
|
||||||
@@ -90,7 +91,6 @@ OptionsList.propTypes = {
|
|||||||
OptionsList.defaultProps = {
|
OptionsList.defaultProps = {
|
||||||
multiple: false,
|
multiple: false,
|
||||||
renderItemChip: null,
|
renderItemChip: null,
|
||||||
columns: [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withI18n()(OptionsList);
|
export default withI18n()(OptionsList);
|
||||||
|
|||||||
@@ -18,12 +18,6 @@ const QS_CONFIG = getQSConfig('notification', {
|
|||||||
order_by: 'name',
|
order_by: 'name',
|
||||||
});
|
});
|
||||||
|
|
||||||
const COLUMNS = [
|
|
||||||
{ key: 'name', name: 'Name', isSortable: true, isSearchable: true },
|
|
||||||
{ key: 'modified', name: 'Modified', isSortable: true, isNumeric: true },
|
|
||||||
{ key: 'created', name: 'Created', isSortable: true, isNumeric: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
class NotificationList extends Component {
|
class NotificationList extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -204,7 +198,35 @@ class NotificationList extends Component {
|
|||||||
itemCount={itemCount}
|
itemCount={itemCount}
|
||||||
pluralizedItemName={i18n._(t`Notifications`)}
|
pluralizedItemName={i18n._(t`Notifications`)}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
toolbarColumns={COLUMNS}
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
renderItem={notification => (
|
renderItem={notification => (
|
||||||
<NotificationListItem
|
<NotificationListItem
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes, { arrayOf, shape, string, bool } from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { DataList } from '@patternfly/react-core';
|
import { DataList } from '@patternfly/react-core';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
replaceParams,
|
replaceParams,
|
||||||
} from '@util/qs';
|
} from '@util/qs';
|
||||||
|
|
||||||
import { QSConfig } from '@types';
|
import { QSConfig, SearchColumns, SortColumns } from '@types';
|
||||||
|
|
||||||
import PaginatedDataListItem from './PaginatedDataListItem';
|
import PaginatedDataListItem from './PaginatedDataListItem';
|
||||||
|
|
||||||
@@ -66,21 +66,29 @@ class PaginatedDataList extends React.Component {
|
|||||||
itemCount,
|
itemCount,
|
||||||
qsConfig,
|
qsConfig,
|
||||||
renderItem,
|
renderItem,
|
||||||
toolbarColumns,
|
toolbarSearchColumns,
|
||||||
|
toolbarSortColumns,
|
||||||
pluralizedItemName,
|
pluralizedItemName,
|
||||||
showPageSizeOptions,
|
showPageSizeOptions,
|
||||||
location,
|
location,
|
||||||
i18n,
|
i18n,
|
||||||
renderToolbar,
|
renderToolbar,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const columns = toolbarColumns.length
|
const searchColumns = toolbarSearchColumns.length
|
||||||
? toolbarColumns
|
? toolbarSearchColumns
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const sortColumns = toolbarSortColumns.length
|
||||||
|
? toolbarSortColumns
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const queryParams = parseQueryString(qsConfig, location.search);
|
const queryParams = parseQueryString(qsConfig, location.search);
|
||||||
@@ -117,7 +125,8 @@ class PaginatedDataList extends React.Component {
|
|||||||
itemCount={itemCount}
|
itemCount={itemCount}
|
||||||
renderToolbar={renderToolbar}
|
renderToolbar={renderToolbar}
|
||||||
emptyStateControls={emptyStateControls}
|
emptyStateControls={emptyStateControls}
|
||||||
columns={columns}
|
searchColumns={searchColumns}
|
||||||
|
sortColumns={sortColumns}
|
||||||
qsConfig={qsConfig}
|
qsConfig={qsConfig}
|
||||||
/>
|
/>
|
||||||
{Content}
|
{Content}
|
||||||
@@ -158,13 +167,8 @@ PaginatedDataList.propTypes = {
|
|||||||
pluralizedItemName: PropTypes.string,
|
pluralizedItemName: PropTypes.string,
|
||||||
qsConfig: QSConfig.isRequired,
|
qsConfig: QSConfig.isRequired,
|
||||||
renderItem: PropTypes.func,
|
renderItem: PropTypes.func,
|
||||||
toolbarColumns: arrayOf(
|
toolbarSearchColumns: SearchColumns,
|
||||||
shape({
|
toolbarSortColumns: SortColumns,
|
||||||
name: string.isRequired,
|
|
||||||
key: string.isRequired,
|
|
||||||
isSortable: bool,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
showPageSizeOptions: PropTypes.bool,
|
showPageSizeOptions: PropTypes.bool,
|
||||||
renderToolbar: PropTypes.func,
|
renderToolbar: PropTypes.func,
|
||||||
hasContentLoading: PropTypes.bool,
|
hasContentLoading: PropTypes.bool,
|
||||||
@@ -175,7 +179,8 @@ PaginatedDataList.propTypes = {
|
|||||||
PaginatedDataList.defaultProps = {
|
PaginatedDataList.defaultProps = {
|
||||||
hasContentLoading: false,
|
hasContentLoading: false,
|
||||||
contentError: null,
|
contentError: null,
|
||||||
toolbarColumns: [],
|
toolbarSearchColumns: [],
|
||||||
|
toolbarSortColumns: [],
|
||||||
pluralizedItemName: 'Items',
|
pluralizedItemName: 'Items',
|
||||||
showPageSizeOptions: true,
|
showPageSizeOptions: true,
|
||||||
renderItem: item => <PaginatedDataListItem key={item.id} item={item} />,
|
renderItem: item => <PaginatedDataListItem key={item.id} item={item} />,
|
||||||
|
|||||||
@@ -162,24 +162,34 @@ class ResourceAccessList extends React.Component {
|
|||||||
itemCount={itemCount}
|
itemCount={itemCount}
|
||||||
pluralizedItemName="Roles"
|
pluralizedItemName="Roles"
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
{
|
|
||||||
name: i18n._(t`First Name`),
|
|
||||||
key: 'first_name',
|
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: i18n._(t`Username`),
|
name: i18n._(t`Username`),
|
||||||
key: 'username',
|
key: 'username',
|
||||||
isSortable: true,
|
isDefault: true
|
||||||
isSearchable: true,
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`First Name`),
|
||||||
|
key: 'first_name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Last Name`),
|
||||||
|
key: 'last_name',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Username`),
|
||||||
|
key: 'username',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`First Name`),
|
||||||
|
key: 'first_name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Last Name`),
|
name: i18n._(t`Last Name`),
|
||||||
key: 'last_name',
|
key: 'last_name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { SearchIcon } from '@patternfly/react-icons';
|
import { SearchIcon } from '@patternfly/react-icons';
|
||||||
|
|
||||||
import { QSConfig } from '@types';
|
import { QSConfig, SearchColumns } from '@types';
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@@ -82,10 +82,11 @@ class Search extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const { sortedColumnKey } = this.props;
|
const { columns } = this.props;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isSearchDropdownOpen: false,
|
isSearchDropdownOpen: false,
|
||||||
searchKey: sortedColumnKey,
|
searchKey: columns.find(col => col.isDefault).key,
|
||||||
searchValue: '',
|
searchValue: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -142,7 +143,7 @@ class Search extends React.Component {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const searchDropdownItems = columns
|
const searchDropdownItems = columns
|
||||||
.filter(({ key, isSearchable }) => isSearchable && key !== searchKey)
|
.filter(({ key }) => key !== searchKey)
|
||||||
.map(({ key, name }) => (
|
.map(({ key, name }) => (
|
||||||
<DropdownItem key={key} component="button">
|
<DropdownItem key={key} component="button">
|
||||||
{name}
|
{name}
|
||||||
@@ -214,14 +215,12 @@ class Search extends React.Component {
|
|||||||
|
|
||||||
Search.propTypes = {
|
Search.propTypes = {
|
||||||
qsConfig: QSConfig.isRequired,
|
qsConfig: QSConfig.isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: SearchColumns.isRequired,
|
||||||
onSearch: PropTypes.func,
|
onSearch: PropTypes.func
|
||||||
sortedColumnKey: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Search.defaultProps = {
|
Search.defaultProps = {
|
||||||
onSearch: null,
|
onSearch: null,
|
||||||
sortedColumnKey: 'name',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withI18n()(Search);
|
export default withI18n()(Search);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ describe('<Search />', () => {
|
|||||||
const QS_CONFIG = {
|
const QS_CONFIG = {
|
||||||
namespace: 'organization',
|
namespace: 'organization',
|
||||||
dateFields: ['modified', 'created'],
|
dateFields: ['modified', 'created'],
|
||||||
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
|
defaulkntParams: { page: 1, page_size: 5, order_by: 'name' },
|
||||||
integerFields: ['page', 'page_size'],
|
integerFields: ['page', 'page_size'],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ describe('<Search />', () => {
|
|||||||
|
|
||||||
test('it triggers the expected callbacks', () => {
|
test('it triggers the expected callbacks', () => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isSearchable: true },
|
{ name: 'Name', key: 'name', isDefault: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
const searchBtn = 'button[aria-label="Search submit button"]';
|
const searchBtn = 'button[aria-label="Search submit button"]';
|
||||||
@@ -31,7 +31,6 @@ describe('<Search />', () => {
|
|||||||
search = mountWithContexts(
|
search = mountWithContexts(
|
||||||
<Search
|
<Search
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
sortedColumnKey="name"
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
/>
|
/>
|
||||||
@@ -47,13 +46,12 @@ describe('<Search />', () => {
|
|||||||
|
|
||||||
test('handleDropdownToggle properly updates state', async () => {
|
test('handleDropdownToggle properly updates state', async () => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isSearchable: true },
|
{ name: 'Name', key: 'name', isDefault: true }
|
||||||
];
|
];
|
||||||
const onSearch = jest.fn();
|
const onSearch = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<Search
|
<Search
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
sortedColumnKey="name"
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
/>
|
/>
|
||||||
@@ -65,19 +63,13 @@ describe('<Search />', () => {
|
|||||||
|
|
||||||
test('handleDropdownSelect properly updates state', async () => {
|
test('handleDropdownSelect properly updates state', async () => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isSearchable: true },
|
{ name: 'Name', key: 'name', isDefault: true },
|
||||||
{
|
{ name: 'Description', key: 'description' },
|
||||||
name: 'Description',
|
|
||||||
key: 'description',
|
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
const onSearch = jest.fn();
|
const onSearch = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<Search
|
<Search
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
sortedColumnKey="name"
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -19,6 +20,11 @@ import {
|
|||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseQueryString
|
||||||
|
} from '@util/qs';
|
||||||
|
import { SortColumns, QSConfig } from '@types';
|
||||||
|
|
||||||
const Dropdown = styled(PFDropdown)`
|
const Dropdown = styled(PFDropdown)`
|
||||||
&&& {
|
&&& {
|
||||||
> button {
|
> button {
|
||||||
@@ -68,8 +74,31 @@ class Sort extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
let sortKey;
|
||||||
|
let sortOrder;
|
||||||
|
let isNumeric;
|
||||||
|
|
||||||
|
const { qsConfig, location } = this.props;
|
||||||
|
const queryParams = parseQueryString(qsConfig, location.search);
|
||||||
|
if (queryParams.order_by && queryParams.order_by.startsWith('-')) {
|
||||||
|
sortKey = queryParams.order_by.substr(1);
|
||||||
|
sortOrder = 'descending';
|
||||||
|
} else if (queryParams.order_by) {
|
||||||
|
sortKey = queryParams.order_by;
|
||||||
|
sortOrder = 'ascending';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qsConfig.integerFields.filter(field => field === sortKey).length) {
|
||||||
|
isNumeric = true;
|
||||||
|
} else {
|
||||||
|
isNumeric = false;
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isSortDropdownOpen: false,
|
isSortDropdownOpen: false,
|
||||||
|
sortKey,
|
||||||
|
sortOrder,
|
||||||
|
isNumeric
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleDropdownToggle = this.handleDropdownToggle.bind(this);
|
this.handleDropdownToggle = this.handleDropdownToggle.bind(this);
|
||||||
@@ -82,34 +111,44 @@ class Sort extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDropdownSelect({ target }) {
|
handleDropdownSelect({ target }) {
|
||||||
const { columns, onSort, sortOrder } = this.props;
|
const { columns, onSort, qsConfig } = this.props;
|
||||||
|
const { sortOrder } = this.state;
|
||||||
const { innerText } = target;
|
const { innerText } = target;
|
||||||
|
|
||||||
const [{ key: searchKey }] = columns.filter(
|
const [{ key: sortKey }] = columns.filter(
|
||||||
({ name }) => name === innerText
|
({ name }) => name === innerText
|
||||||
);
|
);
|
||||||
|
|
||||||
this.setState({ isSortDropdownOpen: false });
|
let isNumeric;
|
||||||
onSort(searchKey, sortOrder);
|
|
||||||
|
if (qsConfig.integerFields.filter(field => field === sortKey).length) {
|
||||||
|
isNumeric = true;
|
||||||
|
} else {
|
||||||
|
isNumeric = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ isSortDropdownOpen: false, sortKey, isNumeric });
|
||||||
|
onSort(sortKey, sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSort() {
|
handleSort() {
|
||||||
const { onSort, sortedColumnKey, sortOrder } = this.props;
|
const { onSort } = this.props;
|
||||||
|
const { sortKey, sortOrder } = this.state;
|
||||||
const newSortOrder = sortOrder === 'ascending' ? 'descending' : 'ascending';
|
const newSortOrder = sortOrder === 'ascending' ? 'descending' : 'ascending';
|
||||||
|
this.setState({ sortOrder: newSortOrder });
|
||||||
onSort(sortedColumnKey, newSortOrder);
|
onSort(sortKey, newSortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { up } = DropdownPosition;
|
const { up } = DropdownPosition;
|
||||||
const { columns, sortedColumnKey, sortOrder, i18n } = this.props;
|
const { columns, i18n } = this.props;
|
||||||
const { isSortDropdownOpen } = this.state;
|
const { isSortDropdownOpen, sortKey, sortOrder, isNumeric } = this.state;
|
||||||
const [{ name: sortedColumnName, isNumeric }] = columns.filter(
|
const [{ name: sortedColumnName }] = columns.filter(
|
||||||
({ key }) => key === sortedColumnKey
|
({ key }) => key === sortKey
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortDropdownItems = columns
|
const sortDropdownItems = columns
|
||||||
.filter(({ key, isSortable }) => isSortable && key !== sortedColumnKey)
|
.filter(({ key }) => key !== sortKey)
|
||||||
.map(({ key, name }) => (
|
.map(({ key, name }) => (
|
||||||
<DropdownItem key={key} component="button">
|
<DropdownItem key={key} component="button">
|
||||||
{name}
|
{name}
|
||||||
@@ -168,16 +207,13 @@ class Sort extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Sort.propTypes = {
|
Sort.propTypes = {
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
qsConfig: QSConfig.isRequired,
|
||||||
onSort: PropTypes.func,
|
columns: SortColumns.isRequired,
|
||||||
sortOrder: PropTypes.string,
|
onSort: PropTypes.func
|
||||||
sortedColumnKey: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Sort.defaultProps = {
|
Sort.defaultProps = {
|
||||||
onSort: null,
|
onSort: null
|
||||||
sortOrder: 'ascending',
|
|
||||||
sortedColumnKey: 'name',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withI18n()(Sort);
|
export default withI18n()(withRouter(Sort));
|
||||||
|
|||||||
@@ -12,8 +12,17 @@ describe('<Sort />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it triggers the expected callbacks', () => {
|
test('it triggers the expected callbacks', () => {
|
||||||
|
const qsConfig = {
|
||||||
|
namespace: 'item',
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
|
||||||
|
integerFields: ['page', 'page_size'],
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isSearchable: true },
|
{
|
||||||
|
name: 'Name',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const sortBtn = 'button[aria-label="Sort"]';
|
const sortBtn = 'button[aria-label="Sort"]';
|
||||||
@@ -22,8 +31,7 @@ describe('<Sort />', () => {
|
|||||||
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="name"
|
qsConfig={qsConfig}
|
||||||
sortOrder="ascending"
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
@@ -36,20 +44,33 @@ describe('<Sort />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('onSort properly passes back descending when ascending was passed as prop', () => {
|
test('onSort properly passes back descending when ascending was passed as prop', () => {
|
||||||
const multipleColumns = [
|
const qsConfig = {
|
||||||
{ name: 'Foo', key: 'foo', isSortable: true },
|
namespace: 'item',
|
||||||
{ name: 'Bar', key: 'bar', isSortable: true },
|
defaultParams: { page: 1, page_size: 5, order_by: 'foo' },
|
||||||
{ name: 'Bakery', key: 'bakery', isSortable: true },
|
integerFields: ['page', 'page_size'],
|
||||||
{ name: 'Baz', key: 'baz' },
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
key: 'foo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
key: 'bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bakery',
|
||||||
|
key: 'bakery',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const onSort = jest.fn();
|
const onSort = jest.fn();
|
||||||
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="foo"
|
qsConfig={qsConfig}
|
||||||
sortOrder="ascending"
|
columns={columns}
|
||||||
columns={multipleColumns}
|
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
).find('Sort');
|
).find('Sort');
|
||||||
@@ -60,20 +81,33 @@ describe('<Sort />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('onSort properly passes back ascending when descending was passed as prop', () => {
|
test('onSort properly passes back ascending when descending was passed as prop', () => {
|
||||||
const multipleColumns = [
|
const qsConfig = {
|
||||||
{ name: 'Foo', key: 'foo', isSortable: true },
|
namespace: 'item',
|
||||||
{ name: 'Bar', key: 'bar', isSortable: true },
|
defaultParams: { page: 1, page_size: 5, order_by: '-foo' },
|
||||||
{ name: 'Bakery', key: 'bakery', isSortable: true },
|
integerFields: ['page', 'page_size'],
|
||||||
{ name: 'Baz', key: 'baz' },
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
key: 'foo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
key: 'bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bakery',
|
||||||
|
key: 'bakery',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const onSort = jest.fn();
|
const onSort = jest.fn();
|
||||||
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="foo"
|
qsConfig={qsConfig}
|
||||||
sortOrder="descending"
|
columns={columns}
|
||||||
columns={multipleColumns}
|
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
).find('Sort');
|
).find('Sort');
|
||||||
@@ -84,20 +118,33 @@ describe('<Sort />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Changing dropdown correctly passes back new sort key', () => {
|
test('Changing dropdown correctly passes back new sort key', () => {
|
||||||
const multipleColumns = [
|
const qsConfig = {
|
||||||
{ name: 'Foo', key: 'foo', isSortable: true },
|
namespace: 'item',
|
||||||
{ name: 'Bar', key: 'bar', isSortable: true },
|
defaultParams: { page: 1, page_size: 5, order_by: 'foo' },
|
||||||
{ name: 'Bakery', key: 'bakery', isSortable: true },
|
integerFields: ['page', 'page_size'],
|
||||||
{ name: 'Baz', key: 'baz' },
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
key: 'foo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
key: 'bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bakery',
|
||||||
|
key: 'bakery',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const onSort = jest.fn();
|
const onSort = jest.fn();
|
||||||
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="foo"
|
qsConfig={qsConfig}
|
||||||
sortOrder="ascending"
|
columns={columns}
|
||||||
columns={multipleColumns}
|
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
).find('Sort');
|
).find('Sort');
|
||||||
@@ -107,20 +154,33 @@ describe('<Sort />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Opening dropdown correctly updates state', () => {
|
test('Opening dropdown correctly updates state', () => {
|
||||||
const multipleColumns = [
|
const qsConfig = {
|
||||||
{ name: 'Foo', key: 'foo', isSortable: true },
|
namespace: 'item',
|
||||||
{ name: 'Bar', key: 'bar', isSortable: true },
|
defaultParams: { page: 1, page_size: 5, order_by: 'foo' },
|
||||||
{ name: 'Bakery', key: 'bakery', isSortable: true },
|
integerFields: ['page', 'page_size'],
|
||||||
{ name: 'Baz', key: 'baz' },
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Foo',
|
||||||
|
key: 'foo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bar',
|
||||||
|
key: 'bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Bakery',
|
||||||
|
key: 'bakery',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const onSort = jest.fn();
|
const onSort = jest.fn();
|
||||||
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="foo"
|
qsConfig={qsConfig}
|
||||||
sortOrder="ascending"
|
columns={columns}
|
||||||
columns={multipleColumns}
|
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
).find('Sort');
|
).find('Sort');
|
||||||
@@ -135,18 +195,38 @@ describe('<Sort />', () => {
|
|||||||
const downAlphaIconSelector = 'SortAlphaDownIcon';
|
const downAlphaIconSelector = 'SortAlphaDownIcon';
|
||||||
const upAlphaIconSelector = 'SortAlphaUpIcon';
|
const upAlphaIconSelector = 'SortAlphaUpIcon';
|
||||||
|
|
||||||
|
const qsConfigNumDown = {
|
||||||
|
namespace: 'item',
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: '-id' },
|
||||||
|
integerFields: ['page', 'page_size', 'id'],
|
||||||
|
};
|
||||||
|
const qsConfigNumUp = {
|
||||||
|
namespace: 'item',
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: 'id' },
|
||||||
|
integerFields: ['page', 'page_size', 'id'],
|
||||||
|
};
|
||||||
|
const qsConfigAlphaDown = {
|
||||||
|
namespace: 'item',
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: '-name' },
|
||||||
|
integerFields: ['page', 'page_size'],
|
||||||
|
};
|
||||||
|
const qsConfigAlphaUp = {
|
||||||
|
namespace: 'item',
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
|
||||||
|
integerFields: ['page', 'page_size'],
|
||||||
|
};
|
||||||
|
|
||||||
const numericColumns = [
|
const numericColumns = [
|
||||||
{ name: 'ID', key: 'id', isSortable: true, isNumeric: true },
|
{ name: 'ID', key: 'id' },
|
||||||
];
|
];
|
||||||
const alphaColumns = [
|
const alphaColumns = [
|
||||||
{ name: 'Name', key: 'name', isSortable: true, isNumeric: false },
|
{ name: 'Name', key: 'name' },
|
||||||
];
|
];
|
||||||
const onSort = jest.fn();
|
const onSort = jest.fn();
|
||||||
|
|
||||||
sort = mountWithContexts(
|
sort = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="id"
|
qsConfig={qsConfigNumDown}
|
||||||
sortOrder="descending"
|
|
||||||
columns={numericColumns}
|
columns={numericColumns}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
@@ -157,8 +237,7 @@ describe('<Sort />', () => {
|
|||||||
|
|
||||||
sort = mountWithContexts(
|
sort = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="id"
|
qsConfig={qsConfigNumUp}
|
||||||
sortOrder="ascending"
|
|
||||||
columns={numericColumns}
|
columns={numericColumns}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
@@ -169,8 +248,7 @@ describe('<Sort />', () => {
|
|||||||
|
|
||||||
sort = mountWithContexts(
|
sort = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="name"
|
qsConfig={qsConfigAlphaDown}
|
||||||
sortOrder="descending"
|
|
||||||
columns={alphaColumns}
|
columns={alphaColumns}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
@@ -181,8 +259,7 @@ describe('<Sort />', () => {
|
|||||||
|
|
||||||
sort = mountWithContexts(
|
sort = mountWithContexts(
|
||||||
<Sort
|
<Sort
|
||||||
sortedColumnKey="name"
|
qsConfig={qsConfigAlphaUp}
|
||||||
sortOrder="ascending"
|
|
||||||
columns={alphaColumns}
|
columns={alphaColumns}
|
||||||
onSort={onSort}
|
onSort={onSort}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -190,24 +190,33 @@ class HostsList extends Component {
|
|||||||
pluralizedItemName={i18n._(t`Hosts`)}
|
pluralizedItemName={i18n._(t`Hosts`)}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={this.handleSelect}
|
onRowClick={this.handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -174,24 +174,33 @@ class InventoriesList extends Component {
|
|||||||
pluralizedItemName={i18n._(t`Inventories`)}
|
pluralizedItemName={i18n._(t`Inventories`)}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={this.handleSelect}
|
onRowClick={this.handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -163,18 +163,25 @@ class JobList extends Component {
|
|||||||
pluralizedItemName="Jobs"
|
pluralizedItemName="Jobs"
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={this.handleSelect}
|
onRowClick={this.handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Finished`),
|
||||||
|
key: 'finished',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Finished`),
|
name: i18n._(t`Finished`),
|
||||||
key: 'finished',
|
key: 'finished',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -119,24 +119,33 @@ function OrganizationsList({ i18n }) {
|
|||||||
pluralizedItemName="Organizations"
|
pluralizedItemName="Organizations"
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={handleSelect}
|
onRowClick={handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -156,24 +156,33 @@ class ProjectsList extends Component {
|
|||||||
pluralizedItemName={i18n._(t`Projects`)}
|
pluralizedItemName={i18n._(t`Projects`)}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={this.handleSelect}
|
onRowClick={this.handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -154,24 +154,33 @@ class TeamsList extends Component {
|
|||||||
pluralizedItemName={i18n._(t`Teams`)}
|
pluralizedItemName={i18n._(t`Teams`)}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={this.handleSelect}
|
onRowClick={this.handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -214,24 +214,33 @@ class TemplatesList extends Component {
|
|||||||
pluralizedItemName={i18n._(t`Templates`)}
|
pluralizedItemName={i18n._(t`Templates`)}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={this.handleSelect}
|
onRowClick={this.handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Name`),
|
||||||
|
key: 'name',
|
||||||
|
isDefault: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Modified`),
|
||||||
|
key: 'modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Created`),
|
||||||
|
key: 'created',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Name`),
|
name: i18n._(t`Name`),
|
||||||
key: 'name',
|
key: 'name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Modified`),
|
name: i18n._(t`Modified`),
|
||||||
key: 'modified',
|
key: 'modified',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Created`),
|
name: i18n._(t`Created`),
|
||||||
key: 'created',
|
key: 'created',
|
||||||
isSortable: true,
|
|
||||||
isNumeric: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -154,24 +154,34 @@ class UsersList extends Component {
|
|||||||
pluralizedItemName="Users"
|
pluralizedItemName="Users"
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
onRowClick={this.handleSelect}
|
onRowClick={this.handleSelect}
|
||||||
toolbarColumns={[
|
toolbarSearchColumns={[
|
||||||
{
|
{
|
||||||
name: i18n._(t`Username`),
|
name: i18n._(t`Username`),
|
||||||
key: 'username',
|
key: 'username',
|
||||||
isSortable: true,
|
isDefault: true
|
||||||
isSearchable: true,
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`First Name`),
|
||||||
|
key: 'first_name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n._(t`Last Name`),
|
||||||
|
key: 'last_name',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
toolbarSortColumns={[
|
||||||
|
{
|
||||||
|
name: i18n._(t`Username`),
|
||||||
|
key: 'username',
|
||||||
|
isDefault: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`First Name`),
|
name: i18n._(t`First Name`),
|
||||||
key: 'first_name',
|
key: 'first_name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Last Name`),
|
name: i18n._(t`Last Name`),
|
||||||
key: 'last_name',
|
key: 'last_name',
|
||||||
isSortable: true,
|
|
||||||
isSearchable: true,
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderToolbar={props => (
|
renderToolbar={props => (
|
||||||
|
|||||||
@@ -249,3 +249,18 @@ export const Group = shape({
|
|||||||
inventory: number,
|
inventory: number,
|
||||||
variables: string,
|
variables: string,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const SearchColumns = arrayOf(
|
||||||
|
shape({
|
||||||
|
name: string.isRequired,
|
||||||
|
key: string.isRequired,
|
||||||
|
isDefault: bool,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const SortColumns = arrayOf(
|
||||||
|
shape({
|
||||||
|
name: string.isRequired,
|
||||||
|
key: string.isRequired,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ export function getQSConfig(
|
|||||||
if (!namespace) {
|
if (!namespace) {
|
||||||
throw new Error('a QS namespace is required');
|
throw new Error('a QS namespace is required');
|
||||||
}
|
}
|
||||||
|
// if order_by isn't passed, default to name
|
||||||
|
if (!Object.keys(defaultParams).filter(key => key === 'order_by').length) {
|
||||||
|
defaultParams.order_by = 'name';
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
namespace,
|
namespace,
|
||||||
defaultParams,
|
defaultParams,
|
||||||
|
|||||||
@@ -121,6 +121,18 @@ describe('qs (qs.js)', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should set order_by in defaultParams if it is not passed', () => {
|
||||||
|
expect(getQSConfig('organization', {
|
||||||
|
page: 1,
|
||||||
|
page_size: 5,
|
||||||
|
})).toEqual({
|
||||||
|
namespace: 'organization',
|
||||||
|
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
|
||||||
|
integerFields: ['page', 'page_size'],
|
||||||
|
dateFields: ['modified', 'created'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('should throw if no namespace given', () => {
|
test('should throw if no namespace given', () => {
|
||||||
expect(() => getQSConfig()).toThrow();
|
expect(() => getQSConfig()).toThrow();
|
||||||
});
|
});
|
||||||
@@ -132,7 +144,7 @@ describe('qs (qs.js)', () => {
|
|||||||
};
|
};
|
||||||
expect(getQSConfig('inventory', defaults)).toEqual({
|
expect(getQSConfig('inventory', defaults)).toEqual({
|
||||||
namespace: 'inventory',
|
namespace: 'inventory',
|
||||||
defaultParams: { page: 1, page_size: 15 },
|
defaultParams: { page: 1, page_size: 15, order_by: 'name' },
|
||||||
integerFields: ['page', 'page_size'],
|
integerFields: ['page', 'page_size'],
|
||||||
dateFields: ['modified', 'created'],
|
dateFields: ['modified', 'created'],
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user