From 39d018d4a2dd7be2343c6b6eb065b50143c06a9f Mon Sep 17 00:00:00 2001 From: Gustav Hansen Date: Mon, 11 Apr 2022 16:32:29 +0200 Subject: [PATCH] useModalQueryParams: Add tests --- .../address/content-types/address/schema.json | 16 ++ .../src/components/basic/simple.json | 5 + .../AssetDialog/BrowseStep/index.js | 8 + .../admin/src/components/AssetDialog/index.js | 65 ++++++-- .../components/MediaLibraryDialog/index.js | 20 ++- .../hooks/tests/useModalQueryParams.test.js | 156 ++++++++++++++++++ .../core/upload/admin/src/hooks/useAssets.js | 7 +- .../core/upload/admin/src/hooks/useFolders.js | 7 +- .../admin/src/hooks/useModalAssets/index.js | 47 ------ .../useModalAssets/useModalQueryParams.js | 59 ------- 10 files changed, 252 insertions(+), 138 deletions(-) create mode 100644 packages/core/upload/admin/src/hooks/tests/useModalQueryParams.test.js delete mode 100644 packages/core/upload/admin/src/hooks/useModalAssets/index.js delete mode 100644 packages/core/upload/admin/src/hooks/useModalAssets/useModalQueryParams.js diff --git a/examples/getstarted/src/api/address/content-types/address/schema.json b/examples/getstarted/src/api/address/content-types/address/schema.json index 90bfbaa50f..96f34f199b 100755 --- a/examples/getstarted/src/api/address/content-types/address/schema.json +++ b/examples/getstarted/src/api/address/content-types/address/schema.json @@ -80,6 +80,22 @@ "component": "blog.test-como", "required": false, "min": 2 + }, + "wat": { + "type": "component", + "repeatable": true, + "component": "basic.simple" + }, + "wat2": { + "type": "dynamiczone", + "components": [ + "basic.simple" + ] + }, + "bla": { + "type": "component", + "repeatable": false, + "component": "basic.simple" } } } diff --git a/examples/getstarted/src/components/basic/simple.json b/examples/getstarted/src/components/basic/simple.json index d8f4c8b426..b7da0b9ced 100644 --- a/examples/getstarted/src/components/basic/simple.json +++ b/examples/getstarted/src/components/basic/simple.json @@ -13,6 +13,11 @@ }, "test": { "type": "string" + }, + "addresses": { + "type": "relation", + "relation": "oneToMany", + "target": "api::address.address" } } } diff --git a/packages/core/upload/admin/src/components/AssetDialog/BrowseStep/index.js b/packages/core/upload/admin/src/components/AssetDialog/BrowseStep/index.js index 23fdf14118..737108df61 100644 --- a/packages/core/upload/admin/src/components/AssetDialog/BrowseStep/index.js +++ b/packages/core/upload/admin/src/components/AssetDialog/BrowseStep/index.js @@ -32,6 +32,7 @@ const EndBlockActions = styled(StartBlockActions)` export const BrowseStep = ({ allowedTypes, assets, + folders, multiple, onChangeFilters, onChangePage, @@ -97,6 +98,8 @@ export const BrowseStep = ({ )} + {folders.length > 0 && <>} + {assets.length > 0 ? ( )} + {pagination.pageCount > 0 && ( @@ -143,7 +147,11 @@ BrowseStep.defaultProps = { BrowseStep.propTypes = { allowedTypes: PropTypes.arrayOf(PropTypes.string), + + // TODO: add asset & folder shapes assets: PropTypes.arrayOf(PropTypes.shape({})).isRequired, + folders: PropTypes.arrayOf(PropTypes.shape({})).isRequired, + multiple: PropTypes.bool, onChangeFilters: PropTypes.func.isRequired, onChangePage: PropTypes.func.isRequired, diff --git a/packages/core/upload/admin/src/components/AssetDialog/index.js b/packages/core/upload/admin/src/components/AssetDialog/index.js index c547324a1e..7b04749be5 100644 --- a/packages/core/upload/admin/src/components/AssetDialog/index.js +++ b/packages/core/upload/admin/src/components/AssetDialog/index.js @@ -10,13 +10,15 @@ import { useIntl } from 'react-intl'; import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs'; import { Badge } from '@strapi/design-system/Badge'; import { Loader } from '@strapi/design-system/Loader'; +import { Stack } from '@strapi/design-system/Stack'; import { NoPermissions, AnErrorOccurred, useSelectionState } from '@strapi/helper-plugin'; import getTrad from '../../utils/getTrad'; import { SelectedStep } from './SelectedStep'; import { BrowseStep } from './BrowseStep'; import { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions'; -import { useModalAssets } from '../../hooks/useModalAssets'; -import useModalQueryParams from '../../hooks/useModalAssets/useModalQueryParams'; +import { useAssets } from '../../hooks/useAssets'; +import { useFolders } from '../../hooks/useFolders'; +import useModalQueryParams from '../../hooks/useModalQueryParams'; import { AssetDefinition } from '../../constants'; import getAllowedFiles from '../../utils/getAllowedFiles'; import { DialogTitle } from './DialogTitle'; @@ -29,6 +31,7 @@ export const AssetDialog = ({ allowedTypes, onClose, onAddAsset, + onAddFolder, onValidate, multiple, initiallySelectedAssets, @@ -46,12 +49,28 @@ export const AssetDialog = ({ } = useMediaLibraryPermissions(); const [ { rawQuery, queryObject }, - { onChangeFilters, onChangePage, onChangePageSize, onChangeSort, onChangeSearch }, + { + onChangeFilters, + onChangePage, + onChangePageSize, + onChangeSort, + onChangeSearch, + onChangeFolder, + }, ] = useModalQueryParams(); - const { data, isLoading, error } = useModalAssets({ skipWhen: !canRead, rawQuery }); + const { + data: { pagination, results: assets } = {}, + isLoading: isLoadingAssets, + error: errorAssets, + } = useAssets({ skipWhen: !canRead, rawQuery }); + const { + data: { results: folders } = {}, + isLoading: isLoadingFolders, + error: errorFolders, + } = useFolders(); const [selectedAssets, { selectOne, selectAll, selectOnly, setSelections }] = useSelectionState( - 'id', + ['id'], initiallySelectedAssets ); @@ -75,10 +94,10 @@ export const AssetDialog = ({ return multiple ? selectOne(asset) : selectOnly(asset); }; - const loading = isLoadingPermissions || isLoading; - const assets = data?.results; + const isLoading = isLoadingPermissions || isLoadingAssets || isLoadingFolders; + const hasError = errorAssets || errorFolders; - if (loading) { + if (isLoading) { return ( @@ -95,7 +114,7 @@ export const AssetDialog = ({ ); } - if (error) { + if (hasError) { return ( @@ -125,12 +144,21 @@ export const AssetDialog = ({ count={6} action={ canCreate ? ( - + + + + + ) : ( undefined ) @@ -175,7 +203,7 @@ export const AssetDialog = ({ }; return ( - + { - const [step, setStep] = useState(Steps.SelectAsset); + const [step, setStep] = useState(STEPS.AssetSelect); - if (step === Steps.SelectAsset) { + if (step === STEPS.AssetSelect) { return ( setStep(Steps.UploadAsset)} + onAddAsset={() => setStep(STEPS.AssetUpload)} + onAddFolder={() => setStep(STEPS.FolderCreate)} multiple /> ); } - return setStep(Steps.SelectAsset)} />; + if (step === STEPS.FolderCreate) { + return null; + } + + return setStep(STEPS.AssetSelect)} />; }; MediaLibraryDialog.defaultProps = { diff --git a/packages/core/upload/admin/src/hooks/tests/useModalQueryParams.test.js b/packages/core/upload/admin/src/hooks/tests/useModalQueryParams.test.js new file mode 100644 index 0000000000..b98b35f1d5 --- /dev/null +++ b/packages/core/upload/admin/src/hooks/tests/useModalQueryParams.test.js @@ -0,0 +1,156 @@ +import { renderHook, act } from '@testing-library/react-hooks'; + +import useModalQueryParams from '../useModalQueryParams'; + +const FIXTURE_QUERY = { + page: 1, + sort: 'updatedAt:DESC', + pageSize: 10, + filters: { + $and: [], + }, +}; + +function setup(...args) { + return renderHook(() => useModalQueryParams(...args)); +} + +describe('useModalQueryParams', () => { + test('setup proper defaults', () => { + const { + result: { + current: [{ queryObject, rawQuery }, callbacks], + }, + } = setup(); + + expect(queryObject).toStrictEqual(FIXTURE_QUERY); + expect(rawQuery).toBe('page=1&sort=updatedAt:DESC&pageSize=10'); + + expect(callbacks).toStrictEqual({ + onChangeFilters: expect.any(Function), + onChangeFolder: expect.any(Function), + onChangePage: expect.any(Function), + onChangePageSize: expect.any(Function), + onChangeSort: expect.any(Function), + onChangeSearch: expect.any(Function), + }); + }); + + test('onChangeFilters', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangeFilters({ some: 'thing' }); + }); + + expect(result.current[0].queryObject).toStrictEqual({ + ...FIXTURE_QUERY, + filters: { + ...FIXTURE_QUERY.filters, + $and: { + some: 'thing', + }, + }, + }); + }); + + test('onChangeFolder', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangeFolder({ id: 1 }); + }); + + expect(result.current[0].queryObject).toStrictEqual({ + ...FIXTURE_QUERY, + folder: { + id: 1, + }, + }); + }); + + test('onChangePage', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangePage({ id: 1 }); + }); + + expect(result.current[0].queryObject).toStrictEqual({ + ...FIXTURE_QUERY, + page: { + id: 1, + }, + }); + }); + + test('onChangePageSize', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangePageSize(5); + }); + + expect(result.current[0].queryObject).toStrictEqual({ + ...FIXTURE_QUERY, + pageSize: 5, + }); + }); + + test('onChangePageSize - converts string to numbers', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangePageSize('5'); + }); + + expect(result.current[0].queryObject).toStrictEqual({ + ...FIXTURE_QUERY, + pageSize: 5, + }); + }); + + test('onChangeSort', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangeSort('something:else'); + }); + + expect(result.current[0].queryObject).toStrictEqual({ + ...FIXTURE_QUERY, + sort: 'something:else', + }); + }); + + test('onChangeSearch', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangeSearch('something'); + }); + + expect(result.current[0].queryObject).toStrictEqual({ + ...FIXTURE_QUERY, + _q: 'something', + }); + }); + + test('onChangeSearch - empty string resets all values and removes _q and page', () => { + const { result } = setup(); + + act(() => { + result.current[1].onChangePage({ id: 1 }); + }); + + act(() => { + result.current[1].onChangeSearch('something'); + }); + + act(() => { + result.current[1].onChangeSearch(''); + }); + + expect(result.current[0].queryObject).toStrictEqual(FIXTURE_QUERY); + }); +}); diff --git a/packages/core/upload/admin/src/hooks/useAssets.js b/packages/core/upload/admin/src/hooks/useAssets.js index 76d6fa0434..ef5e7f8ed3 100644 --- a/packages/core/upload/admin/src/hooks/useAssets.js +++ b/packages/core/upload/admin/src/hooks/useAssets.js @@ -1,19 +1,18 @@ import { useQuery } from 'react-query'; import { useNotifyAT } from '@strapi/design-system/LiveRegions'; -import { useNotification, useQueryParams } from '@strapi/helper-plugin'; +import { useNotification } from '@strapi/helper-plugin'; import { useIntl } from 'react-intl'; import { axiosInstance, getRequestUrl } from '../utils'; -export const useAssets = ({ skipWhen }) => { +export const useAssets = ({ skipWhen, rawQuery }) => { const { formatMessage } = useIntl(); const toggleNotification = useNotification(); const { notifyStatus } = useNotifyAT(); - const [{ rawQuery }] = useQueryParams(); const dataRequestURL = getRequestUrl('files'); const getAssets = async () => { try { - const { data } = await axiosInstance.get(`${dataRequestURL}${rawQuery}`); + const { data } = await axiosInstance.get(`${dataRequestURL}${rawQuery || ''}`); notifyStatus( formatMessage({ diff --git a/packages/core/upload/admin/src/hooks/useFolders.js b/packages/core/upload/admin/src/hooks/useFolders.js index 084aea004a..b4aa6165fa 100644 --- a/packages/core/upload/admin/src/hooks/useFolders.js +++ b/packages/core/upload/admin/src/hooks/useFolders.js @@ -1,19 +1,18 @@ import { useQuery } from 'react-query'; import { useNotifyAT } from '@strapi/design-system/LiveRegions'; -import { useNotification, useQueryParams } from '@strapi/helper-plugin'; +import { useNotification } from '@strapi/helper-plugin'; import { useIntl } from 'react-intl'; import { axiosInstance, getRequestUrl } from '../utils'; -export const useFolders = ({ enabled = true }) => { +export const useFolders = ({ enabled = true, rawQuery } = {}) => { const { formatMessage } = useIntl(); const toggleNotification = useNotification(); const { notifyStatus } = useNotifyAT(); - const [{ rawQuery }] = useQueryParams(); const dataRequestURL = getRequestUrl('folders'); const fetchFolders = async () => { try { - const { data } = await axiosInstance.get(`${dataRequestURL}${rawQuery}`); + const { data } = await axiosInstance.get(`${dataRequestURL}${rawQuery || ''}`); notifyStatus( formatMessage({ diff --git a/packages/core/upload/admin/src/hooks/useModalAssets/index.js b/packages/core/upload/admin/src/hooks/useModalAssets/index.js deleted file mode 100644 index 6158de3d3a..0000000000 --- a/packages/core/upload/admin/src/hooks/useModalAssets/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import { useEffect } from 'react'; -import { useQuery } from 'react-query'; -import { useNotifyAT } from '@strapi/design-system/LiveRegions'; -import { useNotification } from '@strapi/helper-plugin'; -import { useIntl } from 'react-intl'; -import { axiosInstance, getRequestUrl } from '../../utils'; - -export const useModalAssets = ({ skipWhen, rawQuery }) => { - const { formatMessage } = useIntl(); - const toggleNotification = useNotification(); - const { notifyStatus } = useNotifyAT(); - const dataRequestURL = getRequestUrl('files'); - - const getAssets = async () => { - const { data } = await axiosInstance.get(`${dataRequestURL}?${rawQuery}`); - - return data; - }; - - const { data, error, isLoading } = useQuery([`assets`, rawQuery], getAssets, { - enabled: !skipWhen, - staleTime: 0, - cacheTime: 0, - }); - - useEffect(() => { - if (data) { - notifyStatus( - formatMessage({ - id: 'list.asset.at.finished', - defaultMessage: 'The assets have finished loading.', - }) - ); - } - }, [data, notifyStatus, formatMessage]); - - useEffect(() => { - if (error) { - toggleNotification({ - type: 'warning', - message: { id: 'notification.error' }, - }); - } - }, [error, toggleNotification]); - - return { data, error, isLoading }; -}; diff --git a/packages/core/upload/admin/src/hooks/useModalAssets/useModalQueryParams.js b/packages/core/upload/admin/src/hooks/useModalAssets/useModalQueryParams.js deleted file mode 100644 index 6f288bbdca..0000000000 --- a/packages/core/upload/admin/src/hooks/useModalAssets/useModalQueryParams.js +++ /dev/null @@ -1,59 +0,0 @@ -import { useState } from 'react'; - -import { stringify } from 'qs'; - -const useModalQueryParams = () => { - const [queryObject, setQueryObject] = useState({ - page: 1, - sort: 'updatedAt:DESC', - pageSize: 10, - filters: { - $and: [], - }, - }); - - const handleChangeFilters = nextFilters => { - setQueryObject(prev => ({ ...prev, page: 1, filters: { $and: nextFilters } })); - }; - - const handleChangePageSize = pageSize => { - setQueryObject(prev => ({ ...prev, pageSize: parseInt(pageSize, 10), page: 1 })); - }; - - const handeChangePage = page => { - setQueryObject(prev => ({ ...prev, page })); - }; - - const handleChangeSort = sort => { - setQueryObject(prev => ({ ...prev, sort })); - }; - - const handleChangeSearch = _q => { - if (_q) { - setQueryObject(prev => ({ ...prev, _q, page: 1 })); - } else { - const newState = { page: 1 }; - - Object.keys(queryObject).forEach(key => { - if (!['page', '_q'].includes(key)) { - newState[key] = queryObject[key]; - } - }); - - setQueryObject(newState); - } - }; - - return [ - { queryObject, rawQuery: stringify(queryObject, { encode: false }) }, - { - onChangeFilters: handleChangeFilters, - onChangePage: handeChangePage, - onChangePageSize: handleChangePageSize, - onChangeSort: handleChangeSort, - onChangeSearch: handleChangeSearch, - }, - ]; -}; - -export default useModalQueryParams;