From 944e34043c4585b2e1b2d082f0dba08b2270eebe Mon Sep 17 00:00:00 2001 From: soupette Date: Thu, 5 Nov 2020 15:39:57 +0100 Subject: [PATCH] Migrate filters to qs lib Signed-off-by: soupette --- .../src/components/FilterPicker/index.js | 81 +++++++++------ .../admin/src/containers/ListView/Filter.js | 46 +++++---- .../admin/src/containers/ListView/index.js | 99 ++++++------------- .../admin/src/utils/formatFiltersFromQuery.js | 46 +++++++++ .../admin/src/utils/formatFiltersToQuery.js | 13 +++ .../admin/src/utils/index.js | 2 + 6 files changed, 164 insertions(+), 123 deletions(-) create mode 100644 packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersFromQuery.js create mode 100644 packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersToQuery.js diff --git a/packages/strapi-plugin-content-manager/admin/src/components/FilterPicker/index.js b/packages/strapi-plugin-content-manager/admin/src/components/FilterPicker/index.js index 79af4aad2f..7a608b309b 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/FilterPicker/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/FilterPicker/index.js @@ -1,4 +1,4 @@ -import React, { memo, useMemo, useReducer } from 'react'; +import React, { memo, useCallback, useMemo, useReducer, useRef } from 'react'; import { withRouter } from 'react-router'; import PropTypes from 'prop-types'; import { capitalize, get } from 'lodash'; @@ -9,9 +9,11 @@ import { getFilterType, useUser, findMatchingPermissions, + useGlobalContext, } from 'strapi-helper-plugin'; import pluginId from '../../pluginId'; +import { formatFiltersToQuery, getTrad } from '../../utils'; import useListView from '../../hooks/useListView'; import Container from '../Container'; import FilterPickerOption from '../FilterPickerOption'; @@ -21,8 +23,9 @@ import reducer, { initialState } from './reducer'; const NOT_ALLOWED_FILTERS = ['json', 'component', 'relation', 'media', 'richtext', 'dynamiczone']; -function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState }) { - const { schema, filters, slug } = useListView(); +function FilterPicker({ contentType, isOpen, name, toggleFilterPickerState, setQuery, slug }) { + const { emitEvent } = useGlobalContext(); + const emitEventRef = useRef(emitEvent); const userPermissions = useUser(); const readActionAllowedFields = useMemo(() => { const matchingPermissions = findMatchingPermissions(userPermissions, [ @@ -34,15 +37,32 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState return get(matchingPermissions, ['0', 'fields'], []); }, [userPermissions, slug]); - let timestamps = get(schema, ['options', 'timestamps']); + + let timestamps = get(contentType, ['options', 'timestamps']); if (!Array.isArray(timestamps)) { timestamps = []; } - const allowedAttributes = Object.keys(get(schema, ['attributes']), {}) + const actions = [ + { + label: getTrad('components.FiltersPickWrapper.PluginHeader.actions.clearAll'), + kind: 'secondary', + onClick: () => { + toggleFilterPickerState(); + setQuery({ _where: [] }, 'remove'); + }, + }, + { + label: getTrad('components.FiltersPickWrapper.PluginHeader.actions.apply'), + kind: 'primary', + type: 'submit', + }, + ]; + + const allowedAttributes = Object.keys(get(contentType, ['attributes']), {}) .filter(attr => { - const current = get(schema, ['attributes', attr], {}); + const current = get(contentType, ['attributes', attr], {}); if (!readActionAllowedFields.includes(attr) && attr !== 'id' && !timestamps.includes(attr)) { return false; @@ -52,7 +72,7 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState }) .sort() .map(attr => { - const current = get(schema, ['attributes', attr], {}); + const current = get(contentType, ['attributes', attr], {}); return { name: attr, type: current.type, options: current.enum || null }; }); @@ -81,8 +101,7 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState ); - // Generate the first filter for adding a new one or at initial state - const getInitialFilter = () => { + const initialFilter = useMemo(() => { const type = get(allowedAttributes, [0, 'type'], ''); const [filter] = getFilterType(type); @@ -103,36 +122,44 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState }; return initFilter; - }; + }, [allowedAttributes]); + // Set the filters when the collapse is opening const handleEntering = () => { const currentFilters = filters; - const initialFilters = currentFilters.length > 0 ? currentFilters : [getInitialFilter()]; + /* eslint-disable indent */ + const initialFilters = currentFilters.length ? currentFilters : [initialFilter]; dispatch({ type: 'SET_FILTERS', initialFilters, - attributes: get(schema, 'attributes', {}), + attributes: get(contentType, 'attributes', {}), }); }; const addFilter = () => { dispatch({ type: 'ADD_FILTER', - filter: getInitialFilter(), + filter: initialFilter, }); }; + const handleSubmit = useCallback( + e => { + e.preventDefault(); + const nextFilters = formatFiltersToQuery(modifiedData); + + emitEventRef.current('didFilterEntries'); + setQuery(nextFilters); + toggleFilterPickerState(); + }, + [modifiedData, setQuery, toggleFilterPickerState] + ); + return ( -
{ - e.preventDefault(); - - onSubmit(modifiedData); - }} - > + { - const updatedFilters = filters.slice().filter((_, i) => i !== index); + const handleClick = useCallback(() => { + const updatedFilters = filters.slice().filter((_, i) => i !== index); - if (isFilterPickerOpen) { - toggleFilterPickerState(); - } - changeParams({ target: { name: 'filters', value: updatedFilters } }); - }} - label={label} - type={type} - /> - ); + if (isFilterPickerOpen) { + toggleFilterPickerState(); + } + + setQuery(formatFiltersToQuery(updatedFilters)); + }, [filters, index, isFilterPickerOpen, setQuery, toggleFilterPickerState]); + + return ; } Filter.defaultProps = { @@ -64,15 +62,15 @@ Filter.defaultProps = { }; Filter.propTypes = { - changeParams: PropTypes.func.isRequired, - filter: PropTypes.string.isRequired, + contentType: PropTypes.shape({ attributes: PropTypes.object.isRequired }).isRequired, + filterName: PropTypes.string.isRequired, filters: PropTypes.array.isRequired, index: PropTypes.number.isRequired, isFilterPickerOpen: PropTypes.bool.isRequired, name: PropTypes.string, - schema: PropTypes.object.isRequired, + setQuery: PropTypes.func.isRequired, toggleFilterPickerState: PropTypes.func.isRequired, value: PropTypes.any, }; -export default Filter; +export default memo(Filter); diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js index ed66b563c8..c82c450b45 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js @@ -18,13 +18,18 @@ import { import pluginId from '../../pluginId'; import pluginPermissions from '../../permissions'; import { useQueryParams } from '../../hooks'; -import { generatePermissionsObject, getRequestUrl, getTrad } from '../../utils'; +import { + formatFiltersFromQuery, + generatePermissionsObject, + getRequestUrl, + getTrad, +} from '../../utils'; import DisplayedFieldsDropdown from '../../components/DisplayedFieldsDropdown'; import Container from '../../components/Container'; import CustomTable from '../../components/CustomTable'; -// import FilterPicker from '../../components/FilterPicker'; +import FilterPicker from '../../components/FilterPicker'; import Search from '../../components/Search'; import ListViewProvider from '../ListViewProvider'; import { AddFilterCta, FilterIcon, Wrapper } from './components'; @@ -53,7 +58,7 @@ import { getAllAllowedHeaders, getFirstSortableHeader } from './utils'; /* eslint-disable react/no-array-index-key */ -const FilterPicker = () =>
FILTER
; +// const FilterPicker = () =>
FILTER
; function ListView({ count, @@ -105,12 +110,12 @@ function ListView({ } = layout; const { emitEvent } = useGlobalContext(); + const emitEventRef = useRef(emitEvent); const viewPermissions = useMemo(() => generatePermissionsObject(slug), [slug]); const { isLoading: isLoadingForPermissions, allowedActions: { canCreate, canRead, canUpdate, canDelete }, } = useUserPermissions(viewPermissions); - // const query = useQuery(); const defaultSort = `${defaultSortBy}:${defaultSortOrder}`; const [{ query, rawQuery }, setQuery] = useQueryParams({ page: 1, @@ -132,7 +137,9 @@ function ListView({ const allAllowedHeaders = getAllAllowedHeaders(attributes); - const filters = query._where || []; + const filters = useMemo(() => { + return formatFiltersFromQuery(query); + }, [query]); const _limit = parseInt(query.pageSize, 10); const _page = parseInt(query.page, 10); @@ -142,18 +149,6 @@ function ListView({ const label = contentType.info.label; const searchToSendForRequest = rawQuery; - // const searchToSendForRequest = useMemo(() => { - // const currentSearch = new URLSearchParams(search); - - // currentSearch.set('_limit', _limit); - // currentSearch.set('_sort', _sort); - // currentSearch.set('_start', _start); - // currentSearch.delete('_page'); - - // return currentSearch.toString(); - // }, [_limit, _sort, _start, search]); - - // const getDataActionRef = useRef(getData); const getDataSucceededRef = useRef(getDataSucceeded); const shouldSendRequest = useMemo(() => { @@ -327,24 +322,6 @@ function ListView({ [displayedHeaders, onChangeListHeaders] ); - // const handleChangeFilters = ({ target: { value } }) => { - // const newSearch = new URLSearchParams(); - - // // Set the default params - // newSearch.set('_limit', _limit); - // newSearch.set('_sort', _sort); - // newSearch.set('_page', 1); - - // value.forEach(({ filter, name, value: filterValue }) => { - // const filterType = filter === '=' ? '' : filter; - // const filterName = `${name}${filterType}`; - - // newSearch.append(filterName, filterValue); - // }); - - // push({ search: newSearch.toString() }); - // }; - const handleChangeSearch = async ({ target: { name, value } }) => { const currentSearch = new URLSearchParams(searchToSendForRequest); @@ -373,36 +350,15 @@ function ListView({ } }; - const handleSubmit = (filters = []) => { - emitEvent('didFilterEntries'); - toggleFilterPickerState(); - // handleChangeFilters({ target: { name: 'filters', value: filters } }); - }; + const toggleFilterPickerState = useCallback(() => { + setFilterPickerState(prevState => { + if (!prevState) { + emitEventRef.current('willFilterEntries'); + } - const toggleFilterPickerState = () => { - if (!isFilterPickerOpen) { - emitEvent('willFilterEntries'); - } - - setFilterPickerState(prevState => !prevState); - }; - - const filterPickerActions = [ - { - label: `${pluginId}.components.FiltersPickWrapper.PluginHeader.actions.clearAll`, - kind: 'secondary', - onClick: () => { - toggleFilterPickerState(); - // Delete all filters - // handleChangeFilters({ target: { name: 'filters', value: [] } }); - }, - }, - { - label: `${pluginId}.components.FiltersPickWrapper.PluginHeader.actions.apply`, - kind: 'primary', - type: 'submit', - }, - ]; + return !prevState; + }); + }, []); const headerAction = useMemo( () => { @@ -494,11 +450,13 @@ function ListView({ firstSortableHeader={firstSortableHeader} > {!isFilterPickerOpen &&
} @@ -516,16 +474,17 @@ function ListView({ - {filters.map((filter, key) => ( + {filters.map(({ filter: filterName, name }, key) => ( ))} diff --git a/packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersFromQuery.js b/packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersFromQuery.js new file mode 100644 index 0000000000..ade4770628 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersFromQuery.js @@ -0,0 +1,46 @@ +const findAppliedFilter = str => { + let filter = '='; + let name = str; + + const filters = [ + '_ne', + '_lt', + '_lte', + '_gt', + '_gte', + '_contains', + '_containss', + '_ncontains', + '_in', + '_nin', + ]; + + filters.forEach(filterName => { + const split = str.split(filterName); + + if (split[1] === '') { + filter = filterName; + name = split[0]; + } + }); + + return { filter, name }; +}; + +const formatFiltersFromQuery = ({ _where }) => { + if (!_where) { + return []; + } + + return _where.map(obj => { + const [key] = Object.keys(obj); + const { filter, name } = findAppliedFilter(key); + + const value = obj[key]; + + return { name, filter, value }; + }); +}; + +export default formatFiltersFromQuery; +export { findAppliedFilter }; diff --git a/packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersToQuery.js b/packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersToQuery.js new file mode 100644 index 0000000000..d19262947b --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/utils/formatFiltersToQuery.js @@ -0,0 +1,13 @@ +const formatFiltersToQuery = array => { + const nextFilters = array.map(({ name, filter, value }) => { + if (filter === '=') { + return { [name]: value }; + } + + return { [`${name}${filter}`]: value }; + }); + + return { _where: nextFilters }; +}; + +export default formatFiltersToQuery; diff --git a/packages/strapi-plugin-content-manager/admin/src/utils/index.js b/packages/strapi-plugin-content-manager/admin/src/utils/index.js index f5f20f88cd..1f468f4297 100644 --- a/packages/strapi-plugin-content-manager/admin/src/utils/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/utils/index.js @@ -2,6 +2,8 @@ export { default as checkIfAttributeIsDisplayable } from './checkIfAttributeIsDi export { default as createDefaultForm } from './createDefaultForm'; export { default as dateFormats } from './dateFormats'; export { default as formatComponentData } from './formatComponentData'; +export { default as formatFiltersFromQuery } from './formatFiltersFromQuery'; +export { default as formatFiltersToQuery } from './formatFiltersToQuery'; export { default as generatePermissionsObject } from './generatePermissionsObject'; export { default as getInjectedComponents } from './getComponents'; export { default as getMaxTempKey } from './getMaxTempKey';