fix: skip unsupported style updates

This commit is contained in:
Ryan Di
2026-03-18 19:18:05 +11:00
parent f944f1f7aa
commit a7281de157
2 changed files with 121 additions and 29 deletions
@@ -10,7 +10,15 @@ import {
import { Excalidraw } from "../index";
import { API } from "../tests/helpers/api";
import { UI } from "../tests/helpers/ui";
import { render } from "../tests/test-utils";
import { act, render } from "../tests/test-utils";
import {
actionChangeBackgroundColor,
actionChangeRoundness,
actionChangeStrokeWidth,
} from "./actionProperties";
const { h } = window;
describe("element locking", () => {
beforeEach(async () => {
@@ -184,5 +192,72 @@ describe("element locking", () => {
"active",
);
});
it("should not update text background when changing background in mixed frame selection", () => {
const frame = API.createElement({
type: "frame",
});
const text = API.createElement({
type: "text",
backgroundColor: COLOR_PALETTE.transparent,
});
API.setElements([text, frame]);
API.setSelectedElements([text, frame]);
act(() => {
h.app.actionManager.executeAction(actionChangeBackgroundColor, "ui", {
currentItemBackgroundColor: "#ffc9c9",
});
});
expect(API.getElement(frame).backgroundColor).toBe("#ffc9c9");
expect(API.getElement(text).backgroundColor).toBe(
COLOR_PALETTE.transparent,
);
});
it("should not update frame stroke width when changing stroke width in mixed selection", () => {
const frame = API.createElement({
type: "frame",
});
const rect = API.createElement({
type: "rectangle",
strokeWidth: STROKE_WIDTH.thin,
});
API.setElements([rect, frame]);
API.setSelectedElements([rect, frame]);
const originalFrameStrokeWidth = API.getElement(frame).strokeWidth;
act(() => {
h.app.actionManager.executeAction(
actionChangeStrokeWidth,
"ui",
STROKE_WIDTH.extraBold,
);
});
expect(API.getElement(rect).strokeWidth).toBe(STROKE_WIDTH.extraBold);
expect(API.getElement(frame).strokeWidth).toBe(originalFrameStrokeWidth);
});
it("should not update frame roundness when changing roundness in mixed selection", () => {
const frame = API.createElement({
type: "frame",
});
const rect = API.createElement({
type: "rectangle",
roundness: null,
});
API.setElements([rect, frame]);
API.setSelectedElements([rect, frame]);
act(() => {
h.app.actionManager.executeAction(actionChangeRoundness, "ui", "round");
});
expect(API.getElement(rect).roundness).not.toBe(null);
expect(API.getElement(frame).roundness).toBe(null);
});
});
});
@@ -45,6 +45,7 @@ import {
import {
isArrowElement,
isBoundToContainer,
isFrameElement,
isElbowArrow,
isLinearElement,
isLineElement,
@@ -52,7 +53,13 @@ import {
isUsingAdaptiveRadius,
} from "@excalidraw/element";
import { hasStrokeColor } from "@excalidraw/element";
import {
canChangeRoundness,
hasBackground,
hasStrokeColor,
hasStrokeStyle,
hasStrokeWidth,
} from "@excalidraw/element";
import {
updateElbowArrowPoints,
@@ -409,11 +416,13 @@ export const actionChangeBackgroundColor = register<
return el;
});
} else {
nextElements = changeProperty(elements, appState, (el) =>
newElementWith(el, {
backgroundColor: value.currentItemBackgroundColor,
}),
);
nextElements = changeProperty(elements, appState, (el) => {
return hasBackground(el.type) || isFrameElement(el)
? newElementWith(el, {
backgroundColor: value.currentItemBackgroundColor,
})
: el;
});
}
return {
@@ -476,11 +485,13 @@ export const actionChangeFillStyle = register<ExcalidrawElement["fillStyle"]>({
})`,
);
return {
elements: changeProperty(elements, appState, (el) =>
newElementWith(el, {
fillStyle: value,
}),
),
elements: changeProperty(elements, appState, (el) => {
return hasBackground(el.type)
? newElementWith(el, {
fillStyle: value,
})
: el;
}),
appState: { ...appState, currentItemFillStyle: value },
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
};
@@ -553,11 +564,13 @@ export const actionChangeStrokeWidth = register<
trackEvent: false,
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) =>
newElementWith(el, {
strokeWidth: value,
}),
),
elements: changeProperty(elements, appState, (el) => {
return hasStrokeWidth(el.type)
? newElementWith(el, {
strokeWidth: value,
})
: el;
}),
appState: { ...appState, currentItemStrokeWidth: value },
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
};
@@ -609,12 +622,14 @@ export const actionChangeSloppiness = register<ExcalidrawElement["roughness"]>({
trackEvent: false,
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) =>
newElementWith(el, {
seed: randomInteger(),
roughness: value,
}),
),
elements: changeProperty(elements, appState, (el) => {
return hasStrokeStyle(el.type)
? newElementWith(el, {
seed: randomInteger(),
roughness: value,
})
: el;
}),
appState: { ...appState, currentItemRoughness: value },
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
};
@@ -665,11 +680,13 @@ export const actionChangeStrokeStyle = register<
trackEvent: false,
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) =>
newElementWith(el, {
strokeStyle: value,
}),
),
elements: changeProperty(elements, appState, (el) => {
return hasStrokeStyle(el.type)
? newElementWith(el, {
strokeStyle: value,
})
: el;
}),
appState: { ...appState, currentItemStrokeStyle: value },
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
};
@@ -1481,7 +1498,7 @@ export const actionChangeRoundness = register<"sharp" | "round">({
perform: (elements, appState, value) => {
return {
elements: changeProperty(elements, appState, (el) => {
if (isElbowArrow(el)) {
if (isElbowArrow(el) || !canChangeRoundness(el.type)) {
return el;
}