diff --git a/common/changes/@visactor/vtable-gantt/fix-issue-5159-gantt-sort-scales_2026-06-26-12-00.json b/common/changes/@visactor/vtable-gantt/fix-issue-5159-gantt-sort-scales_2026-06-26-12-00.json new file mode 100644 index 000000000..f7c9a9e25 --- /dev/null +++ b/common/changes/@visactor/vtable-gantt/fix-issue-5159-gantt-sort-scales_2026-06-26-12-00.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable-gantt", + "comment": "Fix a crash in the Gantt component when `zoomScale` is configured without `scales`; `_sortScales` accessed `timelineHeader.scales.length` without a guard (GitHub #5159)", + "type": "patch" + } + ], + "packageName": "@visactor/vtable-gantt", + "email": "53095992+Jian-Zhang08@users.noreply.github.com" +} diff --git a/packages/vtable-gantt/__tests__/gantt-sort-scales.test.ts b/packages/vtable-gantt/__tests__/gantt-sort-scales.test.ts new file mode 100644 index 000000000..86258461b --- /dev/null +++ b/packages/vtable-gantt/__tests__/gantt-sort-scales.test.ts @@ -0,0 +1,31 @@ +// @ts-nocheck + +global.__VERSION__ = 'none'; + +import { Gantt } from '../src/index'; + +describe('Gantt._sortScales', () => { + // Regression test for https://github.com/VisActor/VTable/issues/5159 + // When only `zoomScale` is configured (so `timelineHeader.scales` is still + // undefined), `_sortScales` used to crash on `timelineScales.length`. + test('does not throw when timelineHeader.scales is undefined', () => { + const context = { + options: { timelineHeader: {} }, + parsedOptions: {} + }; + + expect(() => Gantt.prototype._sortScales.call(context)).not.toThrow(); + }); + + test('still sorts the configured scales', () => { + const context = { + options: { timelineHeader: { scales: [{ unit: 'day' }, { unit: 'month' }] } }, + parsedOptions: {} + }; + + Gantt.prototype._sortScales.call(context); + + expect(context.parsedOptions.sortedTimelineScales.map(scale => scale.unit)).toEqual(['month', 'day']); + expect(context.parsedOptions.reverseSortedTimelineScales.map(scale => scale.unit)).toEqual(['day', 'month']); + }); +}); diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 4084a9713..5492fd58f 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -689,7 +689,10 @@ export class Gantt extends EventTarget { _sortScales() { const { timelineHeader } = this.options; - if (timelineHeader) { + // `scales` can be undefined when only `zoomScale` is configured (the zoom + // scale manager may not have populated it yet), so guard against it instead + // of crashing on `timelineScales.length`. + if (timelineHeader?.scales) { const timelineScales = timelineHeader.scales; const sortOrder = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second']; if (timelineScales.length === 1) {