From b14acde1739f771930f0214f2ade71b804015266 Mon Sep 17 00:00:00 2001 From: Maximilien B Date: Fri, 4 Sep 2026 17:05:55 +0200 Subject: [PATCH 1/4] initialLoadAnimation - sync animation replay listeners when config changes --- addon/components/hyper-table-v2/index.ts | 30 +++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/addon/components/hyper-table-v2/index.ts b/addon/components/hyper-table-v2/index.ts index 96b26ed3..83cdb86f 100644 --- a/addon/components/hyper-table-v2/index.ts +++ b/addon/components/hyper-table-v2/index.ts @@ -80,6 +80,7 @@ export default class HyperTableV2 extends Component { private initialLoadAnimationPlayed: boolean = false; private initialLoadAnimationTimeout?: number; + private registeredAnimationReplayEvents: Set> = new Set(); declare private hypertableInstanceID: string; @@ -96,7 +97,7 @@ export default class HyperTableV2 extends Component { }); this.hypertableInstanceID = crypto.randomUUID(); - this.registerAnimationReplayListeners(args.handler); + this.updateAnimationReplayListeners(); } get features(): FeatureSet { @@ -140,6 +141,7 @@ export default class HyperTableV2 extends Component { } get initialLoadAnimationContext(): InitialLoadAnimationContext | null { + this.updateAnimationReplayListeners(); return this.initialLoadAnimation ? { active: this.initialLoadAnimationActive, ...this.initialLoadAnimation } : null; } @@ -252,22 +254,34 @@ export default class HyperTableV2 extends Component { this.initialLoadAnimationTimeout = undefined; } - this.unregisterAnimationReplayListeners(); + this.unregisterAllAnimationReplayListeners(); this.args.handler.teardown(); } - private registerAnimationReplayListeners(handler: TableHandler): void { - if (!this.initialLoadAnimation?.replayOn?.length) return; + private updateAnimationReplayListeners(): void { + const desiredEvents = this.initialLoadAnimation?.replayOn ?? []; + const desiredEventsSet = new Set(desiredEvents); - for (const event of this.initialLoadAnimation.replayOn) { - handler.on(event, this.onAnimationReplay); + for (const event of this.registeredAnimationReplayEvents) { + if (!desiredEventsSet.has(event)) { + this.args.handler.off(event, this.onAnimationReplay); + this.registeredAnimationReplayEvents.delete(event); + } + } + + for (const event of desiredEvents) { + if (!this.registeredAnimationReplayEvents.has(event)) { + this.args.handler.on(event, this.onAnimationReplay); + this.registeredAnimationReplayEvents.add(event); + } } } - private unregisterAnimationReplayListeners(): void { - for (const event of this.initialLoadAnimation?.replayOn ?? []) { + private unregisterAllAnimationReplayListeners(): void { + for (const event of this.registeredAnimationReplayEvents) { this.args.handler.off(event, this.onAnimationReplay); } + this.registeredAnimationReplayEvents.clear(); } private _resetFilters(): void { From 528a3efeffd63ca41f991f5f97b2e742fe98a01e Mon Sep 17 00:00:00 2001 From: Maximilien B Date: Fri, 4 Sep 2026 17:08:18 +0200 Subject: [PATCH 2/4] Added related test scenario --- .../components/hyper-table-v2-test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/integration/components/hyper-table-v2-test.ts b/tests/integration/components/hyper-table-v2-test.ts index 17679a72..8cc28caa 100644 --- a/tests/integration/components/hyper-table-v2-test.ts +++ b/tests/integration/components/hyper-table-v2-test.ts @@ -313,6 +313,30 @@ module('Integration | Component | hyper-table-v2', function (hooks) { assert.dom('.hypertable__cell--initial-load-sequence').doesNotExist(); }); + + test('it registers replay listener when the config is enabled dynamically and replays when resetRows is called', async function (this: TestContext, assert: Assert) { + this.options = {}; + + await render(hbs``); + assert.dom('.hypertable__cell--initial-load-sequence').doesNotExist(); + + this.set('options', { + initialLoadAnimation: { + delayMs: 0, + staggerMs: 0, + maxAnimationDurationMs: 50, + replayOn: ['reset-rows'] + } + }); + assert.dom('.hypertable__cell--initial-load-sequence').doesNotExist(); + + await this.handler.resetRows(); + await waitUntil(() => document.querySelectorAll('.hypertable__cell--initial-load-sequence').length === 12); + assert.dom('.hypertable__cell--initial-load-sequence').exists({ count: 12 }); + + await waitUntil(() => !document.querySelector('.hypertable__cell--initial-load-sequence')); + assert.dom('.hypertable__cell--initial-load-sequence').doesNotExist(); + }); }); }); From fa14233ab8c29c6239f3341dfb6be03693d6c15e Mon Sep 17 00:00:00 2001 From: Maximilien B Date: Fri, 4 Sep 2026 17:41:35 +0200 Subject: [PATCH 3/4] fixed PR comments --- addon/components/hyper-table-v2/index.hbs | 7 ++- addon/components/hyper-table-v2/index.ts | 4 +- .../components/hyper-table-v2-test.ts | 45 ++++++++++++++++++- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/addon/components/hyper-table-v2/index.hbs b/addon/components/hyper-table-v2/index.hbs index 6d9f20a9..ae92a071 100644 --- a/addon/components/hyper-table-v2/index.hbs +++ b/addon/components/hyper-table-v2/index.hbs @@ -1,4 +1,9 @@ -
+
{{#if (or (has-block "search") (has-block "contextual-actions") (has-block "table-actions") this.displayHeader)}}
diff --git a/addon/components/hyper-table-v2/index.ts b/addon/components/hyper-table-v2/index.ts index 83cdb86f..11688dd4 100644 --- a/addon/components/hyper-table-v2/index.ts +++ b/addon/components/hyper-table-v2/index.ts @@ -141,7 +141,6 @@ export default class HyperTableV2 extends Component { } get initialLoadAnimationContext(): InitialLoadAnimationContext | null { - this.updateAnimationReplayListeners(); return this.initialLoadAnimation ? { active: this.initialLoadAnimationActive, ...this.initialLoadAnimation } : null; } @@ -258,7 +257,8 @@ export default class HyperTableV2 extends Component { this.args.handler.teardown(); } - private updateAnimationReplayListeners(): void { + @action + updateAnimationReplayListeners(): void { const desiredEvents = this.initialLoadAnimation?.replayOn ?? []; const desiredEventsSet = new Set(desiredEvents); diff --git a/tests/integration/components/hyper-table-v2-test.ts b/tests/integration/components/hyper-table-v2-test.ts index 8cc28caa..63aac44b 100644 --- a/tests/integration/components/hyper-table-v2-test.ts +++ b/tests/integration/components/hyper-table-v2-test.ts @@ -337,12 +337,55 @@ module('Integration | Component | hyper-table-v2', function (hooks) { await waitUntil(() => !document.querySelector('.hypertable__cell--initial-load-sequence')); assert.dom('.hypertable__cell--initial-load-sequence').doesNotExist(); }); + + test('it registers listener even when table transitions from empty to non-empty and replays on resetRows', async function (this: TestContext, assert: Assert) { + const rowsFetcher = this.rowsFetcher; + let fetchCallCount = 0; + let defaultRows: unknown[] = []; + + sinon.stub(rowsFetcher, 'fetch').callsFake(async () => { + fetchCallCount++; + if (fetchCallCount === 1) { + return { rows: [], meta: { total: 0 } }; + } else { + if (defaultRows.length === 0) { + const result = await new RowsFetcher().fetch(0, 50); + defaultRows = result.rows; + } + return { rows: defaultRows, meta: { total: defaultRows.length } }; + } + }); + + this.options = { + initialLoadAnimation: { + delayMs: 0, + staggerMs: 0, + maxAnimationDurationMs: 50, + replayOn: ['reset-rows'] + } + }; + + await render(hbs``); + await waitUntil(() => !this.handler.loadingRows); + + assert.dom('.hypertable__cell--initial-load-sequence').doesNotExist(); + + await this.handler.fetchRows(); + await waitUntil(() => !this.handler.loadingRows); + + await this.handler.resetRows(); + await waitUntil(() => document.querySelectorAll('.hypertable__cell--initial-load-sequence').length === 12); + assert.dom('.hypertable__cell--initial-load-sequence').exists({ count: 12 }); + + await waitUntil(() => !document.querySelector('.hypertable__cell--initial-load-sequence')); + assert.dom('.hypertable__cell--initial-load-sequence').doesNotExist(); + }); }); }); module('empty state', function (hooks) { hooks.beforeEach(function (this: TestContext) { - sinon.stub(this.rowsFetcher, 'fetch').callsFake((_: number, _1: number) => { + sinon.stub(this.rowsFetcher, 'fetch').callsFake(() => { return Promise.resolve({ rows: [], meta: { total: 0 } }); }); }); From 6e7863d482a31e7f254593dddd3e959cd1642689 Mon Sep 17 00:00:00 2001 From: Maximilien B Date: Fri, 4 Sep 2026 17:44:15 +0200 Subject: [PATCH 4/4] Updated typing --- addon/components/hyper-table-v2/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon/components/hyper-table-v2/index.ts b/addon/components/hyper-table-v2/index.ts index 11688dd4..469c175e 100644 --- a/addon/components/hyper-table-v2/index.ts +++ b/addon/components/hyper-table-v2/index.ts @@ -41,6 +41,8 @@ type InitialLoadAnimationConfig = { replayOn?: Extract[]; }; +type AnimationReplayEvent = Extract; + interface HyperTableV2Args { handler: TableHandler; features: FeatureSet; @@ -80,7 +82,7 @@ export default class HyperTableV2 extends Component { private initialLoadAnimationPlayed: boolean = false; private initialLoadAnimationTimeout?: number; - private registeredAnimationReplayEvents: Set> = new Set(); + private registeredAnimationReplayEvents: Set = new Set(); declare private hypertableInstanceID: string;