Skip to content

Commit

Permalink
feat(designer-ui): Adding output descriptions to Floating Action Menu (
Browse files Browse the repository at this point in the history
…#4956)

* Added description field to floating action menu output

* Rolling back changes to http.ts used for testing accidentally committed

* Improving test coverage for fam outputs and dynamically added parameter

* Update libs/designer-ui/src/lib/floatingactionmenu/floatingactionmenuoutputs/index.tsx

Co-authored-by: Travis Harris <hartra344@users.noreply.github.com>

* Adjusting null and undefined safety in fam outputs

* On change was in the wrong spot in onDynamicallyAddedParameterValueChange in fam outputs

---------

Co-authored-by: Travis Harris <hartra344@users.noreply.github.com>
  • Loading branch information
phildaniels and hartra344 committed Jun 17, 2024
1 parent cef9348 commit a48f43b
Show file tree
Hide file tree
Showing 8 changed files with 653 additions and 36 deletions.
2 changes: 2 additions & 0 deletions Localize/lang/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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?",
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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) => <div>{schemaKey}</div>,
};
renderer = ReactShallowRenderer.createRenderer();
});

afterEach(() => {
renderer.unmount();
});

it('should render', () => {
renderer.render(<DynamicallyAddedParameter {...minimal} />);
const output = renderer.getRenderOutput();
expect(output).toBeDefined();
});

it('should not render output if renderDescriptionField is undefined', () => {
const minimalWithRenderDescriptionUndefined = { ...minimal, renderDescriptionField: undefined };
renderer.render(<DynamicallyAddedParameter {...minimalWithRenderDescriptionUndefined} />);
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(<DynamicallyAddedParameter {...minimalWithRenderDescriptionUndefined} />);
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(<DynamicallyAddedParameter {...minimalWithRenderDescriptionTrue} />);
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(<DynamicallyAddedParameter {...minimal} />);
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(<DynamicallyAddedParameter {...minimalWithRenderDescriptionUndefined} />);
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(<DynamicallyAddedParameter {...minimal} onTitleChange={onTitleChange} />);
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(
<DynamicallyAddedParameter {...minimalWithRenderDescriptionTrueAndOnDescriptionChangeMocked} />
);
const newText = 'New text';
fireEvent.change(getByPlaceholderText(minimal.descriptionPlaceholder!), { target: { value: newText } });
expect(onDescriptionChange).toHaveBeenCalledWith(minimal.schemaKey, newText);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
45 changes: 33 additions & 12 deletions libs/designer-ui/src/lib/dynamicallyaddedparameter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -81,20 +85,37 @@ export const DynamicallyAddedParameter = (props: DynamicallyAddedParameterProps)
props.onTitleChange(props.schemaKey, newValue);
};

const onDescriptionChange = (e: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => {
e.preventDefault();
props?.onDescriptionChange?.(props.schemaKey, newValue);
};

return (
<div className="msla-dynamic-added-param-header">
<div className="msla-dynamic-added-param-icon" style={iconStyle} />
<div className="msla-dynamic-added-param-inputs-container">
<TextField
className="msla-dynamic-added-param-title"
placeholder={props.titlePlaceholder}
value={props.title}
onChange={onTitleChange}
/>
<div className="msla-dynamic-added-param-value">{props.onRenderValueField(props.schemaKey)}</div>
<>
<div className="msla-dynamic-added-param-header">
<div className="msla-dynamic-added-param-icon" style={iconStyle} />
<div className="msla-dynamic-added-param-inputs-container">
<TextField
className="msla-dynamic-added-param-title"
placeholder={props.titlePlaceholder}
value={props.title}
onChange={onTitleChange}
/>
<div className="msla-dynamic-added-param-value">{props.onRenderValueField(props.schemaKey)}</div>
</div>
<div className="msla-dynamic-add-param-menu-container">{renderMenuButton()}</div>
</div>
<div className="msla-dynamic-add-param-menu-container">{renderMenuButton()}</div>
</div>
{props?.renderDescriptionField && (
<div className="msla-dynamic-added-param-footer">
<TextField
className="msla-dynamic-added-param-description"
placeholder={props?.descriptionPlaceholder}
value={props?.description}
onChange={onDescriptionChange}
/>
</div>
)}
</>
);
};

Expand Down
Loading

0 comments on commit a48f43b

Please sign in to comment.