1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-04 13:48:56 +02:00

chore: split standard and advanced strategy types (#10433)

https://linear.app/unleash/issue/2-3733/update-strategy-types-to-match-the-new-designs

This updates our strategy types page to match the new designs.

Part of this means visually separating what we are considering
"standard" strategies from "advanced" strategies.

<img width="1520" height="981" alt="image"
src="https://github.com/user-attachments/assets/2682013b-d9df-453d-9427-62871e74d46a"
/>
This commit is contained in:
Nuno Góis 2025-07-30 09:55:51 +01:00 committed by GitHub
parent 0f565c50e9
commit c5b37fc7c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 173 additions and 73 deletions

View File

@ -31,6 +31,7 @@ import { Badge } from 'component/common/Badge/Badge';
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
import { CustomStrategyInfo } from '../CustomStrategyInfo/CustomStrategyInfo.tsx';
import { AddStrategyButton } from './AddStrategyButton/AddStrategyButton.tsx';
import { usePageTitle } from 'hooks/usePageTitle.ts';
interface IDialogueMetaData {
show: boolean;
@ -38,89 +39,70 @@ interface IDialogueMetaData {
onConfirm: () => void;
}
const StyledBox = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2),
}));
const StyledBadge = styled(Badge)(({ theme }) => ({
marginLeft: theme.spacing(1),
display: 'inline-block',
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
const StyledTitle = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: theme.spacing(1),
fontSize: theme.fontSizes.mainHeader,
width: '100%',
}));
const Subtitle: FC<{
const StyledSubtitle = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: theme.spacing(2),
'& > span': {
fontWeight: theme.fontWeight.bold,
},
}));
const Title: FC<{
title: string;
description: string;
link: string;
}> = ({ title, description, link }) => (
<StyledTypography>
<StyledTitle>
{title}
<HelpIcon
htmlTooltip
tooltip={
<>
<Typography
variant='body2'
component='p'
sx={(theme) => ({ marginBottom: theme.spacing(1) })}
>
<Typography variant='body2' component='p' sx={{ mb: 1 }}>
{description}
</Typography>
<Link href={link} target='_blank' variant='body2'>
<Link
href={link}
target='_blank'
rel='noopener noreferrer'
variant='body2'
>
Read more in the documentation
</Link>
</>
}
/>
</StyledTypography>
);
const CustomStrategyTitle: FC = () => (
<Box
sx={(theme) => ({
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing(1.5),
})}
>
<Subtitle
title='Custom strategies'
description='Custom activation strategies let you define your own activation strategies to use with Unleash.'
link='https://docs.getunleash.io/reference/custom-activation-strategies'
/>
<AddStrategyButton />
</Box>
);
const PredefinedStrategyTitle = () => (
<Box>
<Subtitle
title='Predefined strategies'
description='Activation strategies let you enable a feature only for a specified audience. Different strategies use different parameters. Predefined strategies are bundled with Unleash.'
link='https://docs.getunleash.io/reference/activation-strategies'
/>
</Box>
</StyledTitle>
);
const StrategyDeprecationWarning = () => (
<Alert severity='warning' sx={{ mb: 2 }}>
Custom strategies are deprecated and may be removed in a future major
version. We recommend not using custom strategies going forward and
instead using the predefined strategies with{' '}
instead using the gradual rollout strategy with{' '}
<Link
href={
'https://docs.getunleash.io/reference/activation-strategies#constraints'
}
target='_blank'
variant='body2'
rel='noopener noreferrer'
>
constraints
</Link>
@ -129,6 +111,28 @@ const StrategyDeprecationWarning = () => (
</Alert>
);
const RecommendationAlert = () => (
<Alert severity='info' sx={{ mb: 2 }}>
We recommend using gradual rollout. You can customize it with{' '}
<Link
href='https://docs.getunleash.io/reference/activation-strategies#constraints'
target='_blank'
rel='noopener noreferrer'
>
constraints
</Link>{' '}
and{' '}
<Link
href='https://docs.getunleash.io/reference/activation-strategies#variants'
target='_blank'
rel='noopener noreferrer'
>
variants
</Link>
.
</Alert>
);
export const StrategiesList = () => {
const navigate = useNavigate();
const [dialogueMetaData, setDialogueMetaData] = useState<IDialogueMetaData>(
@ -144,6 +148,8 @@ export const StrategiesList = () => {
useStrategiesApi();
const { setToastData, setToastApiError } = useToast();
usePageTitle('Strategy types');
const data = useMemo(() => {
if (loading) {
const mock = Array(5).fill({
@ -152,22 +158,28 @@ export const StrategiesList = () => {
});
return {
all: mock,
predefined: mock,
standard: mock,
advanced: mock,
custom: mock,
};
}
const all = strategies.map(
({ name, description, editable, deprecated }) => ({
({ name, description, editable, deprecated, advanced }) => ({
name,
description,
editable,
deprecated,
advanced,
}),
);
const predefined = all.filter((strategy) => !strategy.editable);
return {
all,
predefined: all.filter((strategy) => !strategy.editable),
standard: predefined.filter((strategy) => !strategy.advanced),
advanced: predefined.filter((strategy) => strategy.advanced),
custom: all.filter((strategy) => strategy.editable),
};
}, [strategies, loading]);
@ -350,19 +362,43 @@ export const StrategiesList = () => {
[],
);
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable(
{
columns: columns as any[], // TODO: fix after `react-table` v8 update
data: data.predefined,
initialState,
sortTypes,
autoResetSortBy: false,
disableSortRemove: true,
autoResetHiddenColumns: false,
},
useSortBy,
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows: standardRows,
prepareRow,
} = useTable(
{
columns: columns as any[], // TODO: fix after `react-table` v8 update
data: data.standard,
initialState,
sortTypes,
autoResetSortBy: false,
disableSortRemove: true,
autoResetHiddenColumns: false,
},
useSortBy,
);
const {
getTableProps: advancedGetTableProps,
getTableBodyProps: advancedGetTableBodyProps,
headerGroups: advancedHeaderGroups,
rows: advancedRows,
prepareRow: advancedPrepareRow,
} = useTable(
{
columns: columns as any[], // TODO: fix after `react-table` v8 update
data: data.advanced,
initialState,
sortTypes,
autoResetSortBy: false,
disableSortRemove: true,
autoResetHiddenColumns: false,
},
useSortBy,
);
const {
getTableProps: customGetTableProps,
@ -392,21 +428,25 @@ export const StrategiesList = () => {
};
return (
<StyledBox>
<>
<PageContent
isLoading={loading}
header={
<PageHeader
titleElement={<PredefinedStrategyTitle />}
title='Strategy types'
/>
<PageHeader>
<Title
title='Standard strategies'
description='Standard strategies let you enable a feature only for a specified audience. Select a starting setup, then customize your strategy with targeting and variants.'
link='https://docs.getunleash.io/reference/activation-strategies'
/>
</PageHeader>
}
>
<Box>
<RecommendationAlert />
<Table {...getTableProps()}>
<SortableTableHeader headerGroups={headerGroups} />
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
{standardRows.map((row) => {
prepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
@ -430,7 +470,7 @@ export const StrategiesList = () => {
</TableBody>
</Table>
<ConditionallyRender
condition={rows.length === 0}
condition={standardRows.length === 0}
show={
<TablePlaceholder>
No strategies available.
@ -455,11 +495,61 @@ export const StrategiesList = () => {
isLoading={loading}
header={
<PageHeader>
<CustomStrategyTitle />
<Title
title='Advanced and custom strategies'
description='Advanced strategies let you target based on specific properties. Custom activation strategies let you define your own activation strategies to use with Unleash.'
link='https://docs.getunleash.io/reference/custom-activation-strategies'
/>
</PageHeader>
}
sx={{ mt: 2 }}
>
<Box>
<StyledSubtitle>
<span>Advanced strategies</span>
</StyledSubtitle>
<Table {...advancedGetTableProps()}>
<SortableTableHeader
headerGroups={advancedHeaderGroups}
/>
<TableBody {...advancedGetTableBodyProps()}>
{advancedRows.map((row) => {
advancedPrepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
<TableRow hover key={key} {...rowProps}>
{row.cells.map((cell) => {
const { key, ...cellProps } =
cell.getCellProps();
return (
<TableCell
key={key}
{...cellProps}
>
{cell.render('Cell')}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
<ConditionallyRender
condition={advancedRows.length === 0}
show={
<TablePlaceholder>
No advanced strategies available.
</TablePlaceholder>
}
/>
</Box>
<Box>
<StyledSubtitle sx={{ mt: 4 }}>
<span>Custom strategies</span>
<AddStrategyButton />
</StyledSubtitle>
<StrategyDeprecationWarning />
<Table {...customGetTableProps()}>
<SortableTableHeader
@ -507,6 +597,6 @@ export const StrategiesList = () => {
}
/>
</PageContent>
</StyledBox>
</>
);
};

View File

@ -11,6 +11,13 @@ interface IUseStrategiesOutput {
error?: Error;
}
const STANDARD_STRATEGIES = ['flexibleRollout', 'default'];
const mapAdvancedStrategies = (strategies: IStrategy[]): IStrategy[] =>
strategies.map((strategy) => ({
...strategy,
advanced: !STANDARD_STRATEGIES.includes(strategy.name),
}));
export const useStrategies = (): IUseStrategiesOutput => {
const { data, error } = useSWR(STRATEGIES_PATH, fetcher);
@ -19,7 +26,9 @@ export const useStrategies = (): IUseStrategiesOutput => {
}, []);
return {
strategies: data?.strategies || defaultStrategies,
strategies: mapAdvancedStrategies(
data?.strategies || defaultStrategies,
),
refetchStrategies,
loading: !error && !data,
error,

View File

@ -41,6 +41,7 @@ export interface IStrategy {
displayName: string;
editable: boolean;
deprecated: boolean;
advanced?: boolean;
description: string;
parameters: IStrategyParameter[];
}