diff --git a/src/components/border_editor/border_editor_widget.ts b/src/components/border_editor/border_editor_widget.ts index 8b5ca8f20c..40e5c6345a 100644 --- a/src/components/border_editor/border_editor_widget.ts +++ b/src/components/border_editor/border_editor_widget.ts @@ -1,10 +1,10 @@ -import { Component, useRef, useState } from "@odoo/owl"; +import { Component, onWillUpdateProps, useRef, useState } from "@odoo/owl"; import { DEFAULT_BORDER_DESC } from "../../constants"; import { BorderPosition, BorderStyle, Color, Pixel, Rect, SpreadsheetChildEnv } from "../../types"; import { BorderEditor } from "./border_editor"; interface Props { - toggleBorderEditor: () => void; + toggleBorderEditor: (ev: MouseEvent) => void; showBorderEditor: boolean; disabled?: boolean; dropdownMaxHeight?: Pixel; @@ -35,6 +35,14 @@ export class BorderEditorWidget extends Component { currentPosition: undefined, }); + setup() { + onWillUpdateProps((newProps: Props) => { + if (!newProps.showBorderEditor) { + this.state.currentPosition = undefined; + } + }); + } + get borderEditorAnchorRect(): Rect { const button = this.borderEditorButtonRef.el!; const buttonRect = button.getBoundingClientRect(); diff --git a/tests/borders/border_editor_widget_component.test.ts b/tests/borders/border_editor_widget_component.test.ts new file mode 100644 index 0000000000..3460f6af34 --- /dev/null +++ b/tests/borders/border_editor_widget_component.test.ts @@ -0,0 +1,126 @@ +import { Component, useState, xml } from "@odoo/owl"; +import { BorderPosition, BorderStyle, Color, Model, SpreadsheetChildEnv } from "../../src"; +import { BorderEditorWidget } from "../../src/components/border_editor/border_editor_widget"; +import { toHex, toZone } from "../../src/helpers"; +import { simulateClick } from "../test_helpers/dom_helper"; +import { mountComponent } from "../test_helpers/helpers"; +import { mockGetBoundingClientRect } from "../test_helpers/mock_helpers"; + +mockGetBoundingClientRect({ + "o-spreadsheet": () => ({ x: 0, y: 0, width: 1000, height: 1000 }), + "border-widget": () => ({ x: 10, y: 10, width: 300, height: 300 }), +}); + +let fixture: HTMLElement; +let model: Model; +type Props = BorderEditorWidget["props"]; + +async function setBorder({ + position, + color, + style, +}: { + position: BorderPosition; + color?: Color; + style?: BorderStyle; +}) { + if (position !== "clear") { + if (color) { + await simulateClick('div[title="Border color"]'); + await simulateClick(`div[data-color="${color}"]`); + } + if (style) { + await simulateClick('div[title="Line style"]'); + await simulateClick(`div[title="${style}"]`); + } + } + await simulateClick(`.o-line-item[name="${position}"]`); +} + +class BorderWidgetContainer extends Component { + static template = xml/* xml */ ` +
+
+ +
+
+ `; + static components = { BorderEditorWidget }; + static props = {}; + state!: { showBorderEditor: boolean }; + + setup() { + this.state = useState({ + showBorderEditor: false, + }); + } + + get borderWidgetProps(): Props { + return { + toggleBorderEditor: () => { + this.state.showBorderEditor = !this.state.showBorderEditor; + }, + showBorderEditor: this.state.showBorderEditor, + class: "border-widget", + }; + } +} + +async function mountBorderWidgetContainer() { + ({ fixture, model } = await mountComponent(BorderWidgetContainer)); +} + +describe("BorderEditorWidget", () => { + test("Clicking the widget toggles the border editor", async () => { + await mountBorderWidgetContainer(); + expect(fixture.querySelector(".o-border-selector")).toBeNull(); + await simulateClick(".border-widget"); + expect(fixture.querySelector(".o-border-selector")).not.toBeNull(); + await simulateClick(".border-widget"); + expect(fixture.querySelector(".o-border-selector")).toBeNull(); + }); + + test("The border style and color are persistent when toggling the editor", async () => { + await mountBorderWidgetContainer(); + const sheetId = model.getters.getActiveSheetId(); + const dispatch = jest.spyOn(model, "dispatch"); + await simulateClick(".border-widget"); + await setBorder({ position: "all", color: toHex("#ff0000"), style: "dashed" }); + expect(dispatch).toHaveBeenCalledWith("SET_ZONE_BORDERS", { + border: { color: "#FF0000", position: "all", style: "dashed" }, + sheetId, + target: [toZone("A1")], + }); + // close the menu + await simulateClick(".border-widget"); + expect(fixture.querySelector(".o-border-selector") as HTMLElement).toBeNull(); + // reopen the menu + await simulateClick(".border-widget"); + await setBorder({ position: "external" }); + expect(dispatch).toHaveBeenCalledWith("SET_ZONE_BORDERS", { + border: { color: "#FF0000", position: "external", style: "dashed" }, + sheetId, + target: [toZone("A1")], + }); + }); + + test("The border position is reset when toggling the editor", async () => { + await mountBorderWidgetContainer(); + const sheetId = model.getters.getActiveSheetId(); + const dispatch = jest.spyOn(model, "dispatch"); + await simulateClick(".border-widget"); + await setBorder({ position: "all", color: toHex("#ff0000"), style: "dashed" }); + expect(dispatch).toHaveBeenCalledWith("SET_ZONE_BORDERS", { + border: { color: "#FF0000", position: "all", style: "dashed" }, + sheetId, + target: [toZone("A1")], + }); + expect(fixture.querySelector('span[name="all"].active')).not.toBeNull(); + + // close the menu + await simulateClick(".border-widget"); + // reopen the menu + await simulateClick(".border-widget"); + expect(fixture.querySelector('span[name="all"].active')).toBeNull(); + }); +});