diff --git a/Localize/lang/strings.json b/Localize/lang/strings.json index 5384a44c76f..3a28c71a58a 100644 --- a/Localize/lang/strings.json +++ b/Localize/lang/strings.json @@ -602,6 +602,7 @@ "NMpFs6": "(UTC-03:00) Araguaina", "NPUFgH": "Status", "NWxGWN": "Enter unique property name", + "Nbl3zN": "Enter a description of the output", "NdOhXD": "Create Connection", "Nh91qA": "Returns the value of the specified variable.", "NhJPUn": "How can I call an external endpoint?", @@ -1532,6 +1533,7 @@ "_NMpFs6.comment": "Time zone value ", "_NPUFgH.comment": "Label text for retry status", "_NWxGWN.comment": "Placeholder text for new property name", + "_Nbl3zN.comment": "Placeholder for output description field", "_NdOhXD.comment": "Header for the create connection panel", "_Nh91qA.comment": "Label for the description of the custom 'variables' function", "_NhJPUn.comment": "Chatbot suggestion message to call an external endpoint", diff --git a/libs/designer-ui/src/lib/dynamicallyaddedparameter/__test__/dynamicallyaddedparameter.spec.tsx b/libs/designer-ui/src/lib/dynamicallyaddedparameter/__test__/dynamicallyaddedparameter.spec.tsx new file mode 100644 index 00000000000..5fb5fcdcfb2 --- /dev/null +++ b/libs/designer-ui/src/lib/dynamicallyaddedparameter/__test__/dynamicallyaddedparameter.spec.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import type { DynamicallyAddedParameterProps } from '..'; +import { DynamicallyAddedParameter } from '..'; +import * as ReactShallowRenderer from 'react-test-renderer/shallow'; +import { describe, vi, beforeEach, afterEach, it, expect } from 'vitest'; +import { render, fireEvent } from '@testing-library/react'; + +describe('ui/dynamicallyaddedparameter', () => { + let minimal: DynamicallyAddedParameterProps, renderer: ReactShallowRenderer.ShallowRenderer; + + beforeEach(() => { + minimal = { + schemaKey: 'text', + icon: 'TEXT', + title: 'title', + description: '', + titlePlaceholder: 'Enter some text', + descriptionPlaceholder: 'Description of the text', + renderDescriptionField: true, + onTitleChange: (_schemaKey: string, _newValue: string | undefined) => {}, + onDescriptionChange: (_schemaKey: string, _newValue: string | undefined) => {}, + onDelete: (_schemaKey: string) => {}, + onRenderValueField: (schemaKey: string) =>
{schemaKey}
, + }; + renderer = ReactShallowRenderer.createRenderer(); + }); + + afterEach(() => { + renderer.unmount(); + }); + + it('should render', () => { + renderer.render(); + const output = renderer.getRenderOutput(); + expect(output).toBeDefined(); + }); + + it('should not render output if renderDescriptionField is undefined', () => { + const minimalWithRenderDescriptionUndefined = { ...minimal, renderDescriptionField: undefined }; + renderer.render(); + const output = renderer.getRenderOutput(); + const [renderDynamicParameter] = output.props.children; + expect(renderDynamicParameter.props.children.filter((child: React.ReactElement) => child !== undefined).length).toBe(1); + }); + + it('should not render output if renderDescriptionField is false', () => { + const minimalWithRenderDescriptionUndefined = { ...minimal, renderDescriptionField: false }; + renderer.render(); + const output = renderer.getRenderOutput(); + const [renderDynamicParameter] = output.props.children; + expect(renderDynamicParameter.props.children.filter((child: React.ReactElement) => child).length).toBe(1); + }); + + it('should render description field if renderDescriptionField is true', () => { + const minimalWithRenderDescriptionTrue = { ...minimal, renderDescriptionField: true }; + renderer.render(); + const output = renderer.getRenderOutput(); + const [renderDynamicParameter] = output.props.children; + expect(React.Children.toArray(renderDynamicParameter.props.children.filter((child: React.ReactElement) => child)).length).toBe(2); + }); + + it('should display passed in title in text box if title is in props', () => { + const { getByPlaceholderText } = render(); + const titleTextBox = getByPlaceholderText(minimal.titlePlaceholder!) as HTMLInputElement; + expect(titleTextBox.value).toBe(minimal.title); + }); + + it('should display passed in description in text box if description is in props', () => { + const minimalWithRenderDescriptionUndefined = { ...minimal, renderDescriptionField: true }; + const { getByPlaceholderText } = render(); + const descriptionTextBox = getByPlaceholderText(minimal.descriptionPlaceholder!) as HTMLInputElement; + expect(descriptionTextBox.value).toBe(minimal.description); + }); + + it('should pass expected values to onTitleChange callback when title is changed', async () => { + const onTitleChange = vi.fn(); + const { getByPlaceholderText } = render(); + const newText = 'New text'; + fireEvent.change(getByPlaceholderText(minimal.titlePlaceholder!), { target: { value: newText } }); + expect(onTitleChange).toHaveBeenCalledWith(minimal.schemaKey, newText); + }); + + it('should pass expected values to onDescriptionChange callback when description is changed', async () => { + const onDescriptionChange = vi.fn(); + const minimalWithRenderDescriptionTrueAndOnDescriptionChangeMocked = { ...minimal, renderDescriptionField: true, onDescriptionChange }; + const { getByPlaceholderText } = render( + + ); + const newText = 'New text'; + fireEvent.change(getByPlaceholderText(minimal.descriptionPlaceholder!), { target: { value: newText } }); + expect(onDescriptionChange).toHaveBeenCalledWith(minimal.schemaKey, newText); + }); +}); diff --git a/libs/designer-ui/src/lib/dynamicallyaddedparameter/dynamicallyaddedparameter.less b/libs/designer-ui/src/lib/dynamicallyaddedparameter/dynamicallyaddedparameter.less index 6928eb3eada..427fe9f1cba 100644 --- a/libs/designer-ui/src/lib/dynamicallyaddedparameter/dynamicallyaddedparameter.less +++ b/libs/designer-ui/src/lib/dynamicallyaddedparameter/dynamicallyaddedparameter.less @@ -47,6 +47,30 @@ } } +.msla-dynamic-added-param-footer { + display: flex; + margin-top: 10px; + margin-left: 45px; + margin-right: 51px; + height: 32px; + + .ms-TextField { + flex-grow: 1; + + .ms-TextField-wrapper { + height: 100%; + + .ms-TextField-fieldGroup { + height: 100%; + } + } + } + + .msla-dynamic-added-param-description { + flex-grow: 1; + } +} + .msla-dynamic-added-param-bottom-divider { border-bottom: solid 1px #d8d8d8; margin-top: 10px; diff --git a/libs/designer-ui/src/lib/dynamicallyaddedparameter/index.tsx b/libs/designer-ui/src/lib/dynamicallyaddedparameter/index.tsx index d0602ffc49d..657ee752bcf 100644 --- a/libs/designer-ui/src/lib/dynamicallyaddedparameter/index.tsx +++ b/libs/designer-ui/src/lib/dynamicallyaddedparameter/index.tsx @@ -17,8 +17,12 @@ export interface DynamicallyAddedParameterProps { schemaKey: string; icon: string; title: string; + description?: string; titlePlaceholder?: string; + descriptionPlaceholder?: string; + renderDescriptionField?: boolean; onTitleChange: (schemaKey: string, newValue: string | undefined) => void; + onDescriptionChange?: (schemaKey: string, newValue: string | undefined) => void; onDelete: (schemaKey: string) => void; onRenderValueField: (schemaKey: string) => JSX.Element; } @@ -81,20 +85,37 @@ export const DynamicallyAddedParameter = (props: DynamicallyAddedParameterProps) props.onTitleChange(props.schemaKey, newValue); }; + const onDescriptionChange = (e: React.FormEvent, newValue?: string): void => { + e.preventDefault(); + props?.onDescriptionChange?.(props.schemaKey, newValue); + }; + return ( -
-
-
- -
{props.onRenderValueField(props.schemaKey)}
+ <> +
+
+
+ +
{props.onRenderValueField(props.schemaKey)}
+
+
{renderMenuButton()}
-
{renderMenuButton()}
-
+ {props?.renderDescriptionField && ( +
+ +
+ )} + ); }; diff --git a/libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/__test__/floatingactionmenuoutputs.spec.tsx b/libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/__test__/floatingactionmenuoutputs.spec.tsx new file mode 100644 index 00000000000..a1ab98dc68d --- /dev/null +++ b/libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/__test__/floatingactionmenuoutputs.spec.tsx @@ -0,0 +1,402 @@ +import React from 'react'; +import { FloatingActionMenuOutputs, type FloatingActionMenuOutputsProps, type FloatingActionMenuOutputViewModel } from '..'; +import * as ReactShallowRenderer from 'react-test-renderer/shallow'; +import { describe, vi, beforeEach, afterEach, it, expect } from 'vitest'; +import { render, fireEvent, act } from '@testing-library/react'; +import { TokenPickerMode } from '../../../tokenpicker'; +import { ValueSegment } from '@microsoft/logic-apps-shared'; +import { ChangeState } from '../../../editor/base'; + +describe('ui/floatingactionmenuoutputs', () => { + let minimal: FloatingActionMenuOutputsProps, renderer: ReactShallowRenderer.ShallowRenderer; + + beforeEach(() => { + minimal = { + supportedTypes: ['text', 'file', 'email', 'boolean', 'number', 'date'], + initialValue: [ + { + id: '00000000-0000-0000-0000-000000000001', + type: 'literal', + value: '{"type": "object", "properties": {}}', + }, + ], + onChange: () => {}, + editorViewModel: { + schema: { + type: 'object', + properties: { + text: { + title: 'Text', + description: 'Enter a description of the output', + type: 'string', + 'x-ms-content-hint': 'TEXT', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: { + text: [ + { + id: '00000000-0000-0000-0000-000000000000', + type: 'literal', + value: 'response', + }, + ], + }, + }, + basePlugins: { + tokens: true, + }, + tokenPickerButtonProps: { + location: 'right', + }, + getTokenPicker: ( + _editorId: string, + _labelId: string, + _tokenPickerMode?: TokenPickerMode, + _valueType?: string, + _tokenClickedCallback?: (token: ValueSegment) => void + ) => <>, + hideValidationErrors: (_newState: ChangeState) => {}, + includeOutputDescription: true, + }; + renderer = ReactShallowRenderer.createRenderer(); + }); + + afterEach(() => { + renderer.unmount(); + }); + + it('should render', () => { + renderer.render(); + const output = renderer.getRenderOutput(); + expect(output).toBeDefined(); + }); + + it('should throw expected validation exception if editorViewModel?.schema?.properties is undefined', () => { + const minimalWithUndefinedSchemaProperties = { + ...minimal, + editorViewModel: { + schema: { + type: 'object', + properties: undefined, + }, + } as unknown as FloatingActionMenuOutputViewModel, + }; + + try { + renderer.render(); + expect(false).toBe(true); + } catch (e) { + expect(e.code).toBe('InvalidParameters'); + expect(e.message).toBe('default value needed for floatingActionMenuOutputs.'); + } + }); + + it('should call on change with expected object if title changes', () => { + const onChange = vi.fn(); + const { getByPlaceholderText } = render(); + const newText = 'New text'; + fireEvent.change(getByPlaceholderText('Enter a name'), { target: { value: newText } }); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + text: { + title: 'New text', + description: 'Enter a description of the output', + type: 'string', + 'x-ms-content-hint': 'TEXT', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: { + text: [ + { + id: '00000000-0000-0000-0000-000000000000', + type: 'literal', + value: 'response', + }, + ], + }, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if description changes', () => { + const onChange = vi.fn(); + const { getByPlaceholderText } = render(); + const newText = 'New text'; + fireEvent.change(getByPlaceholderText('Enter a description of the output'), { target: { value: newText } }); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + text: { + title: 'Text', + description: 'New text', + type: 'string', + 'x-ms-content-hint': 'TEXT', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: { + text: [ + { + id: '00000000-0000-0000-0000-000000000000', + type: 'literal', + value: 'response', + }, + ], + }, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if parameter deleted changes', async () => { + const onChange = vi.fn(); + const { getByTitle, findByText } = render(); + fireEvent.click(getByTitle('Menu')); + const deleteButton = await findByText('Delete'); + fireEvent.click(deleteButton); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: {}, + }, + outputValueSegmentsMap: {}, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if text menu item selected', async () => { + const initialEditorViewModel = { + schema: { + type: 'object', + properties: {}, + }, + outputValueSegmentsMap: {}, + }; + const onChange = vi.fn(); + const { getByText, findByRole } = render( + + ); + fireEvent.click(getByText('Add an output')); + const booleanMenuItem = await findByRole('button', { name: 'Yes/No' }); + fireEvent.click(booleanMenuItem); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + boolean: { + title: '', + description: '', + format: undefined, + type: 'boolean', + 'x-ms-content-hint': 'BOOLEAN', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: {}, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if boolean menu item selected', async () => { + const initialEditorViewModel = { + schema: { + type: 'object', + properties: {}, + }, + outputValueSegmentsMap: {}, + }; + const onChange = vi.fn(); + const { getByText, findByRole } = render( + + ); + fireEvent.click(getByText('Add an output')); + const booleanMenuItem = await findByRole('button', { name: 'Yes/No' }); + fireEvent.click(booleanMenuItem); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + boolean: { + title: '', + description: '', + type: 'boolean', + 'x-ms-content-hint': 'BOOLEAN', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: {}, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if number menu item selected', async () => { + const initialEditorViewModel = { + schema: { + type: 'object', + properties: {}, + }, + outputValueSegmentsMap: {}, + }; + const onChange = vi.fn(); + const { getByText, findByRole } = render( + + ); + fireEvent.click(getByText('Add an output')); + const numberMenuItem = await findByRole('button', { name: 'Number' }); + fireEvent.click(numberMenuItem); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + number: { + title: '', + description: '', + type: 'number', + 'x-ms-content-hint': 'NUMBER', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: {}, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if date menu item selected', async () => { + const initialEditorViewModel = { + schema: { + type: 'object', + properties: {}, + }, + outputValueSegmentsMap: {}, + }; + const onChange = vi.fn(); + const { getByText, findByRole } = render( + + ); + fireEvent.click(getByText('Add an output')); + const dateMenuItem = await findByRole('button', { name: 'Date' }); + fireEvent.click(dateMenuItem); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + date: { + title: '', + description: '', + type: 'string', + format: 'date', + 'x-ms-content-hint': 'DATE', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: {}, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if email menu item selected', async () => { + const initialEditorViewModel = { + schema: { + type: 'object', + properties: {}, + }, + outputValueSegmentsMap: {}, + }; + const onChange = vi.fn(); + const { getByText, findByRole } = render( + + ); + fireEvent.click(getByText('Add an output')); + const emailMenuItem = await findByRole('button', { name: 'Email' }); + fireEvent.click(emailMenuItem); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + email: { + title: '', + description: '', + type: 'string', + format: 'email', + 'x-ms-content-hint': 'EMAIL', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: {}, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); + + it('should call on change with expected object if file menu item selected', async () => { + const initialEditorViewModel = { + schema: { + type: 'object', + properties: {}, + }, + outputValueSegmentsMap: {}, + }; + const onChange = vi.fn(); + const { getByText, findByRole } = render( + + ); + fireEvent.click(getByText('Add an output')); + const fileMenuItem = await findByRole('button', { name: 'File' }); + fireEvent.click(fileMenuItem); + const expectedOnChange = { + value: minimal.initialValue, + viewModel: { + schema: { + type: 'object', + properties: { + file: { + title: '', + description: '', + type: 'string', + format: 'byte', + 'x-ms-content-hint': 'FILE', + 'x-ms-dynamically-added': true, + }, + }, + }, + outputValueSegmentsMap: {}, + }, + }; + expect(onChange).toHaveBeenCalledWith(expect.objectContaining(expectedOnChange)); + }); +}); diff --git a/libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/index.tsx b/libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/index.tsx index 663bb74bd24..bbb9ca3a27f 100644 --- a/libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/index.tsx +++ b/libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/index.tsx @@ -14,6 +14,7 @@ import { useIntl } from 'react-intl'; type DynamicallyAddedParameterOutputsProperties = { type: string; title: string; + description?: string; format?: string; 'x-ms-content-hint'?: DynamicallyAddedParameterTypeType; // May not be present if opening a workflow from v1 designer 'x-ms-dynamically-added': boolean; @@ -27,7 +28,7 @@ export type FloatingActionMenuOutputViewModel = { outputValueSegmentsMap: Record; }; -type FloatingActionMenuOutputsProps = { +export type FloatingActionMenuOutputsProps = { supportedTypes: string[]; initialValue: ValueSegment[]; onChange?: ChangeHandler; @@ -36,6 +37,7 @@ type FloatingActionMenuOutputsProps = { tokenPickerButtonProps: TokenPickerButtonEditorProps | undefined; getTokenPicker: GetTokenPickerHandler; hideValidationErrors: ChangeHandler | undefined; + includeOutputDescription?: boolean; }; export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps): JSX.Element => { @@ -54,17 +56,38 @@ export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps) const onDynamicallyAddedParameterTitleChange = (schemaKey: string, newValue: string | undefined): void => { const { onChange } = props; - if (onChange) { - const viewModel = clone(props.editorViewModel); - viewModel.schema.properties[schemaKey].title = newValue || ''; + if (!onChange) { + return; + } + + const viewModel = clone(props.editorViewModel); + if (viewModel?.schema?.properties?.[schemaKey]) { + viewModel.schema.properties[schemaKey].title = newValue ?? ''; + onChange({ value: props.initialValue, viewModel }); + } + }; + + const onDynamicallyAddedParameterDescriptionChange = (schemaKey: string, newValue: string | undefined): void => { + const { onChange } = props; + if (!onChange) { + return; + } + + const viewModel = clone(props.editorViewModel); + if (viewModel.schema?.properties?.[schemaKey]) { + viewModel.schema.properties[schemaKey].description = newValue ?? ''; onChange({ value: props.initialValue, viewModel }); } }; const onDynamicallyAddedParameterDelete = (schemaKey: string): void => { const { onChange } = props; - if (onChange) { - const viewModel = clone(props.editorViewModel); + if (!onChange) { + return; + } + + const viewModel = clone(props.editorViewModel); + if (viewModel?.schema?.properties && viewModel?.outputValueSegmentsMap) { delete viewModel.schema.properties[schemaKey]; delete viewModel.outputValueSegmentsMap[schemaKey]; onChange({ value: props.initialValue, viewModel }); @@ -79,8 +102,12 @@ export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps) }); const onDynamicallyAddedParameterValueChange = (schemaKey: string, newValue: ValueSegment[]) => { const { onChange } = props; - if (onChange) { - const viewModel = clone(props.editorViewModel); + if (!onChange) { + return; + } + + const viewModel = clone(props.editorViewModel); + if (viewModel?.outputValueSegmentsMap) { viewModel.outputValueSegmentsMap[schemaKey] = newValue; onChange({ value: props.initialValue, viewModel }); } @@ -92,7 +119,7 @@ export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps) placeholder={placeholder} basePlugins={props.basePlugins} readonly={false} - initialValue={props.editorViewModel.outputValueSegmentsMap[schemaKey] || []} + initialValue={props.editorViewModel?.outputValueSegmentsMap?.[schemaKey] ?? []} tokenPickerButtonProps={props.tokenPickerButtonProps} editorBlur={(newState: ChangeState) => onDynamicallyAddedParameterValueChange(schemaKey, newState.value)} getTokenPicker={props.getTokenPicker} @@ -102,14 +129,23 @@ export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps) ); }; - const dynamicParameterProps: Pick[] = Object.entries( - props.editorViewModel.schema.properties + const dynamicParameterProps: Pick[] = Object.entries( + props.editorViewModel?.schema?.properties ?? {} ) .filter(([_key, config]) => { return config?.['x-ms-dynamically-added']; }) .map(([key, config]) => { const contentHint = inferContextHintFromProperties(config); + if (props?.includeOutputDescription) { + return { + schemaKey: key, + icon: getIconForDynamicallyAddedParameterType(contentHint), + title: config.title, + description: config.description, + }; + } + return { schemaKey: key, icon: getIconForDynamicallyAddedParameterType(contentHint), @@ -119,9 +155,12 @@ export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps) const onMenuItemSelected = (item: FloatingActionMenuItem): void => { const { onChange } = props; - if (onChange) { - const viewModel = clone(props.editorViewModel); + if (!onChange) { + return; + } + const viewModel = clone(props.editorViewModel); + if (viewModel?.schema?.properties) { // Effectively a placeholder key that will be renmaed to 'title' if valid at EditorViewModel serialization. const schemaKey = generateDynamicParameterKey(Object.keys(viewModel.schema.properties), item.type); @@ -156,13 +195,25 @@ export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps) break; } } - viewModel.schema.properties[schemaKey] = { - title: '', - type, - format, - 'x-ms-content-hint': item.type, - 'x-ms-dynamically-added': true, - }; + + if (props?.includeOutputDescription) { + viewModel.schema.properties[schemaKey] = { + title: '', + description: '', + type, + format, + 'x-ms-content-hint': item.type, + 'x-ms-dynamically-added': true, + }; + } else { + viewModel.schema.properties[schemaKey] = { + title: '', + type, + format, + 'x-ms-content-hint': item.type, + 'x-ms-dynamically-added': true, + }; + } onChange({ value: props.initialValue, viewModel }); } @@ -184,6 +235,12 @@ export const FloatingActionMenuOutputs = (props: FloatingActionMenuOutputsProps) description: 'Placeholder for output title field', }); + const descriptionPlaceholder = intl.formatMessage({ + defaultMessage: 'Enter a description of the output', + id: 'Nbl3zN', + description: 'Placeholder for output description field', + }); + return ( - {dynamicParameterProps.map((props) => ( + {dynamicParameterProps.map((dynamicallyAddedParameterProps) => ( ))} diff --git a/libs/designer-ui/src/lib/settings/settingsection/settingTokenField.tsx b/libs/designer-ui/src/lib/settings/settingsection/settingTokenField.tsx index a7c045c78fa..e8347869db3 100644 --- a/libs/designer-ui/src/lib/settings/settingsection/settingTokenField.tsx +++ b/libs/designer-ui/src/lib/settings/settingsection/settingTokenField.tsx @@ -286,6 +286,7 @@ export const TokenField = ({ tokenPickerButtonProps={tokenpickerButtonProps} getTokenPicker={getTokenPicker} hideValidationErrors={hideValidationErrors} + includeOutputDescription={editorOptions?.includeOutputDescription} /> ) : (