1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

chore: fix "key" prop issues in front end tests (#8459)

Fixes all warnings about the "key" prop. The majority of the fixes fall
into one of the following categories:

- Extracting "key" props in tables (you're not allowed to just spread
them in)
- Adding "key" props to autocomplete options and chips
- fixing test data that didn't contain ids
This commit is contained in:
Thomas Heartman 2024-10-16 14:57:43 +02:00 committed by GitHub
parent c580e762b3
commit fe09ae214f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 151 additions and 73 deletions

View File

@ -1,4 +1,4 @@
import { Fragment, useState, type ChangeEvent, type VFC } from 'react';
import { type FC, Fragment, useState, type ChangeEvent } from 'react';
import {
Checkbox,
FormControlLabel,
@ -6,6 +6,7 @@ import {
Box,
Paper,
styled,
Chip,
} from '@mui/material';
import { Autocomplete } from '@mui/material';
@ -39,7 +40,7 @@ export interface ISelectProjectInputProps {
error?: string;
}
export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
export const SelectProjectInput: FC<ISelectProjectInputProps> = ({
options,
defaultValue = [ALL_PROJECTS],
onChange,
@ -80,19 +81,22 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
};
const renderOption = (
props: object,
props: object & { key?: string },
option: IAutocompleteBoxOption,
{ selected }: AutocompleteRenderOptionState,
) => (
<li {...props}>
<SelectOptionCheckbox
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
checkedIcon={<CheckBoxIcon fontSize='small' />}
checked={selected}
/>
{option.label}
</li>
);
) => {
const { key, ...rest } = props;
return (
<li key={key} {...rest}>
<SelectOptionCheckbox
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
checkedIcon={<CheckBoxIcon fontSize='small' />}
checked={selected}
/>
{option.label}
</li>
);
};
const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => (
<Fragment key={key}>
@ -149,6 +153,19 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
fullWidth
PaperComponent={CustomPaper}
renderOption={renderOption}
renderTags={(value, getTagProps) => {
return value.map((option, index) => {
const { key, ...props } = getTagProps({ index });
return (
<Chip
size='small'
key={key}
{...props}
label={option.label}
/>
);
});
}}
renderInput={renderInput}
value={
isWildcardSelected || disabled

View File

@ -42,13 +42,18 @@ export const ConnectedInstancesTable = ({
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map((cell) => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
<TableRow hover key={key} {...rowProps}>
{row.cells.map((cell) => {
const { key, ...cellProps } =
cell.getCellProps();
return (
<TableCell key={key} {...cellProps}>
{cell.render('Cell')}
</TableCell>
);
})}
</TableRow>
);
})}

View File

@ -199,13 +199,19 @@ const ContextList: VFC = () => {
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map((cell) => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
<TableRow hover key={key} {...rowProps}>
{row.cells.map((cell) => {
const { key, ...cellProps } =
cell.getCellProps();
return (
<TableCell key={key} {...cellProps}>
{cell.render('Cell')}
</TableCell>
);
})}
</TableRow>
);
})}

View File

@ -3,6 +3,7 @@ import { render } from 'utils/testRenderer';
import { testServerRoute, testServerSetup } from 'utils/testServer';
import { FeatureStrategyConstraintAccordionList } from './FeatureStrategyConstraintAccordionList';
import type { IConstraint } from 'interfaces/strategy';
import { constraintId } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint';
const server = testServerSetup();
@ -18,7 +19,11 @@ const setupApi = () => {
};
const constraints = (limit: number): IConstraint[] =>
Array.from(Array(limit).keys()).map(() => ({
// @ts-expect-error: we access the id field using `Symbol(id)`,
// so just calling the property `id` doesn't work. Instead, we
// need to use the `constraintId` symbol.
Array.from(Array(limit).keys()).map((_, index) => ({
[constraintId]: index,
contextName: 'test',
operator: 'IN',
}));

View File

@ -18,6 +18,7 @@ export const setupSegmentsEndpoint = () => {
testServerRoute(server, '/api/admin/segments', {
segments: [
{
id: 1,
name: 'test',
constraints: [],
},

View File

@ -65,13 +65,18 @@ export const FeatureMetricsTable = ({
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map((cell) => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
<TableRow hover key={key} {...rowProps}>
{row.cells.map((cell) => {
const { key, ...cellProps } =
cell.getCellProps();
return (
<TableCell key={key} {...cellProps}>
{cell.render('Cell')}
</TableCell>
);
})}
</TableRow>
);
})}

View File

@ -11,8 +11,8 @@ export const VariantsTooltip: FC<{
<TooltipLink
tooltip={
<>
{variants.map((child) => (
<div>{child}</div>
{variants.map((child, i) => (
<div key={i}>{child}</div>
))}
</>
}

View File

@ -151,13 +151,18 @@ export const EnvironmentVariantsTable = ({
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map((cell) => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
<TableRow hover {...rowProps} key={key}>
{row.cells.map((cell) => {
const { key, ...cellProps } =
cell.getCellProps();
return (
<TableCell key={key} {...cellProps}>
{cell.render('Cell')}
</TableCell>
);
})}
</TableRow>
);
})}

View File

@ -5,7 +5,7 @@ import {
useContext,
useEffect,
useState,
type VFC,
type FC,
} from 'react';
import {
Alert,
@ -74,7 +74,7 @@ type IntegrationFormProps = {
addon: AddonSchema | Omit<AddonSchema, 'id'>;
};
export const IntegrationForm: VFC<IntegrationFormProps> = ({
export const IntegrationForm: FC<IntegrationFormProps> = ({
editMode,
provider,
addon: initialValues,

View File

@ -1,4 +1,4 @@
import type { VFC } from 'react';
import type { FC } from 'react';
import type { IAutocompleteBoxOption } from '../../../common/AutocompleteBox/AutocompleteBox';
import type {
AutocompleteRenderInputParams,
@ -35,7 +35,7 @@ const StyledCheckbox = styled(Checkbox)(() => ({
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
export const IntegrationMultiSelector: VFC<IIntegrationMultiSelectorProps> = ({
export const IntegrationMultiSelector: FC<IIntegrationMultiSelectorProps> = ({
options,
selectedItems,
onChange,
@ -67,12 +67,13 @@ export const IntegrationMultiSelector: VFC<IIntegrationMultiSelectorProps> = ({
);
const renderOption = (
props: object,
props: object & { key?: string },
option: IAutocompleteBoxOption,
{ selected }: AutocompleteRenderOptionState,
) => {
const { key, ...rest } = props;
return (
<li {...props}>
<li key={key} {...rest}>
<StyledCheckbox
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
checkedIcon={<CheckBoxIcon fontSize='small' />}

View File

@ -1,5 +1,5 @@
import type { ComponentProps, Dispatch, FC, SetStateAction } from 'react';
import { Autocomplete, TextField } from '@mui/material';
import { Autocomplete, Chip, TextField } from '@mui/material';
import { renderOption } from '../../renderOption';
interface IEnvironmentsFieldProps {
@ -59,6 +59,19 @@ export const EnvironmentsField: FC<IEnvironmentsFieldProps> = ({
<TextField {...params} label='Environments' />
)}
renderOption={renderOption}
renderTags={(value, getTagProps) => {
return value.map((option, index) => {
const { key, ...props } = getTagProps({ index });
return (
<Chip
size='small'
key={key}
{...props}
label={option.label}
/>
);
});
}}
getOptionLabel={({ label }) => label}
disableCloseOnSelect={false}
size='small'

View File

@ -7,16 +7,19 @@ const SelectOptionCheckbox = styled(Checkbox)(({ theme }) => ({
}));
export const renderOption = (
props: object,
props: object & { key?: string },
option: { label: string },
{ selected }: { selected: boolean },
) => (
<li {...props}>
<SelectOptionCheckbox
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
checkedIcon={<CheckBoxIcon fontSize='small' />}
checked={selected}
/>
{option.label}
</li>
);
) => {
const { key, ...rest } = props;
return (
<li key={key} {...rest}>
<SelectOptionCheckbox
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
checkedIcon={<CheckBoxIcon fontSize='small' />}
checked={selected}
/>
{option.label}
</li>
);
};

View File

@ -12,6 +12,7 @@ const testCases = [
result: true,
data: [
{
id: 'f17532c8-4b36-4406-a23f-3db75e0adc82',
name: 'default',
parameters: {},
result: { enabled: true, evaluationStatus: 'complete' },
@ -31,6 +32,7 @@ const testCases = [
result: true,
data: [
{
id: 'f17532c8-4b36-4406-a23f-3db75e0adc83',
name: 'default',
parameters: {},
result: { enabled: true, evaluationStatus: 'complete' },
@ -50,6 +52,7 @@ const testCases = [
result: true,
data: [
{
id: 'f17532c8-4b36-4406-a23f-3db75e0adc77',
name: 'default',
parameters: {},
result: { enabled: true, evaluationStatus: 'complete' },
@ -69,11 +72,13 @@ const testCases = [
result: true,
data: [
{
id: 'f17532c8-4b36-4406-a23f-3db75e0adc78',
name: 'default',
parameters: {},
result: { enabled: true, evaluationStatus: 'complete' },
},
{
id: 'f17532c8-4b36-4406-a23f-3db75e0adc79',
name: 'default',
parameters: {},
disabled: true,
@ -97,11 +102,13 @@ const testCases = [
result: true,
data: [
{
id: 'f17532c8-4b36-4406-a23f-3db75e0adc80',
name: 'default',
parameters: {},
result: { enabled: true, evaluationStatus: 'complete' },
},
{
id: 'f17532c8-4b36-4406-a23f-3db75e0adc81',
name: 'default',
parameters: {},
disabled: true,

View File

@ -229,13 +229,18 @@ export const LeadTimeForChanges = ({
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map((cell) => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
<TableRow hover key={key} {...rowProps}>
{row.cells.map((cell) => {
const { key, ...cellProps } =
cell.getCellProps();
return (
<TableCell key={key} {...cellProps}>
{cell.render('Cell')}
</TableCell>
);
})}
</TableRow>
);
})}

View File

@ -15,7 +15,7 @@ const setupApi = () => {
});
testServerRoute(server, '/api/admin/projects', {
projects: [{ name: 'existing' }],
projects: [{ name: 'existing', id: '1' }],
});
};

View File

@ -210,13 +210,18 @@ export const TagTypeList = () => {
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
const { key, ...rowProps } = row.getRowProps();
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map((cell) => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
<TableRow hover key={key} {...rowProps}>
{row.cells.map((cell) => {
const { key, ...cellProps } =
cell.getCellProps();
return (
<TableCell key={key} {...cellProps}>
{cell.render('Cell')}
</TableCell>
);
})}
</TableRow>
);
})}

View File

@ -16,8 +16,8 @@ const RouteNameRender: FC<{}> = () => {
return (
<div>
<RecentlyVisitedRecorder />
{lastVisited.map((visited) => (
<div>{visited.pathName}</div>
{lastVisited.map((visited, index) => (
<div key={index}>{visited.pathName}</div>
))}
</div>
);