diff --git a/packages/core/__tests__/editor/Editor.test.ts b/packages/core/__tests__/editor/Editor.test.ts index e6c6c761bc..675095733b 100644 --- a/packages/core/__tests__/editor/Editor.test.ts +++ b/packages/core/__tests__/editor/Editor.test.ts @@ -18,6 +18,7 @@ import { describe, expect, test } from '@jest/globals'; import { Editor, Geometry } from '../../src'; import { ModelChecker } from '../serialization/utils'; import { parseXml } from '../../src/util/xmlUtils'; +import Cell from '../../src/view/cell/Cell'; describe('writeGraphModel', () => { test('empty model', () => { @@ -98,3 +99,42 @@ test('readGraphModel', () => { }, }); }); + +describe('cycleAttribute', () => { + test('nullish cycleAttributeName, so untouched style', () => { + const editor = new Editor(null!); + editor.cycleAttributeName = null!; // force nullish value + + const cell = new Cell(); + const originalStyle = { ...cell.getStyle() }; + editor.cycleAttribute(cell); + expect(cell.getStyle()).toEqual(originalStyle); + }); + + test('default cycleAttributeName, consumeCycleAttribute returns a value', () => { + const editor = new Editor(null!); + editor.consumeCycleAttribute = () => { + return 'orange'; // simulate for test + }; + + const cell = new Cell(); + cell.getStyle().strokeColor = 'yellow'; + const originalStyle = { ...cell.getStyle() }; + expect(cell.getStyle().fillColor).toBeUndefined(); + editor.cycleAttribute(cell); + expect(cell.getStyle()).toEqual({ ...originalStyle, fillColor: 'orange' }); + }); + + test('default cycleAttributeName, consumeCycleAttribute returns no value', () => { + const editor = new Editor(null!); + editor.consumeCycleAttribute = () => { + return undefined; // simulate for test + }; + + const cell = new Cell(); + cell.getStyle().fillColor = 'yellow'; + const originalStyle = { ...cell.getStyle() }; + editor.cycleAttribute(cell); + expect(cell.getStyle()).toEqual(originalStyle); + }); +}); diff --git a/packages/core/__tests__/serialization/serialization.xml.test.ts b/packages/core/__tests__/serialization/serialization.xml.test.ts index 3bed4279c1..6cf641f27d 100644 --- a/packages/core/__tests__/serialization/serialization.xml.test.ts +++ b/packages/core/__tests__/serialization/serialization.xml.test.ts @@ -45,7 +45,7 @@ const newEdge = (id: string, value: string) => { const getParent = (model: GraphDataModel) => { // As done in the Graph object - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- here we know that model is not null + return model.getRoot()!.getChildAt(0); }; diff --git a/packages/core/src/editor/Editor.ts b/packages/core/src/editor/Editor.ts index cec86c8a07..527aac895a 100644 --- a/packages/core/src/editor/Editor.ts +++ b/packages/core/src/editor/Editor.ts @@ -225,7 +225,7 @@ import type { FitPlugin } from '../view/plugins/index.js'; * action in {@link action}. Alternatively, the entry in the config file's popupmenu * section can be modified to invoke a different action. * - * If you want to displey the properties dialog on a doubleclick, you can set + * If you want to display the properties dialog on a double click, you can set * {@link Editor.dblClickAction} to showProperties as follows: * * ```javascript @@ -733,17 +733,14 @@ export class Editor extends EventSource { maintainSwimlanes = false; /** - * Specifies if the children of swimlanes should - * be layed out, either vertically or horizontally - * depending on {@link horizontalFlow}. Default is false. + * Specifies if the children of swimlanes should be layed out, either vertically or horizontally depending on {@link horizontalFlow}. * @default false */ layoutSwimlanes = false; /** * Specifies the attribute values to be cycled when inserting new swimlanes. - * Default is an empty array. - * @default any[] + * @default [] */ cycleAttributeValues: any[] = []; @@ -752,24 +749,21 @@ export class Editor extends EventSource { // ===================================================================================== /** - * Index of the last consumed attribute index. If a new - * swimlane is inserted, then the {@link cycleAttributeValues} - * at this index will be used as the value for - * {@link cycleAttributeName}. Default is 0. + * Index of the last consumed attribute index. + * If a new swimlane is inserted, then the {@link cycleAttributeValues} at this index will be used as the value for {@link cycleAttributeName}. * @default 0 */ cycleAttributeIndex = 0; /** - * Name of the attribute to be assigned a {@link cycleAttributeValues} - * when inserting new swimlanes. Default is 'fillColor'. + * Name of the attribute to be assigned a {@link cycleAttributeValues} when inserting new swimlanes. * @default 'fillColor' */ - // cycleAttributeName: 'fillColor'; - cycleAttributeName = 'fillColor'; + cycleAttributeName: keyof CellStateStyle | (string & Record) = + 'fillColor'; /** - * Holds the [@link MaxWindow} created in {@link showTasks}. + * Holds the {@link MaxWindow} created in {@link showTasks}. */ tasks: any = null; @@ -1967,21 +1961,21 @@ export class Editor extends EventSource { } /** - * Swaps the styles for the given names in the graph's - * stylesheet and refreshes the graph. - * @param first - * @param second - */ - swapStyles(first: keyof CellStateStyle, second: string): void { - // @ts-ignore - const style = this.graph.getStylesheet().styles[second]; - this.graph - .getView() - // @ts-ignore - .getStylesheet() - // @ts-ignore - .putCellStyle(second, this.graph.getStylesheet().styles[first]); - this.graph.getStylesheet().putCellStyle(first, style); + * Swaps the styles for the given names in the graph's stylesheet and refreshes the graph. + * @param first the name of the first style to swap + * @param second the name of the second style to swap + */ + swapStyles(first: string, second: string): void { + // Note that this method is not used in maxGraph and its examples. It wasn't used in the mxGraph repository either. + const stylesheet = this.graph.getStylesheet(); + const styles = stylesheet.styles; // use internals here as there is no public API to get styles + const firstStyle = styles.get(first); + const secondStyle = styles.get(second); + if (!firstStyle || !secondStyle) { + return; + } + stylesheet.putCellStyle(second, firstStyle); + stylesheet.putCellStyle(first, secondStyle); this.graph.refresh(); } @@ -2478,8 +2472,9 @@ export class Editor extends EventSource { const value = this.consumeCycleAttribute(cell); if (!isNullish(value)) { - // @ts-expect-error TODO - style is no longer a string - cell.setStyle(`${cell.getStyle()};${this.cycleAttributeName}=${value}`); + const style = cell.getStyle(); + // @ts-ignore ignore "not an indexed type" error + style[this.cycleAttributeName] = value; } } } diff --git a/packages/core/src/view/style/Stylesheet.ts b/packages/core/src/view/style/Stylesheet.ts index ba38033e89..4e28b43f1d 100644 --- a/packages/core/src/view/style/Stylesheet.ts +++ b/packages/core/src/view/style/Stylesheet.ts @@ -111,7 +111,6 @@ export class Stylesheet { * Returns the default style for vertices. */ getDefaultVertexStyle(): CellStateStyle { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- the style is set in the constructor return this.styles.get('defaultVertex')!; } @@ -119,7 +118,6 @@ export class Stylesheet { * Returns the default style for edges. */ getDefaultEdgeStyle(): CellStateStyle { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- the style is set in the constructor return this.styles.get('defaultEdge')!; }