Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 7bb1f5a

Browse files
lei9444yeze322boydc2014
authored
fix: Delete & Undo on actions with LG templates doesn't bring back the LG content (#4740)
* async action delete API * async DialogEditApi * wait action deletion * update type and add registerEditorAPI * fix unit tests * add commit for lg update * Revert "add commit for lg update" This reverts commit 1b1481e. * update the lu and lg commit time * update the debounce option Co-authored-by: zeye <[email protected]> Co-authored-by: Dong Lei <[email protected]>
1 parent 53c37ba commit 7bb1f5a

File tree

11 files changed

+57
-47
lines changed

11 files changed

+57
-47
lines changed

Composer/packages/adaptive-flow/src/adaptive-flow-editor/hooks/useDialogEditApi.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ export function useDialogEditApi(shellApi: ShellApi) {
4747
}
4848

4949
function deleteSelectedActions(dialogId: string, dialogData, actionIds: string[]) {
50-
return deleteNodes(dialogData, actionIds, (nodes) => {
51-
deleteActions(dialogId, nodes);
52-
});
50+
return deleteNodes(dialogData, actionIds, (nodes) => deleteActions(dialogId, nodes));
5351
}
5452

5553
function disableSelectedActions(dialogId: string, dialogData, actionIds: string[]) {

Composer/packages/adaptive-flow/src/adaptive-flow-editor/hooks/useEditorEventApi.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,12 @@ export const useEditorEventApi = (
160160
case NodeEventTypes.Delete:
161161
trackActionChange(eventData.id);
162162
handler = (e) => {
163-
onChange(deleteSelectedAction(path, data, e.id), undefined, async () => {
164-
await onFocusSteps([]);
165-
announce(ScreenReaderMessage.ActionDeleted);
166-
});
163+
deleteSelectedAction(path, data, e.id).then((value) =>
164+
onChange(value, undefined, async () => {
165+
await onFocusSteps([]);
166+
announce(ScreenReaderMessage.ActionDeleted);
167+
})
168+
);
167169
};
168170
break;
169171
case NodeEventTypes.Insert:
@@ -272,10 +274,12 @@ export const useEditorEventApi = (
272274
handler = () => {
273275
const actionIds = getClipboardTargetsFromContext();
274276
trackActionListChange(actionIds);
275-
onChange(deleteSelectedActions(path, data, actionIds), undefined, async () => {
276-
await onFocusSteps([]);
277-
announce(ScreenReaderMessage.ActionsDeleted);
278-
});
277+
deleteSelectedActions(path, data, actionIds).then((value) =>
278+
onChange(value, undefined, async () => {
279+
await onFocusSteps([]);
280+
announce(ScreenReaderMessage.ActionsDeleted);
281+
})
282+
);
279283
};
280284
break;
281285
case NodeEventTypes.DisableSelection:

Composer/packages/client/src/shell/lgApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function createLgApi(
9999
addLgTemplate: updateLgTemplate,
100100
getLgTemplates,
101101
updateLgTemplate,
102-
debouncedUpdateLgTemplate: debounce(updateLgTemplate, 250),
102+
debouncedUpdateLgTemplate: debounce(updateLgTemplate, 250, { leading: true, trailing: false }),
103103
removeLgTemplate,
104104
removeLgTemplates,
105105
copyLgTemplate,

Composer/packages/client/src/shell/luApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function createLuApi(
8585
getLuIntents,
8686
getLuIntent,
8787
updateLuIntent,
88-
debouncedUpdateLuIntent: debounce(updateLuIntent, 250),
88+
debouncedUpdateLuIntent: debounce(updateLuIntent, 250, { leading: true, trailing: false }),
8989
renameLuIntent,
9090
removeLuIntent,
9191
};

Composer/packages/lib/shared/__tests__/dialogUtils/jsonTracker.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,34 +38,34 @@ describe('delete node flow', () => {
3838
});
3939

4040
describe('when target node does not exist', () => {
41-
it('should not change the data', () => {
41+
it('should not change the data', async () => {
4242
path = null;
43-
const result = deleteNode(dialog, path, removedDataFn);
43+
const result = await deleteNode(dialog, path, removedDataFn);
4444

4545
expect(result).toEqual(dialog);
4646
expect(removedDataFn).not.toBeCalled();
4747
});
4848
});
4949

5050
describe('when target node exists', () => {
51-
it("should delete node successfully when targetNode's currentKey type is number", () => {
51+
it("should delete node successfully when targetNode's currentKey type is number", async () => {
5252
path = 'foo.bar[0]';
53-
const result = deleteNode(dialog, path, removedDataFn);
53+
const result = await deleteNode(dialog, path, removedDataFn);
5454

5555
expect(result).toEqual({ foo: { bar: [{ $kind: 'secondOne' }] } });
5656
expect(removedDataFn).toBeCalledWith(dialog.foo.bar[0]);
5757
});
58-
it("should delete node successfully when targetNode's currentKey type is string", () => {
58+
it("should delete node successfully when targetNode's currentKey type is string", async () => {
5959
path = 'foo.bar';
60-
const result = deleteNode(dialog, path, removedDataFn);
60+
const result = await deleteNode(dialog, path, removedDataFn);
6161

6262
expect(result).toEqual({ foo: {} });
6363
expect(removedDataFn).toBeCalledWith(dialog.foo.bar);
6464
});
65-
it("removeLgTemplate function should be called when targetNode's $kind is 'Microsoft.SendActivity' && activity includes '[SendActivity_'", () => {
65+
it("removeLgTemplate function should be called when targetNode's $kind is 'Microsoft.SendActivity' && activity includes '[SendActivity_'", async () => {
6666
dialog.foo.activityNode = { $kind: 'Microsoft.SendActivity', activity: '[SendActivity_a]' };
6767
path = 'foo.activityNode';
68-
const result = deleteNode(dialog, path, removedDataFn);
68+
const result = await deleteNode(dialog, path, removedDataFn);
6969

7070
expect(removedDataFn).toBeCalledWith(dialog.foo.activityNode);
7171
expect(result).toEqual({ foo: { bar: [{ $kind: 'firstOne' }, { $kind: 'secondOne' }] } });

Composer/packages/lib/shared/src/deleteUtils/index.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,32 +48,32 @@ const collectLuIntents = (action: any, outputTemplates: string[]) => {
4848
}
4949
};
5050

51-
export const deleteAdaptiveAction = (
51+
export const deleteAdaptiveAction = async (
5252
data: MicrosoftIDialog,
53-
deleteLgTemplates: (lgTemplates: string[]) => any,
54-
deleteLuIntents: (luIntents: string[]) => any
53+
deleteLgTemplates: (lgTemplates: string[]) => Promise<void>,
54+
deleteLuIntents: (luIntents: string[]) => Promise<void>
5555
) => {
5656
const lgTemplates: string[] = [];
5757
const luIntents: string[] = [];
5858

5959
walkAdaptiveAction(data, (action) => collectLgTemplates(action, lgTemplates));
6060
walkAdaptiveAction(data, (action) => collectLuIntents(action, luIntents));
6161

62-
deleteLgTemplates(lgTemplates.filter((activity) => !!activity));
63-
deleteLuIntents(luIntents);
62+
await deleteLgTemplates(lgTemplates.filter((activity) => !!activity));
63+
await deleteLuIntents(luIntents);
6464
};
6565

66-
export const deleteAdaptiveActionList = (
66+
export const deleteAdaptiveActionList = async (
6767
data: MicrosoftIDialog[],
68-
deleteLgTemplates: (lgTemplates: string[]) => any,
69-
deleteLuIntents: (luIntents: string[]) => any
68+
deleteLgTemplates: (lgTemplates: string[]) => Promise<void>,
69+
deleteLuIntents: (luIntents: string[]) => Promise<void>
7070
) => {
7171
const lgTemplates: string[] = [];
7272
const luIntents: string[] = [];
7373

7474
walkAdaptiveActionList(data, (action) => collectLgTemplates(action, lgTemplates));
7575
walkAdaptiveActionList(data, (action) => collectLuIntents(action, luIntents));
7676

77-
deleteLgTemplates(lgTemplates.filter((activity) => !!activity));
78-
deleteLuIntents(luIntents);
77+
await deleteLgTemplates(lgTemplates.filter((activity) => !!activity));
78+
await deleteLuIntents(luIntents);
7979
};

Composer/packages/lib/shared/src/dialogFactory.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,16 +356,16 @@ export const deepCopyActions = async (
356356

357357
export const deleteAction = (
358358
data: MicrosoftIDialog,
359-
deleteLgTemplates: (templates: string[]) => any,
360-
deleteLuIntents: (luIntents: string[]) => any
359+
deleteLgTemplates: (templates: string[]) => Promise<any>,
360+
deleteLuIntents: (luIntents: string[]) => Promise<any>
361361
) => {
362362
return deleteAdaptiveAction(data, deleteLgTemplates, deleteLuIntents);
363363
};
364364

365365
export const deleteActions = (
366366
inputs: MicrosoftIDialog[],
367-
deleteLgTemplates: (templates: string[]) => any,
368-
deleteLuIntents: (luIntents: string[]) => any
367+
deleteLgTemplates: (templates: string[]) => Promise<any>,
368+
deleteLuIntents: (luIntents: string[]) => Promise<any>
369369
) => {
370370
return deleteAdaptiveActionList(inputs, deleteLgTemplates, deleteLuIntents);
371371
};

Composer/packages/lib/shared/src/dialogUtils/jsonTracker.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function queryNodes(inputDialog, nodeIds: string[]) {
9999
return nodeIds.map((id) => queryNode(inputDialog, id)).filter((x) => x !== null);
100100
}
101101

102-
export function deleteNode(inputDialog, path, callbackOnRemovedData?: (removedData: any) => any) {
102+
export async function deleteNode(inputDialog, path, callbackOnRemovedData?: (removedData: any) => Promise<any>) {
103103
const dialog = cloneDeep(inputDialog);
104104
const target = locateNode(dialog, path);
105105
if (!target) return dialog;
@@ -117,13 +117,17 @@ export function deleteNode(inputDialog, path, callbackOnRemovedData?: (removedDa
117117

118118
// invoke callback handler
119119
if (callbackOnRemovedData && typeof callbackOnRemovedData === 'function') {
120-
callbackOnRemovedData(deletedData);
120+
await callbackOnRemovedData(deletedData);
121121
}
122122

123123
return dialog;
124124
}
125125

126-
export function deleteNodes(inputDialog, nodeIds: string[], callbackOnRemovedNodes?: (nodes: any[]) => any) {
126+
export async function deleteNodes(
127+
inputDialog,
128+
nodeIds: string[],
129+
callbackOnRemovedNodes?: (nodes: any[]) => Promise<any>
130+
) {
127131
const dialog = cloneDeep(inputDialog);
128132

129133
const nodeLocations = nodeIds.map((id) => locateNode(dialog, id));
@@ -151,7 +155,7 @@ export function deleteNodes(inputDialog, nodeIds: string[], callbackOnRemovedNod
151155

152156
// invoke callback handler
153157
if (callbackOnRemovedNodes && typeof callbackOnRemovedNodes === 'function') {
154-
callbackOnRemovedNodes(deletedNodes);
158+
await callbackOnRemovedNodes(deletedNodes);
155159
}
156160

157161
return dialog;

Composer/packages/types/src/shell.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export type LuContextApi = {
6363
updateLuIntent: (id: string, intentName: string, intent: LuIntentSection) => Promise<LuFile[] | undefined>;
6464
debouncedUpdateLuIntent: (id: string, intentName: string, intent: LuIntentSection) => Promise<LuFile[] | undefined>;
6565
renameLuIntent: (id: string, intentName: string, newIntentName: string) => Promise<LuFile[] | undefined>;
66-
removeLuIntent: (id: string, intentName: string) => void;
66+
removeLuIntent: (id: string, intentName: string) => Promise<LuFile[] | undefined>;
6767
};
6868

6969
export type LgContextApi = {

Composer/packages/ui-plugins/lg/src/LgField.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ const LgField: React.FC<FieldProps<string>> = (props) => {
4949
const lgFile = lgFiles && lgFiles.find((file) => file.id === lgFileId);
5050

5151
const updateLgTemplate = useCallback(
52-
(body: string) => {
53-
shellApi.debouncedUpdateLgTemplate(lgFileId, lgName, body);
52+
async (body: string) => {
53+
await shellApi.debouncedUpdateLgTemplate(lgFileId, lgName, body);
5454
},
5555
[lgName, lgFileId]
5656
);
@@ -76,12 +76,16 @@ const LgField: React.FC<FieldProps<string>> = (props) => {
7676
const onChange = (body: string) => {
7777
if (designerId) {
7878
if (body) {
79-
updateLgTemplate(body);
79+
updateLgTemplate(body).then(() => {
80+
if (lgTemplateRef) {
81+
shellApi.commitChanges();
82+
}
83+
});
8084
props.onChange(new LgTemplateRef(lgName).toString());
81-
shellApi.commitChanges();
8285
} else {
83-
shellApi.removeLgTemplate(lgFileId, lgName);
84-
props.onChange();
86+
shellApi.removeLgTemplate(lgFileId, lgName).then(() => {
87+
props.onChange();
88+
});
8589
}
8690
}
8791
};

0 commit comments

Comments
 (0)