mirror of
https://github.com/strapi/strapi.git
synced 2025-09-18 21:08:54 +00:00
Merge branch 'market-sort-filters/sort-filter' of github.com:strapi/strapi into market-sort-filters/api-filter-requests
This commit is contained in:
commit
f96a07dae2
@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { Box } from '@strapi/design-system/Box';
|
import { Box } from '@strapi/design-system/Box';
|
||||||
@ -18,6 +19,10 @@ const FilterTag = ({ name, handleRemove }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ButtonToggle = styled(Button)`
|
||||||
|
height: ${({ theme }) => theme.sizes.input.S};
|
||||||
|
`;
|
||||||
|
|
||||||
const NpmPackagesFilters = ({
|
const NpmPackagesFilters = ({
|
||||||
possibleCollections,
|
possibleCollections,
|
||||||
possibleCategories,
|
possibleCategories,
|
||||||
@ -35,7 +40,7 @@ const NpmPackagesFilters = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box paddingTop={1} paddingBottom={1}>
|
<Box paddingTop={1} paddingBottom={1}>
|
||||||
<Button
|
<ButtonToggle
|
||||||
variant="tertiary"
|
variant="tertiary"
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
startIcon={<Filter />}
|
startIcon={<Filter />}
|
||||||
@ -43,7 +48,7 @@ const NpmPackagesFilters = ({
|
|||||||
size="S"
|
size="S"
|
||||||
>
|
>
|
||||||
{formatMessage({ id: 'app.utils.filters', defaultMessage: 'Filters' })}
|
{formatMessage({ id: 'app.utils.filters', defaultMessage: 'Filters' })}
|
||||||
</Button>
|
</ButtonToggle>
|
||||||
{isVisible && (
|
{isVisible && (
|
||||||
<FiltersPopover
|
<FiltersPopover
|
||||||
onToggle={handleToggle}
|
onToggle={handleToggle}
|
||||||
|
@ -1,137 +1,75 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Select, Option } from '@strapi/design-system/Select';
|
||||||
|
import { Box } from '@strapi/design-system/Box';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Option } from '@strapi/design-system/Select';
|
|
||||||
import { Popover } from '@strapi/design-system/Popover';
|
|
||||||
import { Button } from '@strapi/design-system/Button';
|
|
||||||
import { FocusTrap } from '@strapi/design-system/FocusTrap';
|
|
||||||
import CarretDown from '@strapi/icons/CarretDown';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const SortOption = styled(Option)`
|
const SelectWrapper = styled(Box)`
|
||||||
list-style-type: none;
|
font-weight: ${({ theme }) => theme.fontWeights.semiBold};
|
||||||
`;
|
|
||||||
|
|
||||||
const SortToggleButton = styled(Button)`
|
span {
|
||||||
svg {
|
font-size: ${({ theme }) => theme.fontSizes[1]};
|
||||||
width: ${({ theme }) => theme.spaces[2]};
|
|
||||||
height: ${({ theme }) => theme.spaces[1]};
|
|
||||||
}
|
|
||||||
|
|
||||||
svg > path {
|
|
||||||
fill: ${({ theme }) => theme.colors.neutral500};
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SortSelect = ({ sortQuery, setQuery, setTabQuery, npmPackageType }) => {
|
const SortSelect = ({ sortQuery, setQuery, setTabQuery, npmPackageType }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const buttonRef = useRef();
|
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
|
||||||
|
|
||||||
const sortTypes = {
|
const sortTypes = {
|
||||||
'name:asc': {
|
'name:asc': {
|
||||||
id: 'admin.pages.MarketPlacePage.sort.alphabetical',
|
selected: {
|
||||||
defaultMessage: 'Alphabetical order',
|
id: 'admin.pages.MarketPlacePage.sort.alphabetical.selected',
|
||||||
|
defaultMessage: 'Sort by alphabetical order',
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
id: 'admin.pages.MarketPlacePage.sort.alphabetical',
|
||||||
|
defaultMessage: 'Alphabetical order',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'submissionDate:desc': {
|
'submissionDate:desc': {
|
||||||
id: 'admin.pages.MarketPlacePage.sort.newest',
|
selected: {
|
||||||
defaultMessage: 'Newest',
|
id: 'admin.pages.MarketPlacePage.sort.newest.selected',
|
||||||
|
defaultMessage: 'Sort by newest',
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
id: 'admin.pages.MarketPlacePage.sort.newest',
|
||||||
|
defaultMessage: 'Newest',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggle = () => setIsVisible((prev) => !prev);
|
|
||||||
|
|
||||||
const handleBlur = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
||||||
setIsVisible(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSortClick = (sortName) => {
|
|
||||||
setQuery({ sort: sortName });
|
|
||||||
setTabQuery((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[npmPackageType]: { ...prev[npmPackageType], sort: sortName, npmPackageType },
|
|
||||||
}));
|
|
||||||
handleToggle();
|
|
||||||
};
|
|
||||||
|
|
||||||
const computeSortMessage = (sortName) => {
|
|
||||||
const defaultSortLabel = formatMessage({
|
|
||||||
id: 'admin.pages.MarketPlacePage.sort.sortBy',
|
|
||||||
defaultMessage: 'Sort by',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (sortName) {
|
|
||||||
const sortInfo = sortTypes[sortName];
|
|
||||||
const message = formatMessage({
|
|
||||||
id: sortInfo.id,
|
|
||||||
defaultMessage: sortInfo.defaultMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
return `${defaultSortLabel}: ${message}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultSortLabel;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<SelectWrapper>
|
||||||
<SortToggleButton
|
<Select
|
||||||
aria-label={computeSortMessage(sortQuery)}
|
|
||||||
aria-controls="sort-by-values"
|
|
||||||
aria-haspopup="dialog"
|
|
||||||
aria-expanded={isVisible}
|
|
||||||
aria-disabled={false}
|
|
||||||
onClick={handleToggle}
|
|
||||||
variant="tertiary"
|
|
||||||
ref={buttonRef}
|
|
||||||
endIcon={<CarretDown aria-hidden />}
|
|
||||||
size="S"
|
size="S"
|
||||||
|
id="sort-by-select"
|
||||||
|
value={sortQuery}
|
||||||
|
customizeContent={() => formatMessage(sortTypes[sortQuery].selected)}
|
||||||
|
onChange={(sortName) => {
|
||||||
|
setQuery({ sort: sortName });
|
||||||
|
setTabQuery((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[npmPackageType]: { ...prev[npmPackageType], sort: sortName, npmPackageType },
|
||||||
|
}));
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{computeSortMessage(sortQuery)}
|
{Object.entries(sortTypes).map(([sortName, messages]) => {
|
||||||
</SortToggleButton>
|
return (
|
||||||
{isVisible && (
|
<Option key={sortName} value={sortName}>
|
||||||
<Popover
|
{formatMessage(messages.option)}
|
||||||
role="dialog"
|
</Option>
|
||||||
id="sort-by-values"
|
);
|
||||||
onBlur={handleBlur}
|
})}
|
||||||
source={buttonRef}
|
</Select>
|
||||||
spacing={4}
|
</SelectWrapper>
|
||||||
>
|
|
||||||
<FocusTrap onEscape={handleToggle}>
|
|
||||||
{Object.entries(sortTypes).map(([sortName, sortInfo], index) => {
|
|
||||||
const { id, defaultMessage } = sortInfo;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SortOption
|
|
||||||
key={sortName}
|
|
||||||
value={sortName}
|
|
||||||
selected={sortQuery === sortName}
|
|
||||||
onClick={() => handleSortClick(sortName)}
|
|
||||||
tabIndex={index}
|
|
||||||
>
|
|
||||||
{formatMessage({ id, defaultMessage })}
|
|
||||||
</SortOption>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</FocusTrap>
|
|
||||||
</Popover>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
SortSelect.defaultProps = {
|
|
||||||
sortQuery: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
SortSelect.propTypes = {
|
SortSelect.propTypes = {
|
||||||
|
sortQuery: PropTypes.string.isRequired,
|
||||||
setQuery: PropTypes.func.isRequired,
|
setQuery: PropTypes.func.isRequired,
|
||||||
setTabQuery: PropTypes.func.isRequired,
|
setTabQuery: PropTypes.func.isRequired,
|
||||||
sortQuery: PropTypes.string,
|
|
||||||
npmPackageType: PropTypes.string.isRequired,
|
npmPackageType: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ const MarketPlacePage = () => {
|
|||||||
} else {
|
} else {
|
||||||
setQuery(
|
setQuery(
|
||||||
{
|
{
|
||||||
npmPackageType: null,
|
npmPackageType: selectedTab,
|
||||||
// Clear filters
|
// Clear filters
|
||||||
collections: [],
|
collections: [],
|
||||||
categories: [],
|
categories: [],
|
||||||
@ -194,7 +194,6 @@ const MarketPlacePage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(tabQuery);
|
|
||||||
// Check if plugins and providers are installed already
|
// Check if plugins and providers are installed already
|
||||||
const installedPackageNames = Object.keys(dependencies);
|
const installedPackageNames = Object.keys(dependencies);
|
||||||
|
|
||||||
@ -266,7 +265,7 @@ const MarketPlacePage = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
<Flex paddingBottom={4} gap={2}>
|
<Flex paddingBottom={4} gap={2}>
|
||||||
<SortSelect
|
<SortSelect
|
||||||
sortQuery={query?.sort}
|
sortQuery={query?.sort || 'name:asc'}
|
||||||
setQuery={setQuery}
|
setQuery={setQuery}
|
||||||
setTabQuery={setTabQuery}
|
setTabQuery={setTabQuery}
|
||||||
npmPackageType={npmPackageType}
|
npmPackageType={npmPackageType}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -287,7 +287,7 @@ describe('Marketplace page', () => {
|
|||||||
it('shows the correct options on sort select', () => {
|
it('shows the correct options on sort select', () => {
|
||||||
render(App);
|
render(App);
|
||||||
const sortButton = screen.getByRole('button', { name: /Sort by/i });
|
const sortButton = screen.getByRole('button', { name: /Sort by/i });
|
||||||
fireEvent.click(sortButton);
|
fireEvent.mouseDown(sortButton);
|
||||||
|
|
||||||
const alphabeticalOption = screen.getByRole('option', { name: 'Alphabetical order' });
|
const alphabeticalOption = screen.getByRole('option', { name: 'Alphabetical order' });
|
||||||
const newestOption = screen.getByRole('option', { name: 'Newest' });
|
const newestOption = screen.getByRole('option', { name: 'Newest' });
|
||||||
@ -296,10 +296,11 @@ describe('Marketplace page', () => {
|
|||||||
expect(newestOption).toBeVisible();
|
expect(newestOption).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sort by newest changes the url on click', () => {
|
it('changes the url on sort option select', () => {
|
||||||
render(App);
|
render(App);
|
||||||
const sortButton = screen.getByRole('button', { name: /Sort by/i });
|
const sortButton = screen.getByRole('button', { name: /Sort by/i });
|
||||||
fireEvent.click(sortButton);
|
fireEvent.mouseDown(sortButton);
|
||||||
|
|
||||||
const newestOption = screen.getByRole('option', { name: 'Newest' });
|
const newestOption = screen.getByRole('option', { name: 'Newest' });
|
||||||
fireEvent.click(newestOption);
|
fireEvent.click(newestOption);
|
||||||
expect(history.location.search).toEqual('?npmPackageType=provider&sort=submissionDate:desc');
|
expect(history.location.search).toEqual('?npmPackageType=provider&sort=submissionDate:desc');
|
||||||
|
@ -280,9 +280,10 @@
|
|||||||
"admin.pages.MarketPlacePage.tab-group.label": "Plugins and Providers for Strapi",
|
"admin.pages.MarketPlacePage.tab-group.label": "Plugins and Providers for Strapi",
|
||||||
"admin.pages.MarketPlacePage.missingPlugin.title": "Missing a plugin?",
|
"admin.pages.MarketPlacePage.missingPlugin.title": "Missing a plugin?",
|
||||||
"admin.pages.MarketPlacePage.missingPlugin.description": "Tell us what plugin you are looking for and we'll let our community plugin developers know in case they are in search for inspiration!",
|
"admin.pages.MarketPlacePage.missingPlugin.description": "Tell us what plugin you are looking for and we'll let our community plugin developers know in case they are in search for inspiration!",
|
||||||
"admin.pages.MarketPlacePage.sort.sortBy": "Sort by",
|
|
||||||
"admin.pages.MarketPlacePage.sort.alphabetical": "Alphabetical order",
|
"admin.pages.MarketPlacePage.sort.alphabetical": "Alphabetical order",
|
||||||
"admin.pages.MarketPlacePage.sort.newest": "Newest",
|
"admin.pages.MarketPlacePage.sort.newest": "Newest",
|
||||||
|
"admin.pages.MarketPlacePage.sort.alphabetical.selected": "Sort by alphabetical order",
|
||||||
|
"admin.pages.MarketPlacePage.sort.newest.selected": "Sort by newest",
|
||||||
"admin.pages.MarketPlacePage.filters.collections": "Collections",
|
"admin.pages.MarketPlacePage.filters.collections": "Collections",
|
||||||
"admin.pages.MarketPlacePage.filters.collectionsSelected": "{count, plural, =0 {No collections} one {# collection} other {# collections}} selected",
|
"admin.pages.MarketPlacePage.filters.collectionsSelected": "{count, plural, =0 {No collections} one {# collection} other {# collections}} selected",
|
||||||
"admin.pages.MarketPlacePage.filters.categories": "Categories",
|
"admin.pages.MarketPlacePage.filters.categories": "Categories",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user