1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-10 01:16:39 +02:00

constraint values with tooltips

This commit is contained in:
Tymoteusz Czech 2025-03-21 12:58:35 +01:00
parent 6129565828
commit 7c472536aa
No known key found for this signature in database
GPG Key ID: 133555230D88D75F
6 changed files with 147 additions and 37 deletions

View File

@ -1,4 +1,4 @@
import { useMemo, type ComponentProps, type FC } from 'react';
import type { ComponentProps, FC } from 'react';
import { StrategyEvaluationItem } from '../StrategyEvaluationItem/StrategyEvaluationItem';
import type { ConstraintSchema } from 'openapi';
import { formatOperatorDescription } from 'component/common/ConstraintAccordion/ConstraintOperator/formatOperatorDescription';
@ -8,7 +8,7 @@ import { Truncator } from 'component/common/Truncator/Truncator';
import { ValuesList } from '../ValuesList/ValuesList';
import { useLocationSettings } from 'hooks/useLocationSettings';
import { formatConstraintValue } from 'utils/formatConstraintValue';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import { useConstraintTooltips } from './hooks/useConstraintTooltips';
const Inverted: FC = () => (
<Tooltip title='NOT (operator is negated)' arrow>
@ -45,7 +45,6 @@ export const ConstraintItemHeader: FC<
> = ({ onSetTruncated, ...constraint }) => {
const { caseInsensitive, contextName, inverted, operator, value, values } =
constraint;
const { context } = useUnleashContext();
const { locationSettings } = useLocationSettings();
const items = value
? [
@ -54,17 +53,7 @@ export const ConstraintItemHeader: FC<
]
: values || [];
const tooltips = useMemo(
() =>
// FIXME: tooltips
Object.fromEntries(
values?.map((value) => [
value,
context.find(({ name }) => name === value)?.description,
]) || [],
),
[context, values],
);
const tooltips = useConstraintTooltips(contextName, values || []);
return (
<StrategyEvaluationItem type='Constraint'>

View File

@ -0,0 +1,99 @@
import { vi } from 'vitest';
import { renderHook } from '@testing-library/react';
import { useConstraintTooltips } from './useConstraintTooltips';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
vi.mock('hooks/api/getters/useUnleashContext/useUnleashContext', () => ({
default: vi.fn(),
}));
describe('useConstraintTooltips', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('returns tooltip mapping for legal values with descriptions', () => {
(
useUnleashContext as unknown as ReturnType<typeof vi.fn>
).mockReturnValue({
context: [
{
name: 'contextA',
description: 'Test context A',
createdAt: '2021-01-01',
sortOrder: 1,
stickiness: false,
legalValues: [
{ value: 'value1', description: 'Tooltip 1' },
{ value: 'value2', description: 'Tooltip 2' },
{ value: 'value3' }, // No description provided
],
},
],
});
const { result } = renderHook(() =>
useConstraintTooltips('contextA', [
'value1',
'value2',
'value3',
'nonExisting',
]),
);
expect(result.current).toEqual({
value1: 'Tooltip 1',
value2: 'Tooltip 2',
});
});
it('returns an empty object when the context is not found', () => {
(
useUnleashContext as unknown as ReturnType<typeof vi.fn>
).mockReturnValue({
context: [
{
name: 'otherContext',
description: 'Other context',
createdAt: '2021-01-01',
sortOrder: 1,
stickiness: false,
legalValues: [
{ value: 'value1', description: 'Tooltip 1' },
],
},
],
});
const { result } = renderHook(() =>
useConstraintTooltips('contextA', ['value1']),
);
expect(result.current).toEqual({});
});
it('returns an empty object when no values are provided', () => {
(
useUnleashContext as unknown as ReturnType<typeof vi.fn>
).mockReturnValue({
context: [
{
name: 'contextA',
description: 'Test context A',
createdAt: '2021-01-01',
sortOrder: 1,
stickiness: false,
legalValues: [
{ value: 'value1', description: 'Tooltip 1' },
],
},
],
});
const { result } = renderHook(() =>
useConstraintTooltips('contextA', []),
);
expect(result.current).toEqual({});
});
});

View File

@ -0,0 +1,27 @@
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import { useMemo } from 'react';
export const useConstraintTooltips = (
contextName: string,
values: string[],
) => {
const { context } = useUnleashContext();
const contextDefinition = useMemo(
() => context.find(({ name }) => name === contextName),
[contextName, context],
);
return useMemo<Record<string, string>>(
() =>
Object.fromEntries(
values
?.map((item) => [
item,
contextDefinition?.legalValues?.find(
({ value }) => value === item,
)?.description,
])
.filter(([_, tooltip]) => !!tooltip) || [],
),
[context, values],
);
};

View File

@ -45,17 +45,18 @@ export const ValuesList: FC<ValuesListProps> = ({
lines={2}
onSetTruncated={() => onSetTruncated?.(false)}
>
{values[0]}
<Tooltip title={tooltips?.[values[0]] || ''}>
<span>{values[0]}</span>
</Tooltip>
</Truncator>
</StyledSingleValue>
) : null}
{values && values?.length > 1 ? (
<Truncator title='' lines={2} onSetTruncated={onSetTruncated}>
{values.map((value) => (
<Tooltip title={tooltips?.[value] || ''} arrow key={value}>
<Tooltip title={tooltips?.[value] || ''} key={value}>
<StyledValueItem>
<span>{value}</span>
{tooltips?.[value]}
</StyledValueItem>
</Tooltip>
))}

View File

@ -13,6 +13,7 @@ import type {
StrategySchemaParametersItem,
} from 'openapi';
import type { IFeatureStrategyPayload } from 'interfaces/strategy';
import { ValuesList } from 'component/common/ConstraintsList/ValuesList/ValuesList';
export const useCustomStrategyParameters = (
strategy: Pick<
@ -48,14 +49,11 @@ export const useCustomStrategyParameters = (
}
return (
<StrategyEvaluationItem
key={key}
type={typeItem}
// values={values} // FIXME: values
>
<StrategyEvaluationItem key={key} type={typeItem}>
{values.length === 1
? 'has 1 item:'
: `has ${values.length} items:`}
<ValuesList values={values} />
</StrategyEvaluationItem>
);
}
@ -82,12 +80,11 @@ export const useCustomStrategyParameters = (
const value = parseParameterString(parameters[name]);
return (
<StrategyEvaluationItem
key={key}
type={typeItem}
// values={value === '' ? undefined : [value]} // FIXME: values
>
<StrategyEvaluationItem key={key} type={typeItem}>
{value === '' ? 'is an empty string' : 'is set to'}
<ValuesList
values={value === '' ? undefined : [value]}
/>
</StrategyEvaluationItem>
);
}
@ -95,12 +92,9 @@ export const useCustomStrategyParameters = (
case 'number': {
const value = parseParameterNumber(parameters[name]);
return (
<StrategyEvaluationItem
key={key}
type={typeItem}
// values={[`${value}`]} // FIXME: values
>
<StrategyEvaluationItem key={key} type={typeItem}>
is a number set to
<ValuesList values={[`${value}`]} />
</StrategyEvaluationItem>
);
}

View File

@ -2,6 +2,8 @@ import { useMemo } from 'react';
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
import type { FeatureStrategySchema } from 'openapi';
import { RolloutParameter } from '../RolloutParameter/RolloutParameter';
import { ValuesList } from 'component/common/ConstraintsList/ValuesList/ValuesList';
import { parseParameterStrings } from 'utils/parseParameter';
export const useStrategyParameters = (
strategy: Partial<
@ -35,11 +37,9 @@ export const useStrategyParameters = (
if (['userids', 'hostnames', 'ips'].includes(type)) {
return (
<StrategyEvaluationItem
key={key}
type={key}
// values={parseParameterStrings(value)} // FIXME: values
/>
<StrategyEvaluationItem key={key} type={key}>
<ValuesList values={parseParameterStrings(value)} />
</StrategyEvaluationItem>
);
}