feat: support Stats bound text fontSize editing (#8187)

This commit is contained in:
David Luzar 2024-06-28 13:52:29 +02:00 committed by GitHub
parent ba8c09d529
commit abbeed3d5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 215 additions and 150 deletions

View file

@ -15,33 +15,42 @@ import "./DragInput.scss";
import type { AppState } from "../../types"; import type { AppState } from "../../types";
import { cloneJSON } from "../../utils"; import { cloneJSON } from "../../utils";
export type DragInputCallbackType<T extends StatsInputProperty> = (props: { export type DragInputCallbackType<
P extends StatsInputProperty,
E = ExcalidrawElement,
> = (props: {
accumulatedChange: number; accumulatedChange: number;
instantChange: number; instantChange: number;
originalElements: readonly ExcalidrawElement[]; originalElements: readonly E[];
originalElementsMap: ElementsMap; originalElementsMap: ElementsMap;
shouldKeepAspectRatio: boolean; shouldKeepAspectRatio: boolean;
shouldChangeByStepSize: boolean; shouldChangeByStepSize: boolean;
nextValue?: number; nextValue?: number;
property: T; property: P;
scene: Scene; scene: Scene;
originalAppState: AppState; originalAppState: AppState;
}) => void; }) => void;
interface StatsDragInputProps<T extends StatsInputProperty> { interface StatsDragInputProps<
T extends StatsInputProperty,
E = ExcalidrawElement,
> {
label: string | React.ReactNode; label: string | React.ReactNode;
icon?: React.ReactNode; icon?: React.ReactNode;
value: number | "Mixed"; value: number | "Mixed";
elements: readonly ExcalidrawElement[]; elements: readonly E[];
editable?: boolean; editable?: boolean;
shouldKeepAspectRatio?: boolean; shouldKeepAspectRatio?: boolean;
dragInputCallback: DragInputCallbackType<T>; dragInputCallback: DragInputCallbackType<T, E>;
property: T; property: T;
scene: Scene; scene: Scene;
appState: AppState; appState: AppState;
} }
const StatsDragInput = <T extends StatsInputProperty>({ const StatsDragInput = <
T extends StatsInputProperty,
E extends ExcalidrawElement = ExcalidrawElement,
>({
label, label,
icon, icon,
dragInputCallback, dragInputCallback,
@ -52,7 +61,7 @@ const StatsDragInput = <T extends StatsInputProperty>({
property, property,
scene, scene,
appState, appState,
}: StatsDragInputProps<T>) => { }: StatsDragInputProps<T, E>) => {
const app = useApp(); const app = useApp();
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const labelRef = useRef<HTMLDivElement>(null); const labelRef = useRef<HTMLDivElement>(null);
@ -61,7 +70,7 @@ const StatsDragInput = <T extends StatsInputProperty>({
const stateRef = useRef<{ const stateRef = useRef<{
originalAppState: AppState; originalAppState: AppState;
originalElements: readonly ExcalidrawElement[]; originalElements: readonly E[];
lastUpdatedValue: string; lastUpdatedValue: string;
updatePending: boolean; updatePending: boolean;
}>(null!); }>(null!);
@ -82,7 +91,7 @@ const StatsDragInput = <T extends StatsInputProperty>({
const handleInputValue = ( const handleInputValue = (
updatedValue: string, updatedValue: string,
elements: readonly ExcalidrawElement[], elements: readonly E[],
appState: AppState, appState: AppState,
) => { ) => {
if (!stateRef.current.updatePending) { if (!stateRef.current.updatePending) {
@ -173,9 +182,18 @@ const StatsDragInput = <T extends StatsInputProperty>({
y: number; y: number;
} | null = null; } | null = null;
let originalElements: ExcalidrawElement[] | null = null;
let originalElementsMap: Map<string, ExcalidrawElement> | null = let originalElementsMap: Map<string, ExcalidrawElement> | null =
null; app.scene
.getNonDeletedElements()
.reduce((acc: ElementsMap, element) => {
acc.set(element.id, deepCopyElement(element));
return acc;
}, new Map());
let originalElements: readonly E[] | null = elements.map(
(element) => originalElementsMap!.get(element.id) as E,
);
const originalAppState: AppState = cloneJSON(appState); const originalAppState: AppState = cloneJSON(appState);
let accumulatedChange: number | null = null; let accumulatedChange: number | null = null;
@ -183,21 +201,6 @@ const StatsDragInput = <T extends StatsInputProperty>({
document.body.classList.add("excalidraw-cursor-resize"); document.body.classList.add("excalidraw-cursor-resize");
const onPointerMove = (event: PointerEvent) => { const onPointerMove = (event: PointerEvent) => {
if (!originalElementsMap) {
originalElementsMap = app.scene
.getNonDeletedElements()
.reduce((acc, element) => {
acc.set(element.id, deepCopyElement(element));
return acc;
}, new Map() as ElementsMap);
}
if (!originalElements) {
originalElements = elements.map(
(element) => originalElementsMap!.get(element.id)!,
);
}
if (!accumulatedChange) { if (!accumulatedChange) {
accumulatedChange = 0; accumulatedChange = 0;
} }
@ -205,6 +208,7 @@ const StatsDragInput = <T extends StatsInputProperty>({
if ( if (
lastPointer && lastPointer &&
originalElementsMap !== null && originalElementsMap !== null &&
originalElements !== null &&
accumulatedChange !== null accumulatedChange !== null
) { ) {
const instantChange = event.clientX - lastPointer.x; const instantChange = event.clientX - lastPointer.x;

View file

@ -1,5 +1,7 @@
import type { ExcalidrawTextElement } from "../../element/types"; import type {
import { refreshTextDimensions } from "../../element/newElement"; ExcalidrawElement,
ExcalidrawTextElement,
} from "../../element/types";
import StatsDragInput from "./DragInput"; import StatsDragInput from "./DragInput";
import type { DragInputCallbackType } from "./DragInput"; import type { DragInputCallbackType } from "./DragInput";
import { mutateElement } from "../../element/mutateElement"; import { mutateElement } from "../../element/mutateElement";
@ -7,10 +9,12 @@ import { getStepSizedValue } from "./utils";
import { fontSizeIcon } from "../icons"; import { fontSizeIcon } from "../icons";
import type Scene from "../../scene/Scene"; import type Scene from "../../scene/Scene";
import type { AppState } from "../../types"; import type { AppState } from "../../types";
import { isTextElement } from "../../element"; import { isTextElement, redrawTextBoundingBox } from "../../element";
import { hasBoundTextElement } from "../../element/typeChecks";
import { getBoundTextElement } from "../../element/textElement";
interface FontSizeProps { interface FontSizeProps {
element: ExcalidrawTextElement; element: ExcalidrawElement;
scene: Scene; scene: Scene;
appState: AppState; appState: AppState;
property: "fontSize"; property: "fontSize";
@ -20,7 +24,8 @@ const MIN_FONT_SIZE = 4;
const STEP_SIZE = 4; const STEP_SIZE = 4;
const handleFontSizeChange: DragInputCallbackType< const handleFontSizeChange: DragInputCallbackType<
FontSizeProps["property"] FontSizeProps["property"],
ExcalidrawTextElement
> = ({ > = ({
accumulatedChange, accumulatedChange,
originalElements, originalElements,
@ -36,50 +41,52 @@ const handleFontSizeChange: DragInputCallbackType<
if (!latestElement || !isTextElement(latestElement)) { if (!latestElement || !isTextElement(latestElement)) {
return; return;
} }
let nextFontSize;
if (nextValue !== undefined) { if (nextValue !== undefined) {
const nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE); nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE);
} else if (origElement.type === "text") {
const newElement = {
...latestElement,
fontSize: nextFontSize,
};
const updates = refreshTextDimensions(newElement, null, elementsMap);
mutateElement(latestElement, {
...updates,
fontSize: nextFontSize,
});
return;
}
if (origElement.type === "text") {
const originalFontSize = Math.round(origElement.fontSize); const originalFontSize = Math.round(origElement.fontSize);
const changeInFontSize = Math.round(accumulatedChange); const changeInFontSize = Math.round(accumulatedChange);
let nextFontSize = Math.max( nextFontSize = Math.max(
originalFontSize + changeInFontSize, originalFontSize + changeInFontSize,
MIN_FONT_SIZE, MIN_FONT_SIZE,
); );
if (shouldChangeByStepSize) { if (shouldChangeByStepSize) {
nextFontSize = getStepSizedValue(nextFontSize, STEP_SIZE); nextFontSize = getStepSizedValue(nextFontSize, STEP_SIZE);
} }
const newElement = { }
...latestElement,
fontSize: nextFontSize, if (nextFontSize) {
};
const updates = refreshTextDimensions(newElement, null, elementsMap);
mutateElement(latestElement, { mutateElement(latestElement, {
...updates,
fontSize: nextFontSize, fontSize: nextFontSize,
}); });
redrawTextBoundingBox(
latestElement,
scene.getContainerElement(latestElement),
scene.getNonDeletedElementsMap(),
);
} }
} }
}; };
const FontSize = ({ element, scene, appState, property }: FontSizeProps) => { const FontSize = ({ element, scene, appState, property }: FontSizeProps) => {
const _element = isTextElement(element)
? element
: hasBoundTextElement(element)
? getBoundTextElement(element, scene.getNonDeletedElementsMap())
: null;
if (!_element) {
return null;
}
return ( return (
<StatsDragInput <StatsDragInput
label="F" label="F"
value={Math.round(element.fontSize * 10) / 10} value={Math.round(_element.fontSize * 10) / 10}
elements={[element]} elements={[_element]}
dragInputCallback={handleFontSizeChange} dragInputCallback={handleFontSizeChange}
icon={fontSizeIcon} icon={fontSizeIcon}
appState={appState} appState={appState}

View file

@ -1,9 +1,10 @@
import { isTextElement, refreshTextDimensions } from "../../element"; import { isTextElement, redrawTextBoundingBox } from "../../element";
import { mutateElement } from "../../element/mutateElement"; import { mutateElement } from "../../element/mutateElement";
import { isBoundToContainer } from "../../element/typeChecks"; import { hasBoundTextElement } from "../../element/typeChecks";
import type { import type {
ExcalidrawElement, ExcalidrawElement,
ExcalidrawTextElement, ExcalidrawTextElement,
NonDeletedSceneElementsMap,
} from "../../element/types"; } from "../../element/types";
import { isInGroup } from "../../groups"; import { isInGroup } from "../../groups";
import type Scene from "../../scene/Scene"; import type Scene from "../../scene/Scene";
@ -12,10 +13,12 @@ import StatsDragInput from "./DragInput";
import type { DragInputCallbackType } from "./DragInput"; import type { DragInputCallbackType } from "./DragInput";
import { getStepSizedValue } from "./utils"; import { getStepSizedValue } from "./utils";
import type { AppState } from "../../types"; import type { AppState } from "../../types";
import { getBoundTextElement } from "../../element/textElement";
interface MultiFontSizeProps { interface MultiFontSizeProps {
elements: readonly ExcalidrawElement[]; elements: readonly ExcalidrawElement[];
scene: Scene; scene: Scene;
elementsMap: NonDeletedSceneElementsMap;
appState: AppState; appState: AppState;
property: "fontSize"; property: "fontSize";
} }
@ -25,14 +28,34 @@ const STEP_SIZE = 4;
const getApplicableTextElements = ( const getApplicableTextElements = (
elements: readonly (ExcalidrawElement | undefined)[], elements: readonly (ExcalidrawElement | undefined)[],
elementsMap: NonDeletedSceneElementsMap,
) => ) =>
elements.filter( elements.reduce(
(el) => (acc: ExcalidrawTextElement[], el) => {
el && !isInGroup(el) && isTextElement(el) && !isBoundToContainer(el), if (!el || isInGroup(el)) {
) as ExcalidrawTextElement[]; return acc;
}
if (isTextElement(el)) {
acc.push(el);
return acc;
}
if (hasBoundTextElement(el)) {
const boundTextElement = getBoundTextElement(el, elementsMap);
if (boundTextElement) {
acc.push(boundTextElement);
return acc;
}
}
return acc;
},
[],
);
const handleFontSizeChange: DragInputCallbackType< const handleFontSizeChange: DragInputCallbackType<
MultiFontSizeProps["property"] MultiFontSizeProps["property"],
ExcalidrawTextElement
> = ({ > = ({
accumulatedChange, accumulatedChange,
originalElements, originalElements,
@ -41,41 +64,35 @@ const handleFontSizeChange: DragInputCallbackType<
scene, scene,
}) => { }) => {
const elementsMap = scene.getNonDeletedElementsMap(); const elementsMap = scene.getNonDeletedElementsMap();
const latestTextElements = getApplicableTextElements( const latestTextElements = originalElements.map((el) =>
originalElements.map((el) => elementsMap.get(el.id)), elementsMap.get(el.id),
); ) as ExcalidrawTextElement[];
let nextFontSize;
if (nextValue) { if (nextValue) {
const nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE); nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE);
for (const textElement of latestTextElements.map((el) => for (const textElement of latestTextElements) {
elementsMap.get(el.id),
)) {
if (!textElement || !isTextElement(textElement)) {
continue;
}
const newElement = {
...textElement,
fontSize: nextFontSize,
};
const updates = refreshTextDimensions(newElement, null, elementsMap);
mutateElement( mutateElement(
textElement, textElement,
{ {
...updates,
fontSize: nextFontSize, fontSize: nextFontSize,
}, },
false, false,
); );
redrawTextBoundingBox(
textElement,
scene.getContainerElement(textElement),
elementsMap,
false,
);
} }
scene.triggerUpdate(); scene.triggerUpdate();
return; } else {
} const originalTextElements = originalElements as ExcalidrawTextElement[];
const originalTextElements = originalElements.filter(
(el) => !isInGroup(el) && isTextElement(el) && !isBoundToContainer(el),
) as ExcalidrawTextElement[];
for (let i = 0; i < latestTextElements.length; i++) { for (let i = 0; i < latestTextElements.length; i++) {
const latestElement = latestTextElements[i]; const latestElement = latestTextElements[i];
@ -90,22 +107,24 @@ const handleFontSizeChange: DragInputCallbackType<
if (shouldChangeByStepSize) { if (shouldChangeByStepSize) {
nextFontSize = getStepSizedValue(nextFontSize, STEP_SIZE); nextFontSize = getStepSizedValue(nextFontSize, STEP_SIZE);
} }
const newElement = {
...latestElement,
fontSize: nextFontSize,
};
const updates = refreshTextDimensions(newElement, null, elementsMap);
mutateElement( mutateElement(
latestElement, latestElement,
{ {
...updates,
fontSize: nextFontSize, fontSize: nextFontSize,
}, },
false, false,
); );
redrawTextBoundingBox(
latestElement,
scene.getContainerElement(latestElement),
elementsMap,
false,
);
} }
scene.triggerUpdate(); scene.triggerUpdate();
}
}; };
const MultiFontSize = ({ const MultiFontSize = ({
@ -113,8 +132,14 @@ const MultiFontSize = ({
scene, scene,
appState, appState,
property, property,
elementsMap,
}: MultiFontSizeProps) => { }: MultiFontSizeProps) => {
const latestTextElements = getApplicableTextElements(elements); const latestTextElements = getApplicableTextElements(elements, elementsMap);
if (!latestTextElements.length) {
return null;
}
const fontSizes = latestTextElements.map( const fontSizes = latestTextElements.map(
(textEl) => Math.round(textEl.fontSize * 10) / 10, (textEl) => Math.round(textEl.fontSize * 10) / 10,
); );
@ -125,7 +150,7 @@ const MultiFontSize = ({
<StatsDragInput <StatsDragInput
label="F" label="F"
icon={fontSizeIcon} icon={fontSizeIcon}
elements={elements} elements={latestTextElements}
dragInputCallback={handleFontSizeChange} dragInputCallback={handleFontSizeChange}
value={value} value={value}
editable={editable} editable={editable}

View file

@ -21,7 +21,6 @@ import type Scene from "../../scene/Scene";
import { useExcalidrawAppState, useExcalidrawSetAppState } from "../App"; import { useExcalidrawAppState, useExcalidrawSetAppState } from "../App";
import { getAtomicUnits } from "./utils"; import { getAtomicUnits } from "./utils";
import { STATS_PANELS } from "../../constants"; import { STATS_PANELS } from "../../constants";
import { isTextElement } from "../../element";
interface StatsProps { interface StatsProps {
scene: Scene; scene: Scene;
@ -216,14 +215,12 @@ export const StatsInner = memo(
scene={scene} scene={scene}
appState={appState} appState={appState}
/> />
{singleElement.type === "text" && (
<FontSize <FontSize
property="fontSize" property="fontSize"
element={singleElement} element={singleElement}
scene={scene} scene={scene}
appState={appState} appState={appState}
/> />
)}
</div> </div>
</div> </div>
)} )}
@ -278,14 +275,13 @@ export const StatsInner = memo(
scene={scene} scene={scene}
appState={appState} appState={appState}
/> />
{multipleElements.some((el) => isTextElement(el)) && (
<MultiFontSize <MultiFontSize
property="fontSize" property="fontSize"
elements={multipleElements} elements={multipleElements}
scene={scene} scene={scene}
appState={appState} appState={appState}
elementsMap={elementsMap}
/> />
)}
</div> </div>
</div> </div>
)} )}

View file

@ -11,7 +11,7 @@ import * as StaticScene from "../../renderer/staticScene";
import { vi } from "vitest"; import { vi } from "vitest";
import { reseed } from "../../random"; import { reseed } from "../../random";
import { setDateTimeForTests } from "../../utils"; import { setDateTimeForTests } from "../../utils";
import { Excalidraw } from "../.."; import { Excalidraw, mutateElement } from "../..";
import { t } from "../../i18n"; import { t } from "../../i18n";
import type { import type {
ExcalidrawElement, ExcalidrawElement,
@ -37,10 +37,14 @@ const editInput = (input: HTMLInputElement, value: string) => {
}; };
const getStatsProperty = (label: string) => { const getStatsProperty = (label: string) => {
const elementStats = UI.queryStats()?.querySelector("#elementStats");
if (elementStats) { if (elementStats) {
const properties = elementStats?.querySelector(".statsItem"); const properties = elementStats?.querySelector(".statsItem");
return properties?.querySelector?.( return (
properties?.querySelector?.(
`.drag-input-container[data-testid="${label}"]`, `.drag-input-container[data-testid="${label}"]`,
) || null
); );
} }
@ -57,7 +61,7 @@ const testInputProperty = (
const input = getStatsProperty(label)?.querySelector( const input = getStatsProperty(label)?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(input).not.toBeNull(); expect(input).toBeDefined();
expect(input.value).toBe(initialValue.toString()); expect(input.value).toBe(initialValue.toString());
editInput(input, String(nextValue)); editInput(input, String(nextValue));
if (property === "angle") { if (property === "angle") {
@ -131,8 +135,8 @@ describe("stats for a generic element", () => {
}); });
it("should open stats", () => { it("should open stats", () => {
expect(stats).not.toBeNull(); expect(stats).toBeDefined();
expect(elementStats).not.toBeNull(); expect(elementStats).toBeDefined();
// title // title
const title = elementStats?.querySelector("h3"); const title = elementStats?.querySelector("h3");
@ -140,18 +144,18 @@ describe("stats for a generic element", () => {
// element type // element type
const elementType = elementStats?.querySelector(".elementType"); const elementType = elementStats?.querySelector(".elementType");
expect(elementType).not.toBeNull(); expect(elementType).toBeDefined();
expect(elementType?.lastChild?.nodeValue).toBe(t("element.rectangle")); expect(elementType?.lastChild?.nodeValue).toBe(t("element.rectangle"));
// properties // properties
const properties = elementStats?.querySelector(".statsItem"); const properties = elementStats?.querySelector(".statsItem");
expect(properties?.childNodes).not.toBeNull(); expect(properties?.childNodes).toBeDefined();
["X", "Y", "W", "H", "A"].forEach((label) => () => { ["X", "Y", "W", "H", "A"].forEach((label) => () => {
expect( expect(
properties?.querySelector?.( properties?.querySelector?.(
`.drag-input-container[data-testid="${label}"]`, `.drag-input-container[data-testid="${label}"]`,
), ),
).not.toBeNull(); ).toBeDefined();
}); });
}); });
@ -174,7 +178,7 @@ describe("stats for a generic element", () => {
const input = getStatsProperty("W")?.querySelector( const input = getStatsProperty("W")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(input).not.toBeNull(); expect(input).toBeDefined();
expect(input.value).toBe(rectangle.width.toString()); expect(input.value).toBe(rectangle.width.toString());
editInput(input, "123.123"); editInput(input, "123.123");
expect(h.elements.length).toBe(1); expect(h.elements.length).toBe(1);
@ -333,7 +337,7 @@ describe("stats for a non-generic element", () => {
const input = getStatsProperty("F")?.querySelector( const input = getStatsProperty("F")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(input).not.toBeNull(); expect(input).toBeDefined();
expect(input.value).toBe(text.fontSize.toString()); expect(input.value).toBe(text.fontSize.toString());
editInput(input, "36"); editInput(input, "36");
expect(text.fontSize).toBe(36); expect(text.fontSize).toBe(36);
@ -366,7 +370,7 @@ describe("stats for a non-generic element", () => {
elementStats = stats?.querySelector("#elementStats"); elementStats = stats?.querySelector("#elementStats");
expect(elementStats).not.toBeNull(); expect(elementStats).toBeDefined();
// cannot change angle // cannot change angle
const angle = getStatsProperty("A")?.querySelector(".drag-input"); const angle = getStatsProperty("A")?.querySelector(".drag-input");
@ -387,7 +391,7 @@ describe("stats for a non-generic element", () => {
}, },
}); });
elementStats = stats?.querySelector("#elementStats"); elementStats = stats?.querySelector("#elementStats");
expect(elementStats).not.toBeNull(); expect(elementStats).toBeDefined();
const widthToHeight = image.width / image.height; const widthToHeight = image.width / image.height;
// when width or height is changed, the aspect ratio is preserved // when width or height is changed, the aspect ratio is preserved
@ -399,6 +403,35 @@ describe("stats for a non-generic element", () => {
expect(image.height).toBe(80); expect(image.height).toBe(80);
expect(image.width / image.height).toBe(widthToHeight); expect(image.width / image.height).toBe(widthToHeight);
}); });
it("should display fontSize for bound text", () => {
const container = API.createElement({
type: "rectangle",
width: 200,
height: 100,
});
const text = API.createElement({
type: "text",
width: 200,
height: 100,
containerId: container.id,
fontSize: 20,
});
mutateElement(container, {
boundElements: [{ type: "text", id: text.id }],
});
h.elements = [container, text];
API.setSelectedElements([container]);
const fontSize = getStatsProperty("F")?.querySelector(
".drag-input",
) as HTMLInputElement;
expect(fontSize).toBeDefined();
editInput(fontSize, "40");
expect(text.fontSize).toBe(40);
});
}); });
// multiple elements // multiple elements
@ -515,25 +548,25 @@ describe("stats for multiple elements", () => {
const width = getStatsProperty("W")?.querySelector( const width = getStatsProperty("W")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(width).not.toBeNull(); expect(width).toBeDefined();
expect(width.value).toBe("Mixed"); expect(width.value).toBe("Mixed");
const height = getStatsProperty("H")?.querySelector( const height = getStatsProperty("H")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(height).not.toBeNull(); expect(height).toBeDefined();
expect(height.value).toBe("Mixed"); expect(height.value).toBe("Mixed");
const angle = getStatsProperty("A")?.querySelector( const angle = getStatsProperty("A")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(angle).not.toBeNull(); expect(angle).toBeDefined();
expect(angle.value).toBe("0"); expect(angle.value).toBe("0");
const fontSize = getStatsProperty("F")?.querySelector( const fontSize = getStatsProperty("F")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(fontSize).not.toBeNull(); expect(fontSize).toBeDefined();
// changing width does not affect text // changing width does not affect text
editInput(width, "200"); editInput(width, "200");
@ -579,7 +612,7 @@ describe("stats for multiple elements", () => {
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(x).not.toBeNull(); expect(x).toBeDefined();
expect(Number(x.value)).toBe(x1); expect(Number(x.value)).toBe(x1);
editInput(x, "300"); editInput(x, "300");
@ -592,7 +625,7 @@ describe("stats for multiple elements", () => {
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(y).not.toBeNull(); expect(y).toBeDefined();
expect(Number(y.value)).toBe(y1); expect(Number(y.value)).toBe(y1);
editInput(y, "200"); editInput(y, "200");
@ -604,13 +637,13 @@ describe("stats for multiple elements", () => {
const width = getStatsProperty("W")?.querySelector( const width = getStatsProperty("W")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(width).not.toBeNull(); expect(width).toBeDefined();
expect(Number(width.value)).toBe(200); expect(Number(width.value)).toBe(200);
const height = getStatsProperty("H")?.querySelector( const height = getStatsProperty("H")?.querySelector(
".drag-input", ".drag-input",
) as HTMLInputElement; ) as HTMLInputElement;
expect(height).not.toBeNull(); expect(height).toBeDefined();
expect(Number(height.value)).toBe(200); expect(Number(height.value)).toBe(200);
editInput(width, "400"); editInput(width, "400");