diff --git a/packages/oss-console/src/components/Launch/LaunchForm/MapInput.tsx b/packages/oss-console/src/components/Launch/LaunchForm/MapInput.tsx index 00d47e1c6..e70edf516 100644 --- a/packages/oss-console/src/components/Launch/LaunchForm/MapInput.tsx +++ b/packages/oss-console/src/components/Launch/LaunchForm/MapInput.tsx @@ -109,7 +109,7 @@ const MapSingleInputItem = (props: MapInputItemProps) => { ); }; -const getNewMapItem = (id, key = '', value = ''): MapInputItem => { +const getNewMapItem = (id: number | null, key = '', value = ''): MapInputItem => { return { id, key, value }; }; @@ -121,7 +121,11 @@ function parseMappedTypeValue(value?: InputValue): MapInputItem[] { try { const mapObj = JSON.parse(value.toString()); if (typeof mapObj === 'object') { - return Object.keys(mapObj).map((key, index) => getNewMapItem(index, key, mapObj[key])); + return Object.keys(mapObj).map((key, index) => { + // Object values (e.g. Map[str, struct] on relaunch) must be JSON-stringified for the text field. + const itemValue = mapObj[key]; + return getNewMapItem(index, key, typeof itemValue === 'object' ? JSON.stringify(itemValue) : itemValue); + }); } } catch (e) { // do nothing diff --git a/packages/oss-console/src/components/Launch/LaunchForm/test/MapInput.test.tsx b/packages/oss-console/src/components/Launch/LaunchForm/test/MapInput.test.tsx new file mode 100644 index 000000000..13c7ac22b --- /dev/null +++ b/packages/oss-console/src/components/Launch/LaunchForm/test/MapInput.test.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { ThemeProvider } from '@mui/material/styles'; +import { render } from '@testing-library/react'; +import { muiTheme } from '@clients/theme/Theme/muiTheme'; +import { MapInput } from '../MapInput'; +import { InputProps, InputType } from '../types'; + +const makeMapProps = (value: string): InputProps => ({ + description: '', + name: 'mapInput', + label: 'Map Input', + required: false, + typeDefinition: { + literalType: {}, + type: InputType.Map, + subtype: { literalType: {}, type: InputType.Struct }, + }, + value, + onChange: jest.fn(), +}); + +const renderMap = (value: string) => + render( + + + , + ); + +describe('MapInput', () => { + it('renders an object map value as JSON (not "[object Object]") when relaunching', () => { + // A map whose values are themselves objects -- the shape a prior execution produces when + // relaunching (e.g. Map[str, struct]). Before the fix this binds an object straight into the + // value text field, which coerces to "[object Object]" (and produced render errors). + const { container } = renderMap('{"foo":{"nested":"bar"}}'); + + const keyField = container.querySelector('.keyControl input') as HTMLInputElement; + const valueField = container.querySelector('.valueControl textarea') as HTMLTextAreaElement; + + expect(keyField?.value).toBe('foo'); + expect(valueField).not.toBeNull(); + expect(valueField.value).toBe('{"nested":"bar"}'); + expect(valueField.value).not.toBe('[object Object]'); + }); + + it('leaves plain string map values unchanged', () => { + const { container } = renderMap('{"foo":"bar"}'); + const valueField = container.querySelector('.valueControl textarea') as HTMLTextAreaElement; + expect(valueField?.value).toBe('bar'); + }); +});