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 96b26ed3..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,6 +82,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 +99,7 @@ export default class HyperTableV2 extends Component { }); this.hypertableInstanceID = crypto.randomUUID(); - this.registerAnimationReplayListeners(args.handler); + this.updateAnimationReplayListeners(); } get features(): FeatureSet { @@ -252,22 +255,35 @@ 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; + @action + updateAnimationReplayListeners(): void { + const desiredEvents = this.initialLoadAnimation?.replayOn ?? []; + const desiredEventsSet = new Set(desiredEvents); + + 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 this.initialLoadAnimation.replayOn) { - handler.on(event, this.onAnimationReplay); + 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 { diff --git a/tests/integration/components/hyper-table-v2-test.ts b/tests/integration/components/hyper-table-v2-test.ts index 17679a72..63aac44b 100644 --- a/tests/integration/components/hyper-table-v2-test.ts +++ b/tests/integration/components/hyper-table-v2-test.ts @@ -313,12 +313,79 @@ 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(); + }); + + 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 } }); }); });