Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Data Mapper): Update draggable list component and apply suggestions for Error/Warnings panel #6417

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
10 changes: 10 additions & 0 deletions Localize/lang/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@
"BBD8Em": "Key",
"BCAnZP": "Either a single format specifier character or a custom format pattern that indicates how to format the value of this timestamp. If format is not provided, the ISO 8601 format ('o') is used.",
"BCgiRh": "Warning: custom value does not match one of the allowed types for this input",
"BFBJi2": "No errors found in your map.",
"BGw6eH": "Missing required properties ''{missingProperties}'' for authentication type ''{authType}''",
"BHe7qY": "Required. The URI encoded string.",
"BIzX3S": "Learn more about undo operations",
Expand Down Expand Up @@ -784,6 +785,7 @@
"Q8zxeb": "{name} Key",
"QBK72a": "Add custom modules, uncover new scenarios, and find troubleshooting tips",
"QGbUXX": "Status code",
"QHdKnm": "Drag and connect nodes to transform",
"QNfUf/": "Full screen",
"QT4IaP": "Filtered!",
"QVtqAn": "Description",
Expand Down Expand Up @@ -1440,6 +1442,7 @@
"_BBD8Em.comment": "A placeholder for the dictionary key",
"_BCAnZP.comment": "Optional string parameter to identify format of timestamp returned",
"_BCgiRh.comment": "Warning message for when custom value does not match one of the function node input's allowed types",
"_BFBJi2.comment": "Message displayed when there are no errors",
"_BGw6eH.comment": "Error message when missing multiple required authentication properties",
"_BHe7qY.comment": "Required URI encoded string parameter to be converted using uriComponentToString function",
"_BIzX3S.comment": "Aria label for undo operations in chatbot",
Expand Down Expand Up @@ -1847,6 +1850,7 @@
"_Q8zxeb.comment": "Accessibility Label for dictionary text key field",
"_QBK72a.comment": "This is a message give link to user to find out more about this action",
"_QGbUXX.comment": "Response status code for test map API",
"_QHdKnm.comment": "default placeholder text",
"_QNfUf/.comment": "Full Screen token picker",
"_QT4IaP.comment": "Filtered text",
"_QVtqAn.comment": "Label for description column.",
Expand Down Expand Up @@ -2172,6 +2176,7 @@
"_byRkj+.comment": "This operation has secure inputs or outputs enabled.",
"_byTBrn.comment": "Filter by Custom category of connectors",
"_c/+j08.comment": "Do, as in \"to do an action\"",
"_c/2T3J.comment": "Error tab name",
"_c2XklE.comment": "Placeholder text for Operation/Connector search bar",
"_c2ZT7p.comment": "Title text for details",
"_c3FLox.comment": "Time zone value ",
Expand Down Expand Up @@ -2308,6 +2313,7 @@
"_gfBWfH.comment": "Label for Email type dynamically added parameter",
"_ginGIZ.comment": "Time zone value ",
"_gkY5ya.comment": "Delete dynamic parameter corresponding to this row",
"_gnYVoF.comment": "Message displayed when there are no warnings",
"_gpUphl.comment": "Audience Placeholder Text",
"_gtQYgr.comment": "Label for description of custom isFloat Function",
"_gu9o9z.comment": "Label for description of custom iterationIndexes Function",
Expand Down Expand Up @@ -2335,6 +2341,7 @@
"_hlrKDC.comment": "Column name for connection display name",
"_ho2D6F.comment": "Close panel",
"_hq1mk6.comment": "Error while parsing expression for path value",
"_hqa/U1.comment": "Warning tab name",
"_hrbDu6.comment": "Label text for retry duration",
"_htj+eZ.comment": "Header to update target schema",
"_hvbclb.comment": "MSI Audience Label Display Name",
Expand Down Expand Up @@ -2942,6 +2949,7 @@
"byRkj+": "This operation has secure inputs or outputs enabled.",
"byTBrn": "Custom",
"c/+j08": "Do",
"c/2T3J": "Errors",
"c2XklE": "Search",
"c2ZT7p": "Details",
"c3FLox": "(UTC+10:00) Hobart",
Expand Down Expand Up @@ -3078,6 +3086,7 @@
"gfBWfH": "Email",
"ginGIZ": "(UTC-06:00) Guadalajara, Mexico City, Monterrey",
"gkY5ya": "Delete",
"gnYVoF": "No warnings found in your map.",
"gpUphl": "Enter the audience.",
"gtQYgr": "Returns a boolean that indicates whether a string is a floating-point number",
"gu9o9z": "When used inside until loop, this function returns the current iteration index of the specified loop.",
Expand Down Expand Up @@ -3105,6 +3114,7 @@
"hlrKDC": "Connection",
"ho2D6F": "Close panel",
"hq1mk6": "Operation path value does not match the template for segment. Path {pathValue}, Template {pathTemplate}",
"hqa/U1": "Warnings",
"hrbDu6": "Duration",
"htj+eZ": "Update target schema",
"hvbclb": "Audience",
Expand Down
6 changes: 4 additions & 2 deletions libs/data-mapper-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"name": "@microsoft/logic-apps-data-mapper-v2",
"version": "5.21.0",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fluentui/azure-themes": "8.5.70",
"@fluentui/react": "8.110.2",
"@fluentui/react-components": "9.50.0",
Expand All @@ -18,6 +21,7 @@
"@react-hookz/web": "22.0.0",
"@reduxjs/toolkit": "1.8.5",
"@xyflow/react": "^12.3.5",
"dnd-kit-sortable-tree": "^0.1.73",
"elkjs": "0.9.1",
"fuse.js": "6.6.2",
"immer": "9.0.15",
Expand All @@ -27,11 +31,9 @@
"react-arborist": "^3.4.0",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
"react-draggable-list": "^4.2.1",
"react-icons": "4.8.0",
"react-intl": "6.3.0",
"react-redux": "8.0.2",
"react-virtualized-tree": "^3.4.1",
"redux-thunk": "2.4.2",
"redux-undo": "1.1.0",
"use-resize-observer": "^9.1.0"
Expand Down
17 changes: 15 additions & 2 deletions libs/data-mapper-v2/src/components/canvas/ReactFlow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AppDispatch, RootState } from '../../core/state/Store';
import { useEffect, useRef, useCallback, useState, type MouseEvent } from 'react';
import { useEffect, useRef, useCallback, useState, type MouseEvent, useMemo } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import type { Connection, Edge, ConnectionLineComponent, NodeTypes, OnNodeDrag, IsValidConnection } from '@xyflow/react';
import { PanOnScrollMode, ReactFlow, useReactFlow } from '@xyflow/react';
Expand All @@ -21,6 +21,7 @@ import { isFunctionNode } from '../../utils/ReactFlow.Util';
import useReactFlowStates from './useReactflowStates';
import mapPlaceholder from '../../images/map-placeholder.svg';
import { useSchemasButNoConnections } from '../../core/state/selectors/selectors';
import { useIntl } from 'react-intl';
interface DMReactFlowProps {
setIsMapStateDirty?: (isMapStateDirty: boolean) => void;
}
Expand All @@ -41,6 +42,7 @@ export const ReactFlowWrapper = ({ setIsMapStateDirty }: DMReactFlowProps) => {
const showPlaceholder = useSchemasButNoConnections();
const ref = useRef<HTMLDivElement>(null);
const dispatch = useDispatch<AppDispatch>();
const intl = useIntl();
const { width: newWidth = undefined, height: newHeight = undefined } = useResizeObserver<HTMLDivElement>({
ref,
});
Expand All @@ -56,6 +58,17 @@ export const ReactFlowWrapper = ({ setIsMapStateDirty }: DMReactFlowProps) => {

const isMapStateDirty = useSelector((state: RootState) => state.dataMap.present.isDirty);

const stringResources = useMemo(
() => ({
DEFAULT_PLACEHOLDER: intl.formatMessage({
defaultMessage: 'Drag and connect nodes to transform',
id: 'QHdKnm',
description: 'default placeholder text',
}),
}),
[intl]
);

const onEdgeConnect = useCallback(
(connection: Connection) => {
let source = '';
Expand Down Expand Up @@ -241,7 +254,7 @@ export const ReactFlowWrapper = ({ setIsMapStateDirty }: DMReactFlowProps) => {
{showPlaceholder && (
<div className={styles.background}>
<div className={styles.placeholderContainer}>
<div className={styles.placeholderText}>Drag and drop properties to transform</div>
<div className={styles.placeholderText}>{stringResources.DEFAULT_PLACEHOLDER}</div>
<img src={mapPlaceholder} alt="placeholder" className={styles.placeholderImage} />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { WarningModalState, openDiscardWarningModal } from '../../core/state/ModalSlice';
import type { AppDispatch, RootState } from '../../core/state/Store';
import { Toolbar, ToolbarButton, ToolbarGroup, Switch, tokens, useId, Toaster } from '@fluentui/react-components';
import { Dismiss20Regular, OpenFilled, Save20Regular } from '@fluentui/react-icons';
import { Dismiss20Regular, PlayRegular, Save20Regular, TextGrammarErrorRegular } from '@fluentui/react-icons';
import { useCallback, useEffect, useMemo } from 'react';
import { useIntl } from 'react-intl';
import { useDispatch, useSelector } from 'react-redux';
Expand Down Expand Up @@ -220,7 +220,7 @@ export const EditorCommandBar = (_props: EditorCommandBarProps) => {
</ToolbarButton>
<ToolbarButton
aria-label={Resources.OPEN_TEST_PANEL}
icon={<OpenFilled color={disabledState.test ? undefined : tokens.colorPaletteBlueBorderActive} />}
icon={<PlayRegular color={disabledState.test ? undefined : tokens.colorPaletteBlueBorderActive} />}
disabled={disabledState.test}
title={disabledState.test ? Resources.DISABLED_TEST : ''}
onClick={onTestClick}
Expand All @@ -231,7 +231,7 @@ export const EditorCommandBar = (_props: EditorCommandBarProps) => {
<ToolbarGroup className={toolbarStyles.toolbarGroup}>
<ToolbarButton
disabled={disabledState.mapChecker}
icon={<OpenFilled color={disabledState.mapChecker ? undefined : tokens.colorPaletteBlueBorderActive} />}
icon={<TextGrammarErrorRegular color={disabledState.mapChecker ? undefined : tokens.colorPaletteBlueBorderActive} />}
onClick={onMapCheckerClick}
>
{Resources.VIEW_MAP_CHECKER}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,10 @@
import { useCallback, useMemo } from 'react';
import type { ConnectionDictionary, InputConnection } from '../../../models/Connection';
import type { TemplateProps } from 'react-draggable-list';
import type { FunctionData, FunctionInput } from '../../../models';
import { useDispatch, useSelector } from 'react-redux';
import type { RootState } from '../../../core/state/Store';
import type { FunctionData } from '../../../models';
import { InputDropdown, type InputOptionProps } from '../inputDropdown/InputDropdown';
import { getInputTypeFromNode, validateAndCreateConnectionInput } from './inputTab';
import { deleteConnectionFromFunctionMenu, setConnectionInput } from '../../../core/state/DataMapSlice';
import { getInputName, getInputValue } from '../../../utils/Function.Utils';
import { useStyles } from './styles';
import { ListItem } from '@fluentui/react-list-preview';
import { Badge, Button } from '@fluentui/react-components';
import { DeleteRegular, ReOrderRegular } from '@fluentui/react-icons';
import type { SchemaType } from '@microsoft/logic-apps-shared';
import * as React from 'react';

export type CommonProps = {
functionKey: string;
data: FunctionData;
inputsFromManifest: FunctionInput[];
connections: ConnectionDictionary;
schemaType: SchemaType;
draggable: boolean;
};

export type TemplateItemProps = { input: InputConnection; index: number };
type InputListProps = TemplateProps<TemplateItemProps, CommonProps> & {};
type CustomListItemProps = {
name?: string;
value?: string;
Expand All @@ -42,84 +21,6 @@ type CustomListItemProps = {
key: string;
};

const InputList = (props: InputListProps) => {
const connectionDictionary = useSelector((state: RootState) => state.dataMap.present.curDataMapOperation.dataMapConnections);
const sourceSchemaDictionary = useSelector((state: RootState) => state.dataMap.present.curDataMapOperation.flattenedSourceSchema);
const functionNodeDictionary = useSelector((state: RootState) => state.dataMap.present.curDataMapOperation.functionNodes);
const dispatch = useDispatch();
const {
item: { input, index },
commonProps,
dragHandleProps,
} = props;
const { functionKey, data, inputsFromManifest, connections, schemaType } = commonProps;

const inputName = useMemo(() => getInputName(input, connections), [connections, input]);
const inputValue = useMemo(() => getInputValue(input), [input]);
const inputType = useMemo(() => getInputTypeFromNode(input), [input]);
const removeUnboundedInput = useCallback(() => {
const targetNodeReactFlowKey = functionKey;
dispatch(
deleteConnectionFromFunctionMenu({
targetId: targetNodeReactFlowKey,
inputIndex: index,
})
);
}, [dispatch, functionKey, index]);

const updateInput = useCallback(
(newValue: InputConnection) => {
const targetNodeReactFlowKey = functionKey;
dispatch(
setConnectionInput({
targetNode: data,
targetNodeReactFlowKey,
inputIndex: index,
input: newValue,
})
);
},
[data, dispatch, functionKey, index]
);

const validateAndCreateConnection = useCallback(
(optionValue: string | undefined, option: InputOptionProps | undefined) => {
if (optionValue) {
const input = validateAndCreateConnectionInput(
optionValue,
option,
connectionDictionary,
data,
functionNodeDictionary,
sourceSchemaDictionary
);
if (input) {
updateInput(input);
}
}
},
[connectionDictionary, data, functionNodeDictionary, sourceSchemaDictionary, updateInput]
);

return (
<CustomListItem
name={inputName}
value={inputValue}
remove={removeUnboundedInput}
index={index}
customValueAllowed={inputsFromManifest[0].allowCustomInput}
schemaType={schemaType}
type={inputType}
validateAndCreateConnection={validateAndCreateConnection}
functionData={data}
functionKey={functionKey}
key={`input-${inputName}`}
draggable={commonProps.draggable}
dragHandleProps={dragHandleProps}
/>
);
};

export const CustomListItem = (props: CustomListItemProps) => {
const styles = useStyles();
const {
Expand All @@ -138,7 +39,7 @@ export const CustomListItem = (props: CustomListItemProps) => {
} = props;

return (
<ListItem key={`input-${name}`} className={styles.draggableListItem}>
<div key={`input-${name}`} className={styles.draggableListItem}>
<div className={styles.draggableListContainer}>
<span className={styles.formControl}>
<InputDropdown
Expand All @@ -164,12 +65,6 @@ export const CustomListItem = (props: CustomListItemProps) => {
)}
</span>
</div>
</ListItem>
</div>
);
};

export default class InputListWrapper extends React.Component<InputListProps, {}> {
render() {
return <InputList {...this.props} />;
}
}
Loading
Loading