Skip to content

Commit 90ba680

Browse files
cstuncsikguillaumejacquartr00gm
authored
feat(editor): Insights dashboard (#13739)
Co-authored-by: Guillaume Jacquart <[email protected]> Co-authored-by: Raúl Gómez Morales <[email protected]>
1 parent 7379f44 commit 90ba680

30 files changed

+1872
-102
lines changed

packages/frontend/@n8n/design-system/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
"@fortawesome/fontawesome-svg-core": "^1.2.36",
5050
"@fortawesome/free-solid-svg-icons": "^5.15.4",
5151
"@fortawesome/vue-fontawesome": "^3.0.3",
52+
53+
"@tanstack/vue-table": "^8.21.2",
5254
"element-plus": "catalog:frontend",
5355
"is-emoji-supported": "^0.0.5",
5456
"markdown-it": "^13.0.2",
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import userEvent from '@testing-library/user-event';
2+
import { render, within } from '@testing-library/vue';
3+
4+
import N8nDataTableServer, { type TableHeader } from './N8nDataTableServer.vue';
5+
6+
const itemFactory = () => ({
7+
id: crypto.randomUUID() as string,
8+
firstName: crypto.randomUUID() as string,
9+
lastName: crypto.randomUUID() as string,
10+
});
11+
type Item = ReturnType<typeof itemFactory>;
12+
13+
const items: Item[] = [...Array(20).keys()].map(itemFactory);
14+
const headers: Array<TableHeader<Item>> = [
15+
{
16+
title: 'Id',
17+
key: 'id',
18+
},
19+
{
20+
title: 'First name',
21+
key: 'firstName',
22+
},
23+
{
24+
title: 'Last name',
25+
value: 'lastName',
26+
},
27+
{
28+
title: 'Full name',
29+
key: 'fullName',
30+
value(item: Item) {
31+
return `${item.firstName}|${item.lastName}`;
32+
},
33+
},
34+
{
35+
title: 'Empty Column',
36+
},
37+
];
38+
39+
describe('N8nDataTableServer', () => {
40+
it('should render a table', () => {
41+
const { container } = render(N8nDataTableServer, {
42+
//@ts-expect-error testing-library errors due to header generics
43+
props: { items, headers, itemsLength: items.length },
44+
});
45+
46+
expect(container.querySelectorAll('thead tr').length).toEqual(1);
47+
expect(container.querySelectorAll('tbody tr').length).toEqual(items.length);
48+
expect(container.querySelectorAll('tbody tr td').length).toEqual(headers.length * items.length);
49+
});
50+
51+
it('should render dynamic slots', () => {
52+
const slotName = 'item.id' as `item.${string}`;
53+
const { container } = render(N8nDataTableServer, {
54+
//@ts-expect-error testing-library errors due to header generics
55+
props: { items, headers, itemsLength: items.length },
56+
slots: {
57+
[slotName]: ({ item }: { item: Item }) => {
58+
return `🍌 ${item.id}`;
59+
},
60+
},
61+
});
62+
63+
const rows = container.querySelectorAll('tbody tr');
64+
65+
rows.forEach((tr, index) => {
66+
expect(tr.querySelector('td')?.textContent).toBe(`🍌 ${items[index].id}`);
67+
});
68+
});
69+
70+
it('should synchronize the state', async () => {
71+
const { container, rerender } = render(N8nDataTableServer, {
72+
//@ts-expect-error testing-library errors due to header generics
73+
props: { items, headers, itemsLength: items.length },
74+
});
75+
76+
expect(container.querySelector('tbody tr td')?.textContent).toBe(items[0].id);
77+
78+
await rerender({
79+
items: [{ id: '1', firstName: '1', lastName: '1' }],
80+
headers,
81+
itemsLength: 1,
82+
});
83+
expect(container.querySelector('tbody tr td')?.textContent).toBe('1');
84+
});
85+
86+
it('should emit options for sorting / pagination', async () => {
87+
const { container, emitted, getByTestId } = render(N8nDataTableServer, {
88+
//@ts-expect-error testing-library errors due to header generics
89+
props: { items, headers, itemsLength: 100 },
90+
});
91+
92+
await userEvent.click(container.querySelector('thead tr th')!);
93+
await userEvent.click(container.querySelector('thead tr th')!);
94+
await userEvent.click(within(getByTestId('pagination')).getByLabelText('page 2'));
95+
96+
expect(emitted('update:options').length).toBe(3);
97+
expect(emitted('update:options')[0]).toStrictEqual([
98+
expect.objectContaining({ sortBy: [{ id: 'id', desc: false }] }),
99+
]);
100+
expect(emitted('update:options')[1]).toStrictEqual([
101+
expect.objectContaining({ sortBy: [{ id: 'id', desc: true }] }),
102+
]);
103+
expect(emitted('update:options')[2]).toStrictEqual([expect.objectContaining({ page: 1 })]);
104+
});
105+
});

0 commit comments

Comments
 (0)