From fe09ae214f3506a392cc756f8401a4bfab77c58b Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Wed, 16 Oct 2024 14:57:43 +0200 Subject: [PATCH] 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 --- .../SelectProjectInput/SelectProjectInput.tsx | 43 +++++++++++++------ .../ConnectedInstancesTable.tsx | 17 +++++--- .../ContextList/ContextList/ContextList.tsx | 18 +++++--- ...reStrategyConstraintAccordionList.test.tsx | 7 ++- .../featureStrategyFormTestSetup.ts | 1 + .../FeatureMetricsTable.tsx | 17 +++++--- .../VariantsTooltip.tsx | 4 +- .../EnvironmentVariantsTable.tsx | 17 +++++--- .../IntegrationForm/IntegrationForm.tsx | 4 +- .../IntegrationMultiSelector.tsx | 9 ++-- .../EnvironmentsField/EnvironmentsField.tsx | 15 ++++++- .../PlaygroundForm/renderOption.tsx | 25 ++++++----- ...aygroundResultFeatureStrategyList.test.tsx | 7 +++ .../LeadTimeForChanges/LeadTimeForChanges.tsx | 17 +++++--- .../project/ProjectList/ProjectList.test.tsx | 2 +- .../tags/TagTypeList/TagTypeList.tsx | 17 +++++--- .../src/hooks/useRecentlyVisited.test.tsx | 4 +- 17 files changed, 151 insertions(+), 73 deletions(-) diff --git a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx index cdabf91224..21b42a2c4b 100644 --- a/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx +++ b/frontend/src/component/admin/apiToken/ApiTokenForm/ProjectSelector/SelectProjectInput/SelectProjectInput.tsx @@ -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 = ({ +export const SelectProjectInput: FC = ({ options, defaultValue = [ALL_PROJECTS], onChange, @@ -80,19 +81,22 @@ export const SelectProjectInput: VFC = ({ }; const renderOption = ( - props: object, + props: object & { key?: string }, option: IAutocompleteBoxOption, { selected }: AutocompleteRenderOptionState, - ) => ( -
  • - } - checkedIcon={} - checked={selected} - /> - {option.label} -
  • - ); + ) => { + const { key, ...rest } = props; + return ( +
  • + } + checkedIcon={} + checked={selected} + /> + {option.label} +
  • + ); + }; const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => ( @@ -149,6 +153,19 @@ export const SelectProjectInput: VFC = ({ fullWidth PaperComponent={CustomPaper} renderOption={renderOption} + renderTags={(value, getTagProps) => { + return value.map((option, index) => { + const { key, ...props } = getTagProps({ index }); + return ( + + ); + }); + }} renderInput={renderInput} value={ isWildcardSelected || disabled diff --git a/frontend/src/component/application/ConnectedInstances/ConnectedInstancesTable.tsx b/frontend/src/component/application/ConnectedInstances/ConnectedInstancesTable.tsx index 934df13a50..df434c7bbf 100644 --- a/frontend/src/component/application/ConnectedInstances/ConnectedInstancesTable.tsx +++ b/frontend/src/component/application/ConnectedInstances/ConnectedInstancesTable.tsx @@ -42,13 +42,18 @@ export const ConnectedInstancesTable = ({ {rows.map((row) => { prepareRow(row); + const { key, ...rowProps } = row.getRowProps(); return ( - - {row.cells.map((cell) => ( - - {cell.render('Cell')} - - ))} + + {row.cells.map((cell) => { + const { key, ...cellProps } = + cell.getCellProps(); + return ( + + {cell.render('Cell')} + + ); + })} ); })} diff --git a/frontend/src/component/context/ContextList/ContextList/ContextList.tsx b/frontend/src/component/context/ContextList/ContextList/ContextList.tsx index e2e5a7cf5a..86ae24f92b 100644 --- a/frontend/src/component/context/ContextList/ContextList/ContextList.tsx +++ b/frontend/src/component/context/ContextList/ContextList/ContextList.tsx @@ -199,13 +199,19 @@ const ContextList: VFC = () => { {rows.map((row) => { prepareRow(row); + const { key, ...rowProps } = row.getRowProps(); return ( - - {row.cells.map((cell) => ( - - {cell.render('Cell')} - - ))} + + {row.cells.map((cell) => { + const { key, ...cellProps } = + cell.getCellProps(); + + return ( + + {cell.render('Cell')} + + ); + })} ); })} diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraintAccordionList/FeatureStrategyConstraintAccordionList.test.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraintAccordionList/FeatureStrategyConstraintAccordionList.test.tsx index db78c0f7ce..cc87549173 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraintAccordionList/FeatureStrategyConstraintAccordionList.test.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/FeatureStrategyConstraintAccordionList/FeatureStrategyConstraintAccordionList.test.tsx @@ -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', })); diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/featureStrategyFormTestSetup.ts b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/featureStrategyFormTestSetup.ts index 9e3320e4a6..39147ca3d1 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/featureStrategyFormTestSetup.ts +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/featureStrategyFormTestSetup.ts @@ -18,6 +18,7 @@ export const setupSegmentsEndpoint = () => { testServerRoute(server, '/api/admin/segments', { segments: [ { + id: 1, name: 'test', constraints: [], }, diff --git a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx index 358005228b..ca6ab07399 100644 --- a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx @@ -65,13 +65,18 @@ export const FeatureMetricsTable = ({ {rows.map((row) => { prepareRow(row); + const { key, ...rowProps } = row.getRowProps(); return ( - - {row.cells.map((cell) => ( - - {cell.render('Cell')} - - ))} + + {row.cells.map((cell) => { + const { key, ...cellProps } = + cell.getCellProps(); + return ( + + {cell.render('Cell')} + + ); + })} ); })} diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/VariantsTooltip.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/VariantsTooltip.tsx index 510062d03e..9ad5c519f2 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/VariantsTooltip.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/VariantsTooltip.tsx @@ -11,8 +11,8 @@ export const VariantsTooltip: FC<{ - {variants.map((child) => ( -
    {child}
    + {variants.map((child, i) => ( +
    {child}
    ))} } diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/EnvironmentVariantsTable.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/EnvironmentVariantsTable.tsx index 25f76b0626..8197763905 100644 --- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/EnvironmentVariantsTable.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/EnvironmentVariantsTable.tsx @@ -151,13 +151,18 @@ export const EnvironmentVariantsTable = ({ {rows.map((row) => { prepareRow(row); + const { key, ...rowProps } = row.getRowProps(); return ( - - {row.cells.map((cell) => ( - - {cell.render('Cell')} - - ))} + + {row.cells.map((cell) => { + const { key, ...cellProps } = + cell.getCellProps(); + return ( + + {cell.render('Cell')} + + ); + })} ); })} diff --git a/frontend/src/component/integrations/IntegrationForm/IntegrationForm.tsx b/frontend/src/component/integrations/IntegrationForm/IntegrationForm.tsx index 979db6bf32..8a025298a1 100644 --- a/frontend/src/component/integrations/IntegrationForm/IntegrationForm.tsx +++ b/frontend/src/component/integrations/IntegrationForm/IntegrationForm.tsx @@ -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; }; -export const IntegrationForm: VFC = ({ +export const IntegrationForm: FC = ({ editMode, provider, addon: initialValues, diff --git a/frontend/src/component/integrations/IntegrationForm/IntegrationMultiSelector/IntegrationMultiSelector.tsx b/frontend/src/component/integrations/IntegrationForm/IntegrationMultiSelector/IntegrationMultiSelector.tsx index afdbf8e700..7aec37fd79 100644 --- a/frontend/src/component/integrations/IntegrationForm/IntegrationMultiSelector/IntegrationMultiSelector.tsx +++ b/frontend/src/component/integrations/IntegrationForm/IntegrationMultiSelector/IntegrationMultiSelector.tsx @@ -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 }) => ; -export const IntegrationMultiSelector: VFC = ({ +export const IntegrationMultiSelector: FC = ({ options, selectedItems, onChange, @@ -67,12 +67,13 @@ export const IntegrationMultiSelector: VFC = ({ ); const renderOption = ( - props: object, + props: object & { key?: string }, option: IAutocompleteBoxOption, { selected }: AutocompleteRenderOptionState, ) => { + const { key, ...rest } = props; return ( -
  • +
  • } checkedIcon={} diff --git a/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/EnvironmentsField/EnvironmentsField.tsx b/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/EnvironmentsField/EnvironmentsField.tsx index a2f6d13287..f937b002c5 100644 --- a/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/EnvironmentsField/EnvironmentsField.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/EnvironmentsField/EnvironmentsField.tsx @@ -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 = ({ )} renderOption={renderOption} + renderTags={(value, getTagProps) => { + return value.map((option, index) => { + const { key, ...props } = getTagProps({ index }); + return ( + + ); + }); + }} getOptionLabel={({ label }) => label} disableCloseOnSelect={false} size='small' diff --git a/frontend/src/component/playground/Playground/PlaygroundForm/renderOption.tsx b/frontend/src/component/playground/Playground/PlaygroundForm/renderOption.tsx index 001fe6d610..05de0db030 100644 --- a/frontend/src/component/playground/Playground/PlaygroundForm/renderOption.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundForm/renderOption.tsx @@ -7,16 +7,19 @@ const SelectOptionCheckbox = styled(Checkbox)(({ theme }) => ({ })); export const renderOption = ( - props: object, + props: object & { key?: string }, option: { label: string }, { selected }: { selected: boolean }, -) => ( -
  • - } - checkedIcon={} - checked={selected} - /> - {option.label} -
  • -); +) => { + const { key, ...rest } = props; + return ( +
  • + } + checkedIcon={} + checked={selected} + /> + {option.label} +
  • + ); +}; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx index a2b897a333..24340f967c 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx @@ -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, diff --git a/frontend/src/component/project/Project/ProjectInsights/LeadTimeForChanges/LeadTimeForChanges.tsx b/frontend/src/component/project/Project/ProjectInsights/LeadTimeForChanges/LeadTimeForChanges.tsx index 82d5261158..e73f5bdd83 100644 --- a/frontend/src/component/project/Project/ProjectInsights/LeadTimeForChanges/LeadTimeForChanges.tsx +++ b/frontend/src/component/project/Project/ProjectInsights/LeadTimeForChanges/LeadTimeForChanges.tsx @@ -229,13 +229,18 @@ export const LeadTimeForChanges = ({ {rows.map((row) => { prepareRow(row); + const { key, ...rowProps } = row.getRowProps(); return ( - - {row.cells.map((cell) => ( - - {cell.render('Cell')} - - ))} + + {row.cells.map((cell) => { + const { key, ...cellProps } = + cell.getCellProps(); + return ( + + {cell.render('Cell')} + + ); + })} ); })} diff --git a/frontend/src/component/project/ProjectList/ProjectList.test.tsx b/frontend/src/component/project/ProjectList/ProjectList.test.tsx index 4917b7e31a..f9fcac9d4c 100644 --- a/frontend/src/component/project/ProjectList/ProjectList.test.tsx +++ b/frontend/src/component/project/ProjectList/ProjectList.test.tsx @@ -15,7 +15,7 @@ const setupApi = () => { }); testServerRoute(server, '/api/admin/projects', { - projects: [{ name: 'existing' }], + projects: [{ name: 'existing', id: '1' }], }); }; diff --git a/frontend/src/component/tags/TagTypeList/TagTypeList.tsx b/frontend/src/component/tags/TagTypeList/TagTypeList.tsx index 98f096a036..a1b467800e 100644 --- a/frontend/src/component/tags/TagTypeList/TagTypeList.tsx +++ b/frontend/src/component/tags/TagTypeList/TagTypeList.tsx @@ -210,13 +210,18 @@ export const TagTypeList = () => { {rows.map((row) => { prepareRow(row); + const { key, ...rowProps } = row.getRowProps(); return ( - - {row.cells.map((cell) => ( - - {cell.render('Cell')} - - ))} + + {row.cells.map((cell) => { + const { key, ...cellProps } = + cell.getCellProps(); + return ( + + {cell.render('Cell')} + + ); + })} ); })} diff --git a/frontend/src/hooks/useRecentlyVisited.test.tsx b/frontend/src/hooks/useRecentlyVisited.test.tsx index 3bdc9ae7b1..d68f79c9f5 100644 --- a/frontend/src/hooks/useRecentlyVisited.test.tsx +++ b/frontend/src/hooks/useRecentlyVisited.test.tsx @@ -16,8 +16,8 @@ const RouteNameRender: FC<{}> = () => { return (
    - {lastVisited.map((visited) => ( -
    {visited.pathName}
    + {lastVisited.map((visited, index) => ( +
    {visited.pathName}
    ))}
    );