diff --git a/addons/html_editor/static/src/core/history_plugin.js b/addons/html_editor/static/src/core/history_plugin.js index 434ee2df81d4a..7ae71ee82b80c 100644 --- a/addons/html_editor/static/src/core/history_plugin.js +++ b/addons/html_editor/static/src/core/history_plugin.js @@ -544,7 +544,7 @@ export class HistoryPlugin extends Plugin { * @param { Object } [params] * @param { "consumed"|"undo"|"redo" } [params.stepState] */ - addStep({ stepState } = {}) { + addStep({ stepState, customStep } = {}) { // @todo @phoenix should we allow to pause the making of a step? // if (!this.stepsActive) { // return; @@ -559,12 +559,15 @@ export class HistoryPlugin extends Plugin { // executing the onChange callback. // It is useful for external components if they execute shared.can[Undo|Redo] const currentStep = this.currentStep; + if (customStep) { + currentStep.customStep = customStep; + } if (stepState) { this.stepsStates.set(currentStep.id, stepState); } this.handleObserverRecords(); - if (!currentStep.mutations.length) { + if (!currentStep.mutations.length && !currentStep.customStep) { return false; } const stepCommonAncestor = this.getMutationsRoot(currentStep.mutations) || this.editable; @@ -618,6 +621,10 @@ export class HistoryPlugin extends Plugin { this.stepsStates.set(this.steps[pos].id, "consumed"); this.revertMutations(this.steps[pos].mutations, { forNewStep: true }); this.setSerializedSelection(this.steps[pos].selection); + if (this.steps[pos].customStep) { + this.steps[pos].customStep.undo(); + this.currentStep.customStep = this.steps[pos].customStep; + } this.addStep({ stepState: "undo" }); // Consider the last position of the history as an undo. } @@ -639,6 +646,10 @@ export class HistoryPlugin extends Plugin { this.stepsStates.set(this.steps[pos].id, "consumed"); this.revertMutations(this.steps[pos].mutations, { forNewStep: true }); this.setSerializedSelection(this.steps[pos].selection); + if (this.steps[pos].customStep) { + this.steps[pos].customStep.redo(); + this.currentStep.customStep = this.steps[pos].customStep; + } this.addStep({ stepState: "redo" }); } this.dispatchTo("post_redo_handlers"); diff --git a/addons/html_editor/static/tests/history.test.js b/addons/html_editor/static/tests/history.test.js index bd10f8fedcfd6..2ad2684f57a44 100644 --- a/addons/html_editor/static/tests/history.test.js +++ b/addons/html_editor/static/tests/history.test.js @@ -84,6 +84,35 @@ describe("undo", () => { redo(editor); expect(getContent(el)).toBe(`

a[]c

`); }); + + test("should trigger custom step", async () => { + const { el, editor } = await setupEditor(`

[]c

`); + const p = el.querySelector("p"); + editor.shared.dom.insert("a"); + editor.shared.history.addStep(); + editor.shared.history.addStep({ + customStep: { + undo: () => { + expect.step("custom undo"); + }, + redo: () => { + expect.step("custom redo"); + }, + }, + }); + p.prepend(document.createTextNode("b")); + undo(editor); + expect.verifySteps(["custom undo"]); + expect(getContent(el)).toBe(`

a[]c

`); + undo(editor); + expect(getContent(el)).toBe(`

[]c

`); + redo(editor); + expect.verifySteps([]); + expect(getContent(el)).toBe(`

a[]c

`); + redo(editor); + expect.verifySteps(["custom redo"]); + expect(getContent(el)).toBe(`

a[]c

`); + }); }); describe("redo", () => {