fix: adds pagination/infinite scroll to owners select in DashboardList and ChartList (#10035)
This commit is contained in:
parent
be6b9b8fec
commit
c914af0bc4
|
|
@ -7630,6 +7630,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-0.1.5.tgz",
|
||||
"integrity": "sha512-Fx6atDc7JM1r0WkPCDhNetVZNp+DO21q/HGlomAKBG+k8vb1B8fg8Yige4oCf1P9OWTZWm5tM5i3jlXhrSbNOg=="
|
||||
},
|
||||
"@seznam/compose-react-refs": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@seznam/compose-react-refs/-/compose-react-refs-1.0.4.tgz",
|
||||
"integrity": "sha512-TwrojUAFVSd+HPAdnul0o65X8mIam+dJOxcWI6LhHAUIpVRk2cJp2dyWXWl6sJvZTY9ODSJpOibt7JKSNUjVfQ=="
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz",
|
||||
|
|
@ -27381,6 +27386,11 @@
|
|||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz",
|
||||
"integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA=="
|
||||
},
|
||||
"react-is-mounted-hook": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-is-mounted-hook/-/react-is-mounted-hook-1.0.3.tgz",
|
||||
"integrity": "sha512-YCCYcTVYMPfTi6WhWIwM9EYBcpHoivjjkE90O5ScsE9wXSbeXGZvLDMGt4mdSNcWshhc8JD0AzgBmsleCSdSFA=="
|
||||
},
|
||||
"react-json-tree": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.11.2.tgz",
|
||||
|
|
@ -27737,6 +27747,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-select-async-paginate": {
|
||||
"version": "0.4.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/react-select-async-paginate/-/react-select-async-paginate-0.4.0-alpha.1.tgz",
|
||||
"integrity": "sha512-086CF1dP69m9jwlGne+YLA0lk6jz21510hwKWmOBClr0zeYceMerXUcxXZvwr0wwroDo444ub1clkcblP88OQg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.9.6",
|
||||
"@seznam/compose-react-refs": "^1.0.4",
|
||||
"react-is-mounted-hook": "^1.0.3",
|
||||
"sleep-promise": "^8.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz",
|
||||
"integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-select-fast-filter-options": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-select-fast-filter-options/-/react-select-fast-filter-options-0.2.3.tgz",
|
||||
|
|
@ -29368,6 +29404,11 @@
|
|||
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
|
||||
"dev": true
|
||||
},
|
||||
"sleep-promise": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-8.0.1.tgz",
|
||||
"integrity": "sha1-jXlaJ+ojlT32tSuRCB5eImZZk8U="
|
||||
},
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@
|
|||
"react-router-dom": "^5.1.2",
|
||||
"react-search-input": "^0.11.3",
|
||||
"react-select": "^3.1.0",
|
||||
"react-select-async-paginate": "^0.4.0-alpha.1",
|
||||
"react-select-fast-filter-options": "^0.2.1",
|
||||
"react-sortable-hoc": "^1.11.0",
|
||||
"react-split": "^2.0.4",
|
||||
|
|
|
|||
|
|
@ -329,6 +329,7 @@ describe('ListView with new UI filters', () => {
|
|||
id: 'age',
|
||||
input: 'select',
|
||||
fetchSelects: fetchSelectsMock,
|
||||
paginate: true,
|
||||
operator: 'eq',
|
||||
},
|
||||
],
|
||||
|
|
@ -347,10 +348,6 @@ describe('ListView with new UI filters', () => {
|
|||
expect(wrapper.find(ListViewFilters)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('fetched selects if function is provided', () => {
|
||||
expect(fetchSelectsMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls fetchData on filter', () => {
|
||||
act(() => {
|
||||
wrapper
|
||||
|
|
|
|||
|
|
@ -22,10 +22,11 @@ import { withTheme } from 'emotion-theming';
|
|||
|
||||
import {
|
||||
Select,
|
||||
AsyncSelect,
|
||||
PaginatedSelect,
|
||||
PartialThemeConfig,
|
||||
PartialStylesConfig,
|
||||
} from 'src/components/Select';
|
||||
|
||||
import SearchInput from 'src/components/SearchInput';
|
||||
import {
|
||||
Filter,
|
||||
|
|
@ -45,6 +46,7 @@ interface SelectFilterProps extends BaseFilter {
|
|||
selects: Filter['selects'];
|
||||
emptyLabel?: string;
|
||||
fetchSelects?: Filter['fetchSelects'];
|
||||
paginate?: boolean;
|
||||
}
|
||||
|
||||
const FilterContainer = styled.div`
|
||||
|
|
@ -90,6 +92,7 @@ function SelectFilter({
|
|||
initialValue,
|
||||
onSelect,
|
||||
fetchSelects,
|
||||
paginate = false,
|
||||
}: SelectFilterProps) {
|
||||
const clearFilterSelect = {
|
||||
label: emptyLabel,
|
||||
|
|
@ -106,36 +109,52 @@ function SelectFilter({
|
|||
);
|
||||
setSelectedOption(selected);
|
||||
};
|
||||
const fetchAndFormatSelects = async (inputValue: string) => {
|
||||
const fetchAndFormatSelects = async (
|
||||
inputValue: string,
|
||||
loadedOptions: SelectOption[],
|
||||
{ page }: { page: number },
|
||||
) => {
|
||||
// only include clear filter when filter value does not exist
|
||||
let result = inputValue ? [] : [clearFilterSelect];
|
||||
let result = inputValue || page > 0 ? [] : [clearFilterSelect];
|
||||
let hasMore = paginate;
|
||||
if (fetchSelects) {
|
||||
const selectValues = await fetchSelects(inputValue);
|
||||
const selectValues = await fetchSelects(inputValue, page);
|
||||
// update matching option at initial load
|
||||
const matchingOption = result.find(x => x.value === initialValue);
|
||||
if (matchingOption) {
|
||||
setSelectedOption(matchingOption);
|
||||
}
|
||||
if (!selectValues.length) {
|
||||
hasMore = false;
|
||||
}
|
||||
result = [...result, ...selectValues];
|
||||
}
|
||||
return result;
|
||||
return {
|
||||
options: result,
|
||||
hasMore,
|
||||
additional: {
|
||||
page: page + 1,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<FilterContainer>
|
||||
<FilterTitle>{Header}</FilterTitle>
|
||||
{fetchSelects ? (
|
||||
<AsyncSelect
|
||||
<PaginatedSelect
|
||||
data-test="filters-select"
|
||||
themeConfig={filterSelectTheme}
|
||||
stylesConfig={filterSelectStyles}
|
||||
value={selectedOption}
|
||||
onChange={onChange}
|
||||
loadOptions={fetchAndFormatSelects}
|
||||
defaultOptions
|
||||
placeholder={emptyLabel}
|
||||
loadingMessage={() => 'Loading...'}
|
||||
clearable={false}
|
||||
additional={{
|
||||
page: 0,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
|
|
@ -200,7 +219,15 @@ function UIFilters({
|
|||
<FilterWrapper>
|
||||
{filters.map(
|
||||
(
|
||||
{ Header, id, input, selects, unfilteredLabel, fetchSelects },
|
||||
{
|
||||
Header,
|
||||
id,
|
||||
input,
|
||||
selects,
|
||||
unfilteredLabel,
|
||||
fetchSelects,
|
||||
paginate,
|
||||
},
|
||||
index,
|
||||
) => {
|
||||
const initialValue =
|
||||
|
|
@ -215,6 +242,7 @@ function UIFilters({
|
|||
emptyLabel={unfilteredLabel}
|
||||
initialValue={initialValue}
|
||||
fetchSelects={fetchSelects}
|
||||
paginate={paginate}
|
||||
onSelect={(value: any) => updateFilterValue(index, value)}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ interface Props {
|
|||
initialSort?: SortColumn[];
|
||||
filters?: Filters;
|
||||
bulkActions?: Array<{
|
||||
key?: string;
|
||||
name: React.ReactNode;
|
||||
key: string;
|
||||
name: React.ReactNode | string;
|
||||
onSelect: (rows: any[]) => any;
|
||||
}>;
|
||||
useNewUIFilters?: boolean;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export interface Filter {
|
|||
pageIndex?: number,
|
||||
pageSize?: number,
|
||||
) => Promise<SelectOption[]>;
|
||||
paginate?: boolean;
|
||||
}
|
||||
|
||||
export type Filters = Filter[];
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import BasicSelect, {
|
|||
import Async from 'react-select/async';
|
||||
import Creatable from 'react-select/creatable';
|
||||
import AsyncCreatable from 'react-select/async-creatable';
|
||||
import { withAsyncPaginate } from 'react-select-async-paginate';
|
||||
|
||||
import { SelectComponents } from 'react-select/src/components';
|
||||
import {
|
||||
|
|
@ -286,4 +287,7 @@ export const Select = styled(WindowedSelect);
|
|||
export const AsyncSelect = styled(WindowedAsyncSelect);
|
||||
export const CreatableSelect = styled(WindowedCreatableSelect);
|
||||
export const AsyncCreatableSelect = styled(WindowedAsyncCreatableSelect);
|
||||
// Wrap with async pagination (infinite scroll). Cannot use windowed since options are appended dynamically which causes focus jumping
|
||||
// @ts-ignore
|
||||
export const PaginatedSelect = withAsyncPaginate(styled(BasicSelect));
|
||||
export default Select;
|
||||
|
|
|
|||
|
|
@ -434,6 +434,7 @@ class ChartList extends React.PureComponent<Props, State> {
|
|||
operator: 'rel_m_m',
|
||||
unfilteredLabel: 'All',
|
||||
fetchSelects: this.fetchOwners,
|
||||
paginate: true,
|
||||
},
|
||||
{
|
||||
Header: 'Viz Type',
|
||||
|
|
@ -452,6 +453,7 @@ class ChartList extends React.PureComponent<Props, State> {
|
|||
operator: 'eq',
|
||||
unfilteredLabel: 'All',
|
||||
fetchSelects: this.fetchDatasets,
|
||||
paginate: false,
|
||||
},
|
||||
{
|
||||
Header: 'Search',
|
||||
|
|
|
|||
|
|
@ -437,6 +437,7 @@ class DashboardList extends React.PureComponent<Props, State> {
|
|||
operator: 'rel_m_m',
|
||||
unfilteredLabel: 'All',
|
||||
fetchSelects: this.fetchOwners,
|
||||
paginate: true,
|
||||
},
|
||||
{
|
||||
Header: 'Published',
|
||||
|
|
|
|||
Loading…
Reference in New Issue