mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-06 01:15:28 +02: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:
parent
c580e762b3
commit
fe09ae214f
@ -1,4 +1,4 @@
|
|||||||
import { Fragment, useState, type ChangeEvent, type VFC } from 'react';
|
import { type FC, Fragment, useState, type ChangeEvent } from 'react';
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
@ -6,6 +6,7 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Paper,
|
Paper,
|
||||||
styled,
|
styled,
|
||||||
|
Chip,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Autocomplete } from '@mui/material';
|
import { Autocomplete } from '@mui/material';
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ export interface ISelectProjectInputProps {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
export const SelectProjectInput: FC<ISelectProjectInputProps> = ({
|
||||||
options,
|
options,
|
||||||
defaultValue = [ALL_PROJECTS],
|
defaultValue = [ALL_PROJECTS],
|
||||||
onChange,
|
onChange,
|
||||||
@ -80,11 +81,13 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderOption = (
|
const renderOption = (
|
||||||
props: object,
|
props: object & { key?: string },
|
||||||
option: IAutocompleteBoxOption,
|
option: IAutocompleteBoxOption,
|
||||||
{ selected }: AutocompleteRenderOptionState,
|
{ selected }: AutocompleteRenderOptionState,
|
||||||
) => (
|
) => {
|
||||||
<li {...props}>
|
const { key, ...rest } = props;
|
||||||
|
return (
|
||||||
|
<li key={key} {...rest}>
|
||||||
<SelectOptionCheckbox
|
<SelectOptionCheckbox
|
||||||
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
|
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
|
||||||
checkedIcon={<CheckBoxIcon fontSize='small' />}
|
checkedIcon={<CheckBoxIcon fontSize='small' />}
|
||||||
@ -93,6 +96,7 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
|||||||
{option.label}
|
{option.label}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => (
|
const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => (
|
||||||
<Fragment key={key}>
|
<Fragment key={key}>
|
||||||
@ -149,6 +153,19 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
|||||||
fullWidth
|
fullWidth
|
||||||
PaperComponent={CustomPaper}
|
PaperComponent={CustomPaper}
|
||||||
renderOption={renderOption}
|
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}
|
renderInput={renderInput}
|
||||||
value={
|
value={
|
||||||
isWildcardSelected || disabled
|
isWildcardSelected || disabled
|
||||||
|
@ -42,13 +42,18 @@ export const ConnectedInstancesTable = ({
|
|||||||
<TableBody {...getTableBodyProps()}>
|
<TableBody {...getTableBodyProps()}>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
const { key, ...rowProps } = row.getRowProps();
|
||||||
return (
|
return (
|
||||||
<TableRow hover {...row.getRowProps()}>
|
<TableRow hover key={key} {...rowProps}>
|
||||||
{row.cells.map((cell) => (
|
{row.cells.map((cell) => {
|
||||||
<TableCell {...cell.getCellProps()}>
|
const { key, ...cellProps } =
|
||||||
|
cell.getCellProps();
|
||||||
|
return (
|
||||||
|
<TableCell key={key} {...cellProps}>
|
||||||
{cell.render('Cell')}
|
{cell.render('Cell')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -199,13 +199,19 @@ const ContextList: VFC = () => {
|
|||||||
<TableBody {...getTableBodyProps()}>
|
<TableBody {...getTableBodyProps()}>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
const { key, ...rowProps } = row.getRowProps();
|
||||||
return (
|
return (
|
||||||
<TableRow hover {...row.getRowProps()}>
|
<TableRow hover key={key} {...rowProps}>
|
||||||
{row.cells.map((cell) => (
|
{row.cells.map((cell) => {
|
||||||
<TableCell {...cell.getCellProps()}>
|
const { key, ...cellProps } =
|
||||||
|
cell.getCellProps();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableCell key={key} {...cellProps}>
|
||||||
{cell.render('Cell')}
|
{cell.render('Cell')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -3,6 +3,7 @@ import { render } from 'utils/testRenderer';
|
|||||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||||
import { FeatureStrategyConstraintAccordionList } from './FeatureStrategyConstraintAccordionList';
|
import { FeatureStrategyConstraintAccordionList } from './FeatureStrategyConstraintAccordionList';
|
||||||
import type { IConstraint } from 'interfaces/strategy';
|
import type { IConstraint } from 'interfaces/strategy';
|
||||||
|
import { constraintId } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint';
|
||||||
|
|
||||||
const server = testServerSetup();
|
const server = testServerSetup();
|
||||||
|
|
||||||
@ -18,7 +19,11 @@ const setupApi = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const constraints = (limit: number): IConstraint[] =>
|
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',
|
contextName: 'test',
|
||||||
operator: 'IN',
|
operator: 'IN',
|
||||||
}));
|
}));
|
||||||
|
@ -18,6 +18,7 @@ export const setupSegmentsEndpoint = () => {
|
|||||||
testServerRoute(server, '/api/admin/segments', {
|
testServerRoute(server, '/api/admin/segments', {
|
||||||
segments: [
|
segments: [
|
||||||
{
|
{
|
||||||
|
id: 1,
|
||||||
name: 'test',
|
name: 'test',
|
||||||
constraints: [],
|
constraints: [],
|
||||||
},
|
},
|
||||||
|
@ -65,13 +65,18 @@ export const FeatureMetricsTable = ({
|
|||||||
<TableBody {...getTableBodyProps()}>
|
<TableBody {...getTableBodyProps()}>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
const { key, ...rowProps } = row.getRowProps();
|
||||||
return (
|
return (
|
||||||
<TableRow hover {...row.getRowProps()}>
|
<TableRow hover key={key} {...rowProps}>
|
||||||
{row.cells.map((cell) => (
|
{row.cells.map((cell) => {
|
||||||
<TableCell {...cell.getCellProps()}>
|
const { key, ...cellProps } =
|
||||||
|
cell.getCellProps();
|
||||||
|
return (
|
||||||
|
<TableCell key={key} {...cellProps}>
|
||||||
{cell.render('Cell')}
|
{cell.render('Cell')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -11,8 +11,8 @@ export const VariantsTooltip: FC<{
|
|||||||
<TooltipLink
|
<TooltipLink
|
||||||
tooltip={
|
tooltip={
|
||||||
<>
|
<>
|
||||||
{variants.map((child) => (
|
{variants.map((child, i) => (
|
||||||
<div>{child}</div>
|
<div key={i}>{child}</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -151,13 +151,18 @@ export const EnvironmentVariantsTable = ({
|
|||||||
<TableBody {...getTableBodyProps()}>
|
<TableBody {...getTableBodyProps()}>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
const { key, ...rowProps } = row.getRowProps();
|
||||||
return (
|
return (
|
||||||
<TableRow hover {...row.getRowProps()}>
|
<TableRow hover {...rowProps} key={key}>
|
||||||
{row.cells.map((cell) => (
|
{row.cells.map((cell) => {
|
||||||
<TableCell {...cell.getCellProps()}>
|
const { key, ...cellProps } =
|
||||||
|
cell.getCellProps();
|
||||||
|
return (
|
||||||
|
<TableCell key={key} {...cellProps}>
|
||||||
{cell.render('Cell')}
|
{cell.render('Cell')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
type VFC,
|
type FC,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
@ -74,7 +74,7 @@ type IntegrationFormProps = {
|
|||||||
addon: AddonSchema | Omit<AddonSchema, 'id'>;
|
addon: AddonSchema | Omit<AddonSchema, 'id'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationForm: VFC<IntegrationFormProps> = ({
|
export const IntegrationForm: FC<IntegrationFormProps> = ({
|
||||||
editMode,
|
editMode,
|
||||||
provider,
|
provider,
|
||||||
addon: initialValues,
|
addon: initialValues,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { VFC } from 'react';
|
import type { FC } from 'react';
|
||||||
import type { IAutocompleteBoxOption } from '../../../common/AutocompleteBox/AutocompleteBox';
|
import type { IAutocompleteBoxOption } from '../../../common/AutocompleteBox/AutocompleteBox';
|
||||||
import type {
|
import type {
|
||||||
AutocompleteRenderInputParams,
|
AutocompleteRenderInputParams,
|
||||||
@ -35,7 +35,7 @@ const StyledCheckbox = styled(Checkbox)(() => ({
|
|||||||
|
|
||||||
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
|
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
|
||||||
|
|
||||||
export const IntegrationMultiSelector: VFC<IIntegrationMultiSelectorProps> = ({
|
export const IntegrationMultiSelector: FC<IIntegrationMultiSelectorProps> = ({
|
||||||
options,
|
options,
|
||||||
selectedItems,
|
selectedItems,
|
||||||
onChange,
|
onChange,
|
||||||
@ -67,12 +67,13 @@ export const IntegrationMultiSelector: VFC<IIntegrationMultiSelectorProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderOption = (
|
const renderOption = (
|
||||||
props: object,
|
props: object & { key?: string },
|
||||||
option: IAutocompleteBoxOption,
|
option: IAutocompleteBoxOption,
|
||||||
{ selected }: AutocompleteRenderOptionState,
|
{ selected }: AutocompleteRenderOptionState,
|
||||||
) => {
|
) => {
|
||||||
|
const { key, ...rest } = props;
|
||||||
return (
|
return (
|
||||||
<li {...props}>
|
<li key={key} {...rest}>
|
||||||
<StyledCheckbox
|
<StyledCheckbox
|
||||||
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
|
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
|
||||||
checkedIcon={<CheckBoxIcon fontSize='small' />}
|
checkedIcon={<CheckBoxIcon fontSize='small' />}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { ComponentProps, Dispatch, FC, SetStateAction } from 'react';
|
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';
|
import { renderOption } from '../../renderOption';
|
||||||
|
|
||||||
interface IEnvironmentsFieldProps {
|
interface IEnvironmentsFieldProps {
|
||||||
@ -59,6 +59,19 @@ export const EnvironmentsField: FC<IEnvironmentsFieldProps> = ({
|
|||||||
<TextField {...params} label='Environments' />
|
<TextField {...params} label='Environments' />
|
||||||
)}
|
)}
|
||||||
renderOption={renderOption}
|
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}
|
getOptionLabel={({ label }) => label}
|
||||||
disableCloseOnSelect={false}
|
disableCloseOnSelect={false}
|
||||||
size='small'
|
size='small'
|
||||||
|
@ -7,11 +7,13 @@ const SelectOptionCheckbox = styled(Checkbox)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const renderOption = (
|
export const renderOption = (
|
||||||
props: object,
|
props: object & { key?: string },
|
||||||
option: { label: string },
|
option: { label: string },
|
||||||
{ selected }: { selected: boolean },
|
{ selected }: { selected: boolean },
|
||||||
) => (
|
) => {
|
||||||
<li {...props}>
|
const { key, ...rest } = props;
|
||||||
|
return (
|
||||||
|
<li key={key} {...rest}>
|
||||||
<SelectOptionCheckbox
|
<SelectOptionCheckbox
|
||||||
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
|
icon={<CheckBoxOutlineBlankIcon fontSize='small' />}
|
||||||
checkedIcon={<CheckBoxIcon fontSize='small' />}
|
checkedIcon={<CheckBoxIcon fontSize='small' />}
|
||||||
@ -20,3 +22,4 @@ export const renderOption = (
|
|||||||
{option.label}
|
{option.label}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
@ -12,6 +12,7 @@ const testCases = [
|
|||||||
result: true,
|
result: true,
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
|
id: 'f17532c8-4b36-4406-a23f-3db75e0adc82',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
result: { enabled: true, evaluationStatus: 'complete' },
|
result: { enabled: true, evaluationStatus: 'complete' },
|
||||||
@ -31,6 +32,7 @@ const testCases = [
|
|||||||
result: true,
|
result: true,
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
|
id: 'f17532c8-4b36-4406-a23f-3db75e0adc83',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
result: { enabled: true, evaluationStatus: 'complete' },
|
result: { enabled: true, evaluationStatus: 'complete' },
|
||||||
@ -50,6 +52,7 @@ const testCases = [
|
|||||||
result: true,
|
result: true,
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
|
id: 'f17532c8-4b36-4406-a23f-3db75e0adc77',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
result: { enabled: true, evaluationStatus: 'complete' },
|
result: { enabled: true, evaluationStatus: 'complete' },
|
||||||
@ -69,11 +72,13 @@ const testCases = [
|
|||||||
result: true,
|
result: true,
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
|
id: 'f17532c8-4b36-4406-a23f-3db75e0adc78',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
result: { enabled: true, evaluationStatus: 'complete' },
|
result: { enabled: true, evaluationStatus: 'complete' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'f17532c8-4b36-4406-a23f-3db75e0adc79',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
disabled: true,
|
disabled: true,
|
||||||
@ -97,11 +102,13 @@ const testCases = [
|
|||||||
result: true,
|
result: true,
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
|
id: 'f17532c8-4b36-4406-a23f-3db75e0adc80',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
result: { enabled: true, evaluationStatus: 'complete' },
|
result: { enabled: true, evaluationStatus: 'complete' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'f17532c8-4b36-4406-a23f-3db75e0adc81',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
disabled: true,
|
disabled: true,
|
||||||
|
@ -229,13 +229,18 @@ export const LeadTimeForChanges = ({
|
|||||||
<TableBody {...getTableBodyProps()}>
|
<TableBody {...getTableBodyProps()}>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
const { key, ...rowProps } = row.getRowProps();
|
||||||
return (
|
return (
|
||||||
<TableRow hover {...row.getRowProps()}>
|
<TableRow hover key={key} {...rowProps}>
|
||||||
{row.cells.map((cell) => (
|
{row.cells.map((cell) => {
|
||||||
<TableCell {...cell.getCellProps()}>
|
const { key, ...cellProps } =
|
||||||
|
cell.getCellProps();
|
||||||
|
return (
|
||||||
|
<TableCell key={key} {...cellProps}>
|
||||||
{cell.render('Cell')}
|
{cell.render('Cell')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -15,7 +15,7 @@ const setupApi = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testServerRoute(server, '/api/admin/projects', {
|
testServerRoute(server, '/api/admin/projects', {
|
||||||
projects: [{ name: 'existing' }],
|
projects: [{ name: 'existing', id: '1' }],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,13 +210,18 @@ export const TagTypeList = () => {
|
|||||||
<TableBody {...getTableBodyProps()}>
|
<TableBody {...getTableBodyProps()}>
|
||||||
{rows.map((row) => {
|
{rows.map((row) => {
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
const { key, ...rowProps } = row.getRowProps();
|
||||||
return (
|
return (
|
||||||
<TableRow hover {...row.getRowProps()}>
|
<TableRow hover key={key} {...rowProps}>
|
||||||
{row.cells.map((cell) => (
|
{row.cells.map((cell) => {
|
||||||
<TableCell {...cell.getCellProps()}>
|
const { key, ...cellProps } =
|
||||||
|
cell.getCellProps();
|
||||||
|
return (
|
||||||
|
<TableCell key={key} {...cellProps}>
|
||||||
{cell.render('Cell')}
|
{cell.render('Cell')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -16,8 +16,8 @@ const RouteNameRender: FC<{}> = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<RecentlyVisitedRecorder />
|
<RecentlyVisitedRecorder />
|
||||||
{lastVisited.map((visited) => (
|
{lastVisited.map((visited, index) => (
|
||||||
<div>{visited.pathName}</div>
|
<div key={index}>{visited.pathName}</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user