mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 08:19:07 +00:00
useModalQueryParams: Add tests
This commit is contained in:
parent
3933455e6c
commit
39d018d4a2
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,11 @@
|
||||
},
|
||||
"test": {
|
||||
"type": "string"
|
||||
},
|
||||
"addresses": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "api::address.address"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ const EndBlockActions = styled(StartBlockActions)`
|
||||
export const BrowseStep = ({
|
||||
allowedTypes,
|
||||
assets,
|
||||
folders,
|
||||
multiple,
|
||||
onChangeFilters,
|
||||
onChangePage,
|
||||
@ -97,6 +98,8 @@ export const BrowseStep = ({
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{folders.length > 0 && <></>}
|
||||
|
||||
{assets.length > 0 ? (
|
||||
<AssetList
|
||||
allowedTypes={allowedTypes}
|
||||
@ -120,6 +123,7 @@ export const BrowseStep = ({
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{pagination.pageCount > 0 && (
|
||||
<Flex justifyContent="space-between">
|
||||
<PageSize pageSize={queryObject.pageSize} onChangePageSize={onChangePageSize} />
|
||||
@ -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,
|
||||
|
@ -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 (
|
||||
<ModalLayout onClose={onClose} labelledBy="asset-dialog-title" aria-busy>
|
||||
<DialogTitle />
|
||||
@ -95,7 +114,7 @@ export const AssetDialog = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (hasError) {
|
||||
return (
|
||||
<ModalLayout onClose={onClose} labelledBy="asset-dialog-title">
|
||||
<DialogTitle />
|
||||
@ -125,12 +144,21 @@ export const AssetDialog = ({
|
||||
count={6}
|
||||
action={
|
||||
canCreate ? (
|
||||
<Button variant="secondary" startIcon={<PlusIcon />} onClick={onAddAsset}>
|
||||
{formatMessage({
|
||||
id: getTrad('header.actions.add-assets'),
|
||||
defaultMessage: 'Add new assets',
|
||||
})}
|
||||
</Button>
|
||||
<Stack space={2} horizontal id="asset-dialog-title">
|
||||
<Button variant="tertiary" onClick={onAddFolder}>
|
||||
{formatMessage({
|
||||
id: getTrad('header.actions.add-folder'),
|
||||
defaultMessage: 'Add folder',
|
||||
})}
|
||||
</Button>
|
||||
|
||||
<Button variant="secondary" startIcon={<PlusIcon />} onClick={onAddAsset}>
|
||||
{formatMessage({
|
||||
id: getTrad('header.actions.add-assets'),
|
||||
defaultMessage: 'Add new assets',
|
||||
})}
|
||||
</Button>
|
||||
</Stack>
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
@ -175,7 +203,7 @@ export const AssetDialog = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalLayout onClose={onClose} labelledBy="asset-dialog-title" aria-busy={loading}>
|
||||
<ModalLayout onClose={onClose} labelledBy="asset-dialog-title" aria-busy={isLoading}>
|
||||
<DialogTitle />
|
||||
|
||||
<TabGroup
|
||||
@ -218,14 +246,16 @@ export const AssetDialog = ({
|
||||
<BrowseStep
|
||||
allowedTypes={allowedTypes}
|
||||
assets={assets}
|
||||
folder={folders}
|
||||
onSelectAsset={handleSelectAsset}
|
||||
selectedAssets={selectedAssets}
|
||||
multiple={multiple}
|
||||
onSelectAllAsset={handleSelectAllAssets}
|
||||
onEditAsset={canUpdate ? setAssetToEdit : undefined}
|
||||
pagination={data?.pagination}
|
||||
pagination={pagination}
|
||||
queryObject={queryObject}
|
||||
onChangeFilters={onChangeFilters}
|
||||
onChangeFolder={onChangeFolder}
|
||||
onChangePage={onChangePage}
|
||||
onChangePageSize={onChangePageSize}
|
||||
onChangeSort={onChangeSort}
|
||||
@ -262,6 +292,7 @@ AssetDialog.propTypes = {
|
||||
initiallySelectedAssets: PropTypes.arrayOf(AssetDefinition),
|
||||
multiple: PropTypes.bool,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onAddFolder: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onValidate: PropTypes.func.isRequired,
|
||||
trackedLocation: PropTypes.string,
|
||||
|
@ -3,27 +3,33 @@ import PropTypes from 'prop-types';
|
||||
import { AssetDialog } from '../AssetDialog';
|
||||
import { UploadAssetDialog } from '../UploadAssetDialog/UploadAssetDialog';
|
||||
|
||||
const Steps = {
|
||||
SelectAsset: 'SelectAsset',
|
||||
UploadAsset: 'UploadAsset',
|
||||
const STEPS = {
|
||||
AssetSelect: 'SelectAsset',
|
||||
AssetUpload: 'UploadAsset',
|
||||
FolderCreate: 'FolderCreate',
|
||||
};
|
||||
|
||||
export const MediaLibraryDialog = ({ onClose, onSelectAssets, allowedTypes }) => {
|
||||
const [step, setStep] = useState(Steps.SelectAsset);
|
||||
const [step, setStep] = useState(STEPS.AssetSelect);
|
||||
|
||||
if (step === Steps.SelectAsset) {
|
||||
if (step === STEPS.AssetSelect) {
|
||||
return (
|
||||
<AssetDialog
|
||||
allowedTypes={allowedTypes}
|
||||
onClose={onClose}
|
||||
onValidate={onSelectAssets}
|
||||
onAddAsset={() => setStep(Steps.UploadAsset)}
|
||||
onAddAsset={() => setStep(STEPS.AssetUpload)}
|
||||
onAddFolder={() => setStep(STEPS.FolderCreate)}
|
||||
multiple
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <UploadAssetDialog onClose={() => setStep(Steps.SelectAsset)} />;
|
||||
if (step === STEPS.FolderCreate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <UploadAssetDialog onClose={() => setStep(STEPS.AssetSelect)} />;
|
||||
};
|
||||
|
||||
MediaLibraryDialog.defaultProps = {
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
@ -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({
|
||||
|
@ -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({
|
||||
|
@ -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 };
|
||||
};
|
@ -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;
|
Loading…
x
Reference in New Issue
Block a user