From c914af0bc437826e4d5bd960eb257c665f3f0147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CA=88=E1=B5=83=E1=B5=A2?= Date: Wed, 17 Jun 2020 16:27:21 -0700 Subject: [PATCH] fix: adds pagination/infinite scroll to owners select in DashboardList and ChartList (#10035) --- superset-frontend/package-lock.json | 41 +++++++++++++++++ superset-frontend/package.json | 1 + .../components/ListView/ListView_spec.jsx | 5 +-- .../src/components/ListView/Filters.tsx | 44 +++++++++++++++---- .../src/components/ListView/ListView.tsx | 4 +- .../src/components/ListView/types.ts | 1 + .../Select/SupersetStyledSelect.tsx | 4 ++ .../src/views/chartList/ChartList.tsx | 2 + .../src/views/dashboardList/DashboardList.tsx | 1 + 9 files changed, 89 insertions(+), 14 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 877c389c0..712314643 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -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", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 6d231202c..df020996f 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -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", diff --git a/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx b/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx index 7ffb1f9e6..e5e04d5e6 100644 --- a/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx +++ b/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx @@ -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 diff --git a/superset-frontend/src/components/ListView/Filters.tsx b/superset-frontend/src/components/ListView/Filters.tsx index 18380eec3..b4f3eeec5 100644 --- a/superset-frontend/src/components/ListView/Filters.tsx +++ b/superset-frontend/src/components/ListView/Filters.tsx @@ -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 ( {Header} {fetchSelects ? ( - 'Loading...'} clearable={false} + additional={{ + page: 0, + }} /> ) : (