mirror of
https://github.com/strapi/strapi.git
synced 2025-12-30 00:37:24 +00:00
commit
7f4e24ba39
@ -480,7 +480,7 @@ describe('<SearchURLQuery />', () => {
|
||||
expect(clearedUrlSearchQuery).toEqual('?page=1');
|
||||
});
|
||||
|
||||
it.only('should call trackUsage with trackedEvent props when submit', async () => {
|
||||
it('should call trackUsage with trackedEvent props when submit', async () => {
|
||||
const history = createMemoryHistory();
|
||||
const { container } = render(makeApp(history, 'thisEvent'));
|
||||
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
import React, { useState, useLayoutEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Searchbar, SearchForm } from '@strapi/design-system/Searchbar';
|
||||
import { IconButton } from '@strapi/design-system/IconButton';
|
||||
import SearchIcon from '@strapi/icons/Search';
|
||||
import getTrad from '../../../../../utils/getTrad';
|
||||
|
||||
const SearchAsset = ({ onChangeSearch, queryValue }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const [isOpen, setIsOpen] = useState(!!queryValue);
|
||||
const [value, setValue] = useState(queryValue || '');
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (isOpen) {
|
||||
setTimeout(() => {
|
||||
wrapperRef.current.querySelector('input').focus();
|
||||
}, 0);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleToggle = () => {
|
||||
setIsOpen(prev => !prev);
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
handleToggle();
|
||||
onChangeSearch(null);
|
||||
};
|
||||
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
onChangeSearch(value);
|
||||
};
|
||||
|
||||
if (isOpen) {
|
||||
return (
|
||||
<div ref={wrapperRef}>
|
||||
<SearchForm onSubmit={handleSubmit}>
|
||||
<Searchbar
|
||||
name="search"
|
||||
onClear={handleClear}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
clearLabel={formatMessage({
|
||||
id: getTrad('search.clear.label'),
|
||||
defaultMessage: 'Clear the search',
|
||||
})}
|
||||
size="S"
|
||||
value={value}
|
||||
placeholder={formatMessage({
|
||||
id: getTrad('search.placeholder'),
|
||||
defaultMessage: 'e.g: the first dog on the moon',
|
||||
})}
|
||||
>
|
||||
{formatMessage({ id: getTrad('search.label'), defaultMessage: 'Search for an asset' })}
|
||||
</Searchbar>
|
||||
</SearchForm>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <IconButton icon={<SearchIcon />} label="Search" onClick={handleToggle} />;
|
||||
};
|
||||
|
||||
SearchAsset.defaultProps = {
|
||||
queryValue: null,
|
||||
};
|
||||
|
||||
SearchAsset.propTypes = {
|
||||
onChangeSearch: PropTypes.func.isRequired,
|
||||
queryValue: PropTypes.string,
|
||||
};
|
||||
|
||||
export default SearchAsset;
|
||||
@ -0,0 +1,210 @@
|
||||
/**
|
||||
*
|
||||
* Tests for SearchAsset
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
import SearchAsset from '../index';
|
||||
|
||||
const handleChange = jest.fn();
|
||||
|
||||
const makeApp = queryValue => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en">
|
||||
<SearchAsset onChangeSearch={handleChange} queryValue={queryValue} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
describe('<SearchURLQuery />', () => {
|
||||
it('renders and matches the snapshot', () => {
|
||||
const { container } = render(makeApp(null));
|
||||
|
||||
expect(container).toMatchInlineSnapshot(`
|
||||
.c2 {
|
||||
border: 0;
|
||||
-webkit-clip: rect(0 0 0 0);
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #dcdce4;
|
||||
position: relative;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.c0 svg {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.c0 svg > g,
|
||||
.c0 svg path {
|
||||
fill: #ffffff;
|
||||
}
|
||||
|
||||
.c0[aria-disabled='true'] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.c0:after {
|
||||
-webkit-transition-property: all;
|
||||
transition-property: all;
|
||||
-webkit-transition-duration: 0.2s;
|
||||
transition-duration: 0.2s;
|
||||
border-radius: 8px;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
bottom: -4px;
|
||||
left: -4px;
|
||||
right: -4px;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.c0:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.c0:focus-visible:after {
|
||||
border-radius: 8px;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
bottom: -5px;
|
||||
left: -5px;
|
||||
right: -5px;
|
||||
border: 2px solid #4945ff;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.c1 svg > g,
|
||||
.c1 svg path {
|
||||
fill: #8e8ea9;
|
||||
}
|
||||
|
||||
.c1:hover svg > g,
|
||||
.c1:hover svg path {
|
||||
fill: #666687;
|
||||
}
|
||||
|
||||
.c1:active svg > g,
|
||||
.c1:active svg path {
|
||||
fill: #a5a5ba;
|
||||
}
|
||||
|
||||
.c1[aria-disabled='true'] {
|
||||
background-color: #eaeaef;
|
||||
}
|
||||
|
||||
.c1[aria-disabled='true'] svg path {
|
||||
fill: #666687;
|
||||
}
|
||||
|
||||
<div>
|
||||
<span>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-labelledby="tooltip-1"
|
||||
class="c0 c1"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M23.813 20.163l-5.3-5.367a9.792 9.792 0 001.312-4.867C19.825 4.455 15.375 0 9.913 0 4.45 0 0 4.455 0 9.929c0 5.473 4.45 9.928 9.912 9.928a9.757 9.757 0 005.007-1.4l5.275 5.35a.634.634 0 00.913 0l2.706-2.737a.641.641 0 000-.907zM9.91 3.867c3.338 0 6.05 2.718 6.05 6.061s-2.712 6.061-6.05 6.061c-3.337 0-6.05-2.718-6.05-6.06 0-3.344 2.713-6.062 6.05-6.062z"
|
||||
fill="#32324D"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
<div
|
||||
class="c2"
|
||||
>
|
||||
<p
|
||||
aria-live="polite"
|
||||
aria-relevant="all"
|
||||
id="live-region-log"
|
||||
role="log"
|
||||
/>
|
||||
<p
|
||||
aria-live="polite"
|
||||
aria-relevant="all"
|
||||
id="live-region-status"
|
||||
role="status"
|
||||
/>
|
||||
<p
|
||||
aria-live="assertive"
|
||||
aria-relevant="all"
|
||||
id="live-region-alert"
|
||||
role="alert"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
|
||||
it('should set input value to queryValue if it exists', () => {
|
||||
const queryValue = 'michka';
|
||||
const { container } = render(makeApp(queryValue));
|
||||
|
||||
const input = container.querySelector('input[name="search"]');
|
||||
|
||||
expect(input).toBeInTheDocument();
|
||||
expect(input.value).toEqual(queryValue);
|
||||
});
|
||||
|
||||
it('should call handleChange when submitting search input', () => {
|
||||
const { container } = render(makeApp(null));
|
||||
|
||||
fireEvent.click(container.querySelector('button[type="button"]'));
|
||||
const input = container.querySelector('input[name="search"]');
|
||||
|
||||
fireEvent.change(input, { target: { value: 'michka' } });
|
||||
fireEvent.submit(input);
|
||||
|
||||
expect(handleChange.mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@ -4,18 +4,20 @@ import { useIntl } from 'react-intl';
|
||||
import { Flex } from '@strapi/design-system/Flex';
|
||||
import { Stack } from '@strapi/design-system/Stack';
|
||||
import { BaseCheckbox } from '@strapi/design-system/BaseCheckbox';
|
||||
import { AssetList } from '../../../AssetList';
|
||||
import getTrad from '../../../../utils/getTrad';
|
||||
import { AssetList } from '../../../AssetList';
|
||||
import SortPicker from '../../../SortPicker';
|
||||
import getAllowedFiles from '../../utils/getAllowedFiles';
|
||||
import PaginationFooter from './PaginationFooter';
|
||||
import PageSize from './PageSize';
|
||||
import SearchAsset from './SearchAsset';
|
||||
import getAllowedFiles from '../../utils/getAllowedFiles';
|
||||
|
||||
export const BrowseStep = ({
|
||||
allowedTypes,
|
||||
assets,
|
||||
onChangePage,
|
||||
onChangePageSize,
|
||||
onChangeSearch,
|
||||
onChangeSort,
|
||||
onEditAsset,
|
||||
onSelectAllAsset,
|
||||
@ -38,27 +40,30 @@ export const BrowseStep = ({
|
||||
<>
|
||||
<Stack size={4}>
|
||||
{onSelectAllAsset && (
|
||||
<Stack horizontal size={2}>
|
||||
<Flex
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
background="neutral0"
|
||||
hasRadius
|
||||
borderColor="neutral200"
|
||||
height={`${32 / 16}rem`}
|
||||
>
|
||||
<BaseCheckbox
|
||||
aria-label={formatMessage({
|
||||
id: getTrad('bulk.select.label'),
|
||||
defaultMessage: 'Select all assets',
|
||||
})}
|
||||
indeterminate={!areAllAssetSelected && hasSomeAssetSelected}
|
||||
value={areAllAssetSelected}
|
||||
onChange={onSelectAllAsset}
|
||||
/>
|
||||
</Flex>
|
||||
<SortPicker onChangeSort={onChangeSort} />
|
||||
</Stack>
|
||||
<Flex justifyContent="space-between">
|
||||
<Stack horizontal size={2}>
|
||||
<Flex
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
background="neutral0"
|
||||
hasRadius
|
||||
borderColor="neutral200"
|
||||
height={`${32 / 16}rem`}
|
||||
>
|
||||
<BaseCheckbox
|
||||
aria-label={formatMessage({
|
||||
id: getTrad('bulk.select.label'),
|
||||
defaultMessage: 'Select all assets',
|
||||
})}
|
||||
indeterminate={!areAllAssetSelected && hasSomeAssetSelected}
|
||||
value={areAllAssetSelected}
|
||||
onChange={onSelectAllAsset}
|
||||
/>
|
||||
</Flex>
|
||||
<SortPicker onChangeSort={onChangeSort} />
|
||||
</Stack>
|
||||
<SearchAsset onChangeSearch={onChangeSearch} queryValue={queryObject._q || ''} />
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<AssetList
|
||||
@ -93,12 +98,14 @@ BrowseStep.propTypes = {
|
||||
onChangePage: PropTypes.func.isRequired,
|
||||
onChangePageSize: PropTypes.func.isRequired,
|
||||
onChangeSort: PropTypes.func.isRequired,
|
||||
onChangeSearch: PropTypes.func.isRequired,
|
||||
onEditAsset: PropTypes.func.isRequired,
|
||||
onSelectAsset: PropTypes.func.isRequired,
|
||||
onSelectAllAsset: PropTypes.func,
|
||||
queryObject: PropTypes.shape({
|
||||
page: PropTypes.number.isRequired,
|
||||
pageSize: PropTypes.number.isRequired,
|
||||
_q: PropTypes.string,
|
||||
}).isRequired,
|
||||
pagination: PropTypes.shape({ pageCount: PropTypes.number.isRequired }).isRequired,
|
||||
selectedAssets: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
||||
|
||||
@ -33,7 +33,7 @@ export const AssetDialog = ({
|
||||
const { canRead, canCreate, isLoading: isLoadingPermissions } = useMediaLibraryPermissions();
|
||||
const [
|
||||
{ rawQuery, queryObject },
|
||||
{ onChangePage, onChangePageSize, onChangeSort },
|
||||
{ onChangePage, onChangePageSize, onChangeSort, onChangeSearch },
|
||||
] = useModalQueryParams();
|
||||
const { data, isLoading, error } = useModalAssets({ skipWhen: !canRead, rawQuery });
|
||||
|
||||
@ -102,7 +102,7 @@ export const AssetDialog = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (canRead && assets?.length === 0) {
|
||||
if (canRead && assets?.length === 0 && !queryObject._q) {
|
||||
return (
|
||||
<ModalLayout onClose={onClose} labelledBy="asset-dialog-title">
|
||||
<DialogTitle />
|
||||
@ -189,6 +189,7 @@ export const AssetDialog = ({
|
||||
onChangePage={onChangePage}
|
||||
onChangePageSize={onChangePageSize}
|
||||
onChangeSort={onChangeSort}
|
||||
onChangeSearch={onChangeSearch}
|
||||
/>
|
||||
</ModalBody>
|
||||
</TabPanel>
|
||||
|
||||
@ -21,12 +21,29 @@ const useModalQueryParams = () => {
|
||||
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 }) },
|
||||
{
|
||||
onChangePage: handeChangePage,
|
||||
onChangePageSize: handleChangePageSize,
|
||||
onChangeSort: handleChangeSort,
|
||||
onChangeSearch: handleChangeSearch,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@ -77,6 +77,7 @@
|
||||
"plugin.description.long": "Media file management.",
|
||||
"plugin.description.short": "Media file management.",
|
||||
"plugin.name": "Media Library",
|
||||
"search.clear.label": "Clear the search",
|
||||
"search.label": "Search for an asset",
|
||||
"search.placeholder": "e.g: the first dog on the moon",
|
||||
"settings.form.autoOrientation.description": "Automatically rotate image according to EXIF orientation tag",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user