1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-24 17:51:14 +02:00

chore: limit total custom strategies displayed (#10688)

https://linear.app/unleash/issue/2-3897/limit-custom-strategies-like-were-doing-with-release-templates

Limits total custom strategies displayed, like we're doing for release
templates, in the new "add strategy" modal.

Added a more generic logic to `FeatureStrategyMenuCardsSection.tsx` so
we can reuse it for both.

We can also do it for other sections in the modal, but that feels like a
premature optimization. These 2 categories are the ones that are user
owned, and can have many items.
This commit is contained in:
Nuno Góis 2025-09-24 13:06:49 +01:00 committed by GitHub
parent e98b511bb2
commit 3d9f5581d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 63 additions and 42 deletions

View File

@ -38,6 +38,8 @@ const FILTERS = [
export type StrategyFilterValue = (typeof FILTERS)[number]['value']; export type StrategyFilterValue = (typeof FILTERS)[number]['value'];
const CUSTOM_STRATEGY_DISPLAY_LIMIT = 5;
const StyledContainer = styled(Box)(() => ({ const StyledContainer = styled(Box)(() => ({
width: '100%', width: '100%',
display: 'flex', display: 'flex',
@ -118,6 +120,9 @@ export const FeatureStrategyMenuCards = ({
(strategy) => strategy.editable, (strategy) => strategy.editable,
); );
const customStrategyDisplayLimit =
filter === 'custom' ? 0 : CUSTOM_STRATEGY_DISPLAY_LIMIT;
const availableFilters = useMemo( const availableFilters = useMemo(
() => () =>
FILTERS.filter(({ value }) => { FILTERS.filter(({ value }) => {
@ -281,7 +286,11 @@ export const FeatureStrategyMenuCards = ({
</FeatureStrategyMenuCardsSection> </FeatureStrategyMenuCardsSection>
)} )}
{shouldRender('custom') && customStrategies.length > 0 && ( {shouldRender('custom') && customStrategies.length > 0 && (
<FeatureStrategyMenuCardsSection title='Custom strategies'> <FeatureStrategyMenuCardsSection
title='Custom strategies'
limit={customStrategyDisplayLimit}
viewMore={() => setFilter('custom')}
>
{customStrategies.map(renderStrategy)} {customStrategies.map(renderStrategy)}
</FeatureStrategyMenuCardsSection> </FeatureStrategyMenuCardsSection>
)} )}

View File

@ -2,7 +2,7 @@ import { useReleasePlanTemplates } from 'hooks/api/getters/useReleasePlanTemplat
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { ReactComponent as ReleaseTemplateIcon } from 'assets/img/releaseTemplates.svg'; import { ReactComponent as ReleaseTemplateIcon } from 'assets/img/releaseTemplates.svg';
import type { IReleasePlanTemplate } from 'interfaces/releasePlans.ts'; import type { IReleasePlanTemplate } from 'interfaces/releasePlans.ts';
import { Box, Button, styled } from '@mui/material'; import { Box, styled } from '@mui/material';
import type { StrategyFilterValue } from './FeatureStrategyMenuCards.tsx'; import type { StrategyFilterValue } from './FeatureStrategyMenuCards.tsx';
import type { Dispatch, SetStateAction } from 'react'; import type { Dispatch, SetStateAction } from 'react';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
@ -74,16 +74,6 @@ const StyledNoTemplatesDescription = styled('p')(({ theme }) => ({
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
})); }));
const StyledViewMoreButton = styled(Button)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: theme.spacing(10),
padding: theme.spacing(2),
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.spacing(1),
}));
const StyledLink = styled(RouterLink)({ const StyledLink = styled(RouterLink)({
textDecoration: 'none', textDecoration: 'none',
'&:hover': { '&:hover': {
@ -113,10 +103,9 @@ export const FeatureStrategyMenuCardsReleaseTemplates = ({
const isFiltered = filter === 'releaseTemplates'; const isFiltered = filter === 'releaseTemplates';
const shouldShowHeader = !isFiltered || templates.length > 0; const shouldShowHeader = !isFiltered || templates.length > 0;
const releaseTemplatesDisplayLimit = isFiltered
const slicedTemplates = isFiltered ? 0
? templates : RELEASE_TEMPLATE_DISPLAY_LIMIT;
: templates.slice(0, RELEASE_TEMPLATE_DISPLAY_LIMIT);
return ( return (
<Box> <Box>
@ -153,8 +142,12 @@ export const FeatureStrategyMenuCardsReleaseTemplates = ({
</StyledNoTemplatesBody> </StyledNoTemplatesBody>
</StyledNoTemplatesContainer> </StyledNoTemplatesContainer>
) : ( ) : (
<FeatureStrategyMenuCardsSection> <FeatureStrategyMenuCardsSection
{slicedTemplates.map((template) => ( limit={releaseTemplatesDisplayLimit}
viewMore={() => setFilter('releaseTemplates')}
viewMoreLabel='View more templates'
>
{templates.map((template) => (
<FeatureStrategyMenuCard <FeatureStrategyMenuCard
key={template.id} key={template.id}
name={template.name} name={template.name}
@ -175,16 +168,6 @@ export const FeatureStrategyMenuCardsReleaseTemplates = ({
</FeatureStrategyMenuCardAction> </FeatureStrategyMenuCardAction>
</FeatureStrategyMenuCard> </FeatureStrategyMenuCard>
))} ))}
{slicedTemplates.length < templates.length &&
templates.length > RELEASE_TEMPLATE_DISPLAY_LIMIT && (
<StyledViewMoreButton
variant='text'
size='small'
onClick={() => setFilter('releaseTemplates')}
>
View more templates
</StyledViewMoreButton>
)}
</FeatureStrategyMenuCardsSection> </FeatureStrategyMenuCardsSection>
)} )}
</Box> </Box>

View File

@ -1,4 +1,4 @@
import { Box, styled } from '@mui/material'; import { Box, Button, styled } from '@mui/material';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
export const StyledStrategyModalSectionHeader = styled(Box)(({ theme }) => ({ export const StyledStrategyModalSectionHeader = styled(Box)(({ theme }) => ({
@ -17,23 +17,52 @@ const StyledStrategyModalSectionGrid = styled(Box)(({ theme }) => ({
width: '100%', width: '100%',
})); }));
const StyledViewMoreButton = styled(Button)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: theme.spacing(10),
padding: theme.spacing(2),
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.spacing(1),
}));
interface IFeatureStrategyMenuCardsSectionProps { interface IFeatureStrategyMenuCardsSectionProps {
title?: string; title?: string;
children: ReactNode; limit?: number;
viewMore?: () => void;
viewMoreLabel?: string;
children: ReactNode[];
} }
export const FeatureStrategyMenuCardsSection = ({ export const FeatureStrategyMenuCardsSection = ({
title, title,
limit,
viewMore,
viewMoreLabel = 'View more',
children, children,
}: IFeatureStrategyMenuCardsSectionProps) => ( }: IFeatureStrategyMenuCardsSectionProps) => {
<Box> const limitedChildren = limit ? children.slice(0, limit) : children;
{title && (
<StyledStrategyModalSectionHeader> return (
{title} <Box>
</StyledStrategyModalSectionHeader> {title && (
)} <StyledStrategyModalSectionHeader>
<StyledStrategyModalSectionGrid> {title}
{children} </StyledStrategyModalSectionHeader>
</StyledStrategyModalSectionGrid> )}
</Box> <StyledStrategyModalSectionGrid>
); {limitedChildren}
{viewMore && limitedChildren.length < children.length && (
<StyledViewMoreButton
variant='text'
size='small'
onClick={viewMore}
>
{viewMoreLabel}
</StyledViewMoreButton>
)}
</StyledStrategyModalSectionGrid>
</Box>
);
};