diff --git a/src/plugins/core/borders.ts b/src/plugins/core/borders.ts index d9e32a1710..867ba87368 100644 --- a/src/plugins/core/borders.ts +++ b/src/plugins/core/borders.ts @@ -342,22 +342,35 @@ export class BordersPlugin extends CorePlugin implements Bor ) { const borders: ZoneBorder[] = []; const plannedBorder = newBorder ? { zone, style: newBorder } : undefined; - const sideToClear = { - left: force || !!newBorder?.left, - right: force || !!newBorder?.right, - top: force || !!newBorder?.top, - bottom: force || !!newBorder?.bottom, + + // For each side, decide if we must clear the border on the *adjacent* + // existing cell when we draw on the opposite side of the new zone. + // + // Example: + // - newBorder.right is set → we draw border on the RIGHT side of `zone` + // - the cell on the right may already have a LEFT border on that edge + // In that case we clear that LEFT border, so only the new RIGHT border + // remains on the shared edge. + // + // existingBorderSideToClear[side] = true means we should clear the border on that + // side of the existing adjacent zone before adding the new border. + const existingBorderSideToClear = { + left: force || !!newBorder?.right, + right: force || !!newBorder?.left, + top: force || !!newBorder?.bottom, + bottom: force || !!newBorder?.top, }; let editingZone: Zone[] = [zone]; for (const existingBorder of this.borders[sheetId] ?? []) { const inter = intersection(existingBorder.zone, zone); if (!inter) { - // Clear adjacent borders on which you write + // Check if the existing border is adjacent to the new zone const adjacentEdge = adjacent(existingBorder.zone, zone); - if (adjacentEdge && sideToClear[adjacentEdge.position]) { + if (adjacentEdge && existingBorderSideToClear[adjacentEdge.position]) { for (const newZone of splitIfAdjacent(existingBorder.zone, zone)) { const border = this.computeBorderFromZone(newZone, existingBorder); const adjacentEdge = adjacent(newZone, zone); + // Clear the existing border on the side that touches the new zone switch (adjacentEdge?.position) { case "left": border.style.left = undefined; diff --git a/tests/borders/border_plugin.test.ts b/tests/borders/border_plugin.test.ts index 129dceae91..90548bc92e 100644 --- a/tests/borders/border_plugin.test.ts +++ b/tests/borders/border_plugin.test.ts @@ -147,6 +147,60 @@ describe("borders", () => { }); }); + test("Preserves side borders when combining external and all via command", () => { + const model = new Model(); + const defaultBorder = DEFAULT_BORDER_DESC; + + setZoneBorders(model, { position: "all" }, ["C3"]); + setBordersOnTarget(model, ["C2"], { + top: defaultBorder, + right: defaultBorder, + left: defaultBorder, + }); + setBordersOnTarget(model, ["C4"], { + bottom: defaultBorder, + left: defaultBorder, + right: defaultBorder, + }); + setBordersOnTarget(model, ["B3"], { + top: defaultBorder, + bottom: defaultBorder, + left: defaultBorder, + }); + setBordersOnTarget(model, ["D3"], { + top: defaultBorder, + bottom: defaultBorder, + right: defaultBorder, + }); + + expect(getBorder(model, "C3")).toEqual({ + top: defaultBorder, + bottom: defaultBorder, + left: defaultBorder, + right: defaultBorder, + }); + expect(getBorder(model, "C2")).toEqual({ + top: defaultBorder, + left: defaultBorder, + right: defaultBorder, + }); + expect(getBorder(model, "C4")).toEqual({ + bottom: defaultBorder, + left: defaultBorder, + right: defaultBorder, + }); + expect(getBorder(model, "B3")).toEqual({ + top: defaultBorder, + left: defaultBorder, + bottom: defaultBorder, + }); + expect(getBorder(model, "D3")).toEqual({ + top: defaultBorder, + bottom: defaultBorder, + right: defaultBorder, + }); + }); + test("can set all borders in a zone", () => { const model = new Model();