Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion addon/components/hyper-table-v2/index.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<div class="hypertable-container" {{will-destroy this.teardown}} ...attributes>
<div
class="hypertable-container"
{{will-destroy this.teardown}}
{{did-update this.updateAnimationReplayListeners @options}}
...attributes
>
{{#if (or (has-block "search") (has-block "contextual-actions") (has-block "table-actions") this.displayHeader)}}
<div class="hypertable__upper-header">
<div class="fx-row">
Expand Down
32 changes: 24 additions & 8 deletions addon/components/hyper-table-v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type InitialLoadAnimationConfig = {
replayOn?: Extract<HandlerEvent, 'reset-rows'>[];
};

type AnimationReplayEvent = Extract<HandlerEvent, 'reset-rows'>;

interface HyperTableV2Args {
handler: TableHandler;
features: FeatureSet;
Expand Down Expand Up @@ -80,6 +82,7 @@ export default class HyperTableV2 extends Component<HyperTableV2Args> {

private initialLoadAnimationPlayed: boolean = false;
private initialLoadAnimationTimeout?: number;
private registeredAnimationReplayEvents: Set<AnimationReplayEvent> = new Set();

declare private hypertableInstanceID: string;

Expand All @@ -96,7 +99,7 @@ export default class HyperTableV2 extends Component<HyperTableV2Args> {
});

this.hypertableInstanceID = crypto.randomUUID();
this.registerAnimationReplayListeners(args.handler);
this.updateAnimationReplayListeners();
}

get features(): FeatureSet {
Expand Down Expand Up @@ -252,22 +255,35 @@ export default class HyperTableV2 extends Component<HyperTableV2Args> {
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 {
Expand Down
69 changes: 68 additions & 1 deletion tests/integration/components/hyper-table-v2-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`<HyperTableV2 @handler={{this.handler}} @options={{this.options}} />`);
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`<HyperTableV2 @handler={{this.handler}} @options={{this.options}} />`);
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 } });
});
});
Expand Down
Loading