@fragments-sdk/ui 0.9.4 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/assets/ui.css +443 -247
  2. package/dist/blocks/components/index.d.ts +0 -2
  3. package/dist/blocks/components/index.d.ts.map +1 -1
  4. package/dist/codeblock.cjs +187 -184
  5. package/dist/codeblock.cjs.map +1 -1
  6. package/dist/codeblock.js +183 -180
  7. package/dist/codeblock.js.map +1 -1
  8. package/dist/components/Box/Box.module.scss.cjs +73 -0
  9. package/dist/components/Box/Box.module.scss.cjs.map +1 -1
  10. package/dist/components/Box/Box.module.scss.js +73 -0
  11. package/dist/components/Box/Box.module.scss.js.map +1 -1
  12. package/dist/components/ButtonGroup/ButtonGroup.module.scss.cjs +6 -0
  13. package/dist/components/ButtonGroup/ButtonGroup.module.scss.cjs.map +1 -1
  14. package/dist/components/ButtonGroup/ButtonGroup.module.scss.js +6 -0
  15. package/dist/components/ButtonGroup/ButtonGroup.module.scss.js.map +1 -1
  16. package/dist/components/CodeBlock/CodeBlock.module.scss.cjs +20 -23
  17. package/dist/components/CodeBlock/CodeBlock.module.scss.cjs.map +1 -1
  18. package/dist/components/CodeBlock/CodeBlock.module.scss.js +20 -23
  19. package/dist/components/CodeBlock/CodeBlock.module.scss.js.map +1 -1
  20. package/dist/components/CodeBlock/index.d.ts +11 -7
  21. package/dist/components/CodeBlock/index.d.ts.map +1 -1
  22. package/dist/components/Combobox/Combobox.module.scss.cjs +15 -15
  23. package/dist/components/Combobox/Combobox.module.scss.js +15 -15
  24. package/dist/components/DataTable/DataTable.module.scss.cjs +84 -0
  25. package/dist/components/DataTable/DataTable.module.scss.cjs.map +1 -0
  26. package/dist/components/DataTable/DataTable.module.scss.js +84 -0
  27. package/dist/components/DataTable/DataTable.module.scss.js.map +1 -0
  28. package/dist/components/DataTable/index.cjs +383 -0
  29. package/dist/components/DataTable/index.cjs.map +1 -0
  30. package/dist/components/DataTable/index.d.ts +78 -0
  31. package/dist/components/DataTable/index.d.ts.map +1 -0
  32. package/dist/components/DataTable/index.js +366 -0
  33. package/dist/components/DataTable/index.js.map +1 -0
  34. package/dist/components/Drawer/Drawer.module.scss.cjs +9 -0
  35. package/dist/components/Drawer/Drawer.module.scss.cjs.map +1 -1
  36. package/dist/components/Drawer/Drawer.module.scss.js +9 -0
  37. package/dist/components/Drawer/Drawer.module.scss.js.map +1 -1
  38. package/dist/components/Image/Image.module.scss.cjs +12 -0
  39. package/dist/components/Image/Image.module.scss.cjs.map +1 -1
  40. package/dist/components/Image/Image.module.scss.js +12 -0
  41. package/dist/components/Image/Image.module.scss.js.map +1 -1
  42. package/dist/components/Link/Link.module.scss.cjs +3 -0
  43. package/dist/components/Link/Link.module.scss.cjs.map +1 -1
  44. package/dist/components/Link/Link.module.scss.js +3 -0
  45. package/dist/components/Link/Link.module.scss.js.map +1 -1
  46. package/dist/components/List/List.module.scss.cjs +5 -0
  47. package/dist/components/List/List.module.scss.cjs.map +1 -1
  48. package/dist/components/List/List.module.scss.js +5 -0
  49. package/dist/components/List/List.module.scss.js.map +1 -1
  50. package/dist/components/Loading/Loading.module.scss.cjs +5 -0
  51. package/dist/components/Loading/Loading.module.scss.cjs.map +1 -1
  52. package/dist/components/Loading/Loading.module.scss.js +5 -0
  53. package/dist/components/Loading/Loading.module.scss.js.map +1 -1
  54. package/dist/components/Markdown/Markdown.module.scss.cjs +1 -1
  55. package/dist/components/Markdown/Markdown.module.scss.js +1 -1
  56. package/dist/components/Message/Message.module.scss.cjs +22 -16
  57. package/dist/components/Message/Message.module.scss.cjs.map +1 -1
  58. package/dist/components/Message/Message.module.scss.js +22 -16
  59. package/dist/components/Message/Message.module.scss.js.map +1 -1
  60. package/dist/components/Message/index.cjs +5 -3
  61. package/dist/components/Message/index.cjs.map +1 -1
  62. package/dist/components/Message/index.d.ts +5 -1
  63. package/dist/components/Message/index.d.ts.map +1 -1
  64. package/dist/components/Message/index.js +5 -3
  65. package/dist/components/Message/index.js.map +1 -1
  66. package/dist/components/Skeleton/Skeleton.module.scss.cjs +14 -0
  67. package/dist/components/Skeleton/Skeleton.module.scss.cjs.map +1 -1
  68. package/dist/components/Skeleton/Skeleton.module.scss.js +14 -0
  69. package/dist/components/Skeleton/Skeleton.module.scss.js.map +1 -1
  70. package/dist/components/Stack/Stack.module.scss.cjs +14 -0
  71. package/dist/components/Stack/Stack.module.scss.cjs.map +1 -1
  72. package/dist/components/Stack/Stack.module.scss.js +14 -0
  73. package/dist/components/Stack/Stack.module.scss.js.map +1 -1
  74. package/dist/components/Table/Table.module.scss.cjs +21 -36
  75. package/dist/components/Table/Table.module.scss.cjs.map +1 -1
  76. package/dist/components/Table/Table.module.scss.js +21 -36
  77. package/dist/components/Table/Table.module.scss.js.map +1 -1
  78. package/dist/components/Table/index.d.ts +35 -55
  79. package/dist/components/Table/index.d.ts.map +1 -1
  80. package/dist/components/Text/Text.module.scss.cjs +14 -0
  81. package/dist/components/Text/Text.module.scss.cjs.map +1 -1
  82. package/dist/components/Text/Text.module.scss.js +14 -0
  83. package/dist/components/Text/Text.module.scss.js.map +1 -1
  84. package/dist/components/Textarea/Textarea.module.scss.cjs +4 -0
  85. package/dist/components/Textarea/Textarea.module.scss.cjs.map +1 -1
  86. package/dist/components/Textarea/Textarea.module.scss.js +4 -0
  87. package/dist/components/Textarea/Textarea.module.scss.js.map +1 -1
  88. package/dist/components/ToggleGroup/ToggleGroup.module.scss.cjs +5 -0
  89. package/dist/components/ToggleGroup/ToggleGroup.module.scss.cjs.map +1 -1
  90. package/dist/components/ToggleGroup/ToggleGroup.module.scss.js +5 -0
  91. package/dist/components/ToggleGroup/ToggleGroup.module.scss.js.map +1 -1
  92. package/dist/index.cjs +119 -117
  93. package/dist/index.cjs.map +1 -1
  94. package/dist/index.d.ts +2 -1
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +3 -1
  97. package/dist/index.js.map +1 -1
  98. package/dist/table.cjs +44 -262
  99. package/dist/table.cjs.map +1 -1
  100. package/dist/table.js +47 -248
  101. package/dist/table.js.map +1 -1
  102. package/fragments.json +1 -1
  103. package/package.json +110 -118
  104. package/src/blocks/components/index.ts +0 -3
  105. package/src/components/CodeBlock/CodeBlock.module.scss +16 -34
  106. package/src/components/CodeBlock/index.tsx +351 -345
  107. package/src/components/Combobox/Combobox.module.scss +13 -9
  108. package/src/components/ConversationList/ConversationList.fragment.tsx +96 -129
  109. package/src/components/DataTable/DataTable.fragment.tsx +754 -0
  110. package/src/components/DataTable/DataTable.module.scss +300 -0
  111. package/src/components/DataTable/DataTable.test.tsx +224 -0
  112. package/src/components/DataTable/index.tsx +533 -0
  113. package/src/components/Message/Message.fragment.tsx +34 -0
  114. package/src/components/Message/Message.module.scss +11 -0
  115. package/src/components/Message/index.tsx +12 -3
  116. package/src/components/Table/Table.fragment.tsx +190 -175
  117. package/src/components/Table/Table.module.scss +15 -88
  118. package/src/components/Table/Table.test.tsx +184 -94
  119. package/src/components/Table/index.tsx +105 -374
  120. package/src/index.ts +15 -4
  121. package/src/tokens/_computed.scss +7 -6
  122. package/src/tokens/_density.scss +87 -47
  123. package/src/tokens/_variables.scss +46 -31
  124. package/dist/blocks/components/DataTable.d.ts +0 -19
  125. package/dist/blocks/components/DataTable.d.ts.map +0 -1
  126. package/src/blocks/components/DataTable.tsx +0 -124
@@ -1,129 +1,219 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { render, screen, userEvent, expectNoA11yViolations } from '../../test/utils';
3
- import { Table, createColumns } from './index';
1
+ import { describe, it, expect } from 'vitest';
2
+ import { render, screen, expectNoA11yViolations } from '../../test/utils';
3
+ import { Table } from './index';
4
4
 
5
- type Person = { id: string; name: string; age: number };
5
+ describe('Table', () => {
6
+ it('renders a table with semantic structure', () => {
7
+ render(
8
+ <Table aria-label="People">
9
+ <Table.Head>
10
+ <Table.Row>
11
+ <Table.HeaderCell>Name</Table.HeaderCell>
12
+ <Table.HeaderCell>Age</Table.HeaderCell>
13
+ </Table.Row>
14
+ </Table.Head>
15
+ <Table.Body>
16
+ <Table.Row>
17
+ <Table.Cell>Alice</Table.Cell>
18
+ <Table.Cell>30</Table.Cell>
19
+ </Table.Row>
20
+ </Table.Body>
21
+ </Table>
22
+ );
6
23
 
7
- const columns = createColumns<Person>([
8
- { key: 'name', header: 'Name' },
9
- { key: 'age', header: 'Age' },
10
- ]);
24
+ expect(screen.getByRole('table')).toBeInTheDocument();
25
+ expect(screen.getAllByRole('columnheader')).toHaveLength(2);
26
+ expect(screen.getAllByRole('row')).toHaveLength(2);
27
+ expect(screen.getByText('Alice')).toBeInTheDocument();
28
+ });
11
29
 
12
- const data: Person[] = [
13
- { id: '1', name: 'Alice', age: 30 },
14
- { id: '2', name: 'Bob', age: 25 },
15
- { id: '3', name: 'Carol', age: 35 },
16
- ];
30
+ it('adds scope="col" to header cells by default', () => {
31
+ render(
32
+ <Table aria-label="Test">
33
+ <Table.Head>
34
+ <Table.Row>
35
+ <Table.HeaderCell>Col 1</Table.HeaderCell>
36
+ <Table.HeaderCell>Col 2</Table.HeaderCell>
37
+ </Table.Row>
38
+ </Table.Head>
39
+ <Table.Body>
40
+ <Table.Row>
41
+ <Table.Cell>A</Table.Cell>
42
+ <Table.Cell>B</Table.Cell>
43
+ </Table.Row>
44
+ </Table.Body>
45
+ </Table>
46
+ );
17
47
 
18
- describe('Table', () => {
19
- it('renders a table element with column headers', () => {
20
- render(<Table columns={columns} data={data} aria-label="People" />);
21
- expect(screen.getByRole('table')).toBeInTheDocument();
22
48
  const headers = screen.getAllByRole('columnheader');
23
- expect(headers).toHaveLength(2);
24
49
  expect(headers[0]).toHaveAttribute('scope', 'col');
25
- expect(headers[0]).toHaveTextContent('Name');
26
- expect(headers[1]).toHaveTextContent('Age');
50
+ expect(headers[1]).toHaveAttribute('scope', 'col');
27
51
  });
28
52
 
29
- it('renders data rows', () => {
30
- render(<Table columns={columns} data={data} aria-label="People" />);
31
- const rows = screen.getAllByRole('row');
32
- // 1 header row + 3 data rows
33
- expect(rows).toHaveLength(4);
34
- expect(screen.getByText('Alice')).toBeInTheDocument();
35
- expect(screen.getByText('25')).toBeInTheDocument();
36
- });
53
+ it('renders visible caption', () => {
54
+ render(
55
+ <Table aria-label="Test">
56
+ <Table.Caption>My Table</Table.Caption>
57
+ <Table.Head>
58
+ <Table.Row>
59
+ <Table.HeaderCell>A</Table.HeaderCell>
60
+ </Table.Row>
61
+ </Table.Head>
62
+ <Table.Body>
63
+ <Table.Row>
64
+ <Table.Cell>1</Table.Cell>
65
+ </Table.Row>
66
+ </Table.Body>
67
+ </Table>
68
+ );
37
69
 
38
- it('renders caption when provided', () => {
39
- render(<Table columns={columns} data={data} caption="People Table" aria-label="People" />);
40
- expect(screen.getByText('People Table')).toBeInTheDocument();
70
+ expect(screen.getByText('My Table')).toBeInTheDocument();
41
71
  });
42
72
 
43
- it('shows empty state message when data is empty', () => {
44
- render(<Table columns={columns} data={[]} emptyMessage="Nothing here" aria-label="People" />);
45
- expect(screen.getByText('Nothing here')).toBeInTheDocument();
46
- expect(screen.queryByRole('table')).not.toBeInTheDocument();
47
- });
73
+ it('renders visually hidden caption', () => {
74
+ render(
75
+ <Table aria-label="Test">
76
+ <Table.Caption hidden>Hidden Caption</Table.Caption>
77
+ <Table.Head>
78
+ <Table.Row>
79
+ <Table.HeaderCell>A</Table.HeaderCell>
80
+ </Table.Row>
81
+ </Table.Head>
82
+ <Table.Body>
83
+ <Table.Row>
84
+ <Table.Cell>1</Table.Cell>
85
+ </Table.Row>
86
+ </Table.Body>
87
+ </Table>
88
+ );
48
89
 
49
- it('defaults to "No data available" empty message', () => {
50
- render(<Table columns={columns} data={[]} aria-label="People" />);
51
- expect(screen.getByText('No data available')).toBeInTheDocument();
90
+ const caption = screen.getByText('Hidden Caption');
91
+ expect(caption).toBeInTheDocument();
92
+ expect(caption.className).toContain('captionHidden');
52
93
  });
53
94
 
54
- it('supports sortable columns with aria-sort', async () => {
55
- const user = userEvent.setup();
56
- render(<Table columns={columns} data={data} sortable aria-label="People" />);
57
- const headers = screen.getAllByRole('columnheader');
58
- // Initially aria-sort="none" for sortable columns
59
- expect(headers[0]).toHaveAttribute('aria-sort', 'none');
60
-
61
- // Click the sort button inside the first header
62
- const sortButton = headers[0].querySelector('button')!;
63
- await user.click(sortButton);
64
- expect(headers[0]).toHaveAttribute('aria-sort', 'ascending');
95
+ it('applies striped class', () => {
96
+ const { container } = render(
97
+ <Table striped aria-label="Test">
98
+ <Table.Body>
99
+ <Table.Row><Table.Cell>A</Table.Cell></Table.Row>
100
+ <Table.Row><Table.Cell>B</Table.Cell></Table.Row>
101
+ </Table.Body>
102
+ </Table>
103
+ );
65
104
 
66
- await user.click(sortButton);
67
- expect(headers[0]).toHaveAttribute('aria-sort', 'descending');
105
+ expect(container.querySelector('.striped')).toBeInTheDocument();
68
106
  });
69
107
 
70
- it('calls onRowClick when a row is clicked', async () => {
71
- const user = userEvent.setup();
72
- const handleClick = vi.fn();
73
- render(<Table columns={columns} data={data} onRowClick={handleClick} aria-label="People" />);
74
- const rows = screen.getAllByRole('row');
75
- // rows[0] is header, rows[1] is first data row
76
- await user.click(rows[1]);
77
- expect(handleClick).toHaveBeenCalledWith(data[0]);
108
+ it('applies bordered class', () => {
109
+ const { container } = render(
110
+ <Table bordered aria-label="Test">
111
+ <Table.Body>
112
+ <Table.Row><Table.Cell>A</Table.Cell></Table.Row>
113
+ </Table.Body>
114
+ </Table>
115
+ );
116
+
117
+ expect(container.querySelector('.bordered')).toBeInTheDocument();
78
118
  });
79
119
 
80
- it('applies striped class when striped prop is true', () => {
81
- const { container } = render(<Table columns={columns} data={data} striped aria-label="People" />);
82
- expect(container.querySelector('.striped')).toBeInTheDocument();
120
+ it('applies sm size class', () => {
121
+ const { container } = render(
122
+ <Table size="sm" aria-label="Test">
123
+ <Table.Body>
124
+ <Table.Row><Table.Cell>A</Table.Cell></Table.Row>
125
+ </Table.Body>
126
+ </Table>
127
+ );
128
+
129
+ expect(container.querySelector('.sm')).toBeInTheDocument();
83
130
  });
84
131
 
85
- it('createColumns helper generates proper column defs', () => {
86
- const cols = createColumns<Person>([
87
- { key: 'name', header: 'Full Name', width: 200 },
88
- { key: 'age', header: 'Years', cell: (row) => `${row.age} years` },
89
- ]);
90
- expect(cols).toHaveLength(2);
91
- expect(cols[0].id).toBe('name');
92
- expect(cols[0].header).toBe('Full Name');
93
- expect(cols[0].size).toBe(200);
94
- expect(cols[1].id).toBe('age');
132
+ it('applies selected state on Row', () => {
133
+ render(
134
+ <Table aria-label="Test">
135
+ <Table.Body>
136
+ <Table.Row selected><Table.Cell>Selected row</Table.Cell></Table.Row>
137
+ </Table.Body>
138
+ </Table>
139
+ );
140
+
141
+ const row = screen.getByRole('row');
142
+ expect(row).toHaveAttribute('data-selected');
143
+ expect(row.className).toContain('selected');
95
144
  });
96
145
 
97
- it('supports row selection', () => {
146
+ it('forwards HTML attributes to sub-components', () => {
98
147
  render(
99
- <Table
100
- columns={columns}
101
- data={data}
102
- selectable
103
- rowSelection={{ '1': true }}
104
- getRowId={(row) => row.id}
105
- aria-label="People"
106
- />
148
+ <Table aria-label="Test" data-testid="root">
149
+ <Table.Head data-testid="head">
150
+ <Table.Row data-testid="header-row">
151
+ <Table.HeaderCell data-testid="header-cell">H</Table.HeaderCell>
152
+ </Table.Row>
153
+ </Table.Head>
154
+ <Table.Body data-testid="body">
155
+ <Table.Row data-testid="body-row">
156
+ <Table.Cell data-testid="cell">C</Table.Cell>
157
+ </Table.Row>
158
+ </Table.Body>
159
+ <Table.Footer data-testid="footer">
160
+ <Table.Row data-testid="footer-row">
161
+ <Table.Cell data-testid="footer-cell">F</Table.Cell>
162
+ </Table.Row>
163
+ </Table.Footer>
164
+ </Table>
107
165
  );
108
- const rows = screen.getAllByRole('row');
109
- // First data row (id='1') should have data-selected
110
- expect(rows[1]).toHaveAttribute('data-selected');
166
+
167
+ expect(screen.getByTestId('root')).toBeInTheDocument();
168
+ expect(screen.getByTestId('head')).toBeInTheDocument();
169
+ expect(screen.getByTestId('body')).toBeInTheDocument();
170
+ expect(screen.getByTestId('footer')).toBeInTheDocument();
171
+ expect(screen.getByTestId('header-row')).toBeInTheDocument();
172
+ expect(screen.getByTestId('body-row')).toBeInTheDocument();
173
+ expect(screen.getByTestId('footer-row')).toBeInTheDocument();
174
+ expect(screen.getByTestId('header-cell')).toBeInTheDocument();
175
+ expect(screen.getByTestId('cell')).toBeInTheDocument();
176
+ expect(screen.getByTestId('footer-cell')).toBeInTheDocument();
111
177
  });
112
178
 
113
- it('supports keyboard activation of clickable rows', async () => {
114
- const user = userEvent.setup();
115
- const handleClick = vi.fn();
116
- render(<Table columns={columns} data={data} onRowClick={handleClick} aria-label="People" />);
117
- const rows = screen.getAllByRole('row');
118
- rows[1].focus();
119
- await user.keyboard('{Enter}');
120
- expect(handleClick).toHaveBeenCalledWith(data[0]);
179
+ it('renders tfoot element', () => {
180
+ const { container } = render(
181
+ <Table aria-label="Test">
182
+ <Table.Body>
183
+ <Table.Row><Table.Cell>A</Table.Cell></Table.Row>
184
+ </Table.Body>
185
+ <Table.Footer>
186
+ <Table.Row><Table.Cell>Total</Table.Cell></Table.Row>
187
+ </Table.Footer>
188
+ </Table>
189
+ );
190
+
191
+ expect(container.querySelector('tfoot')).toBeInTheDocument();
121
192
  });
122
193
 
123
194
  it('has no accessibility violations', async () => {
124
195
  const { container } = render(
125
- <Table columns={columns} data={data} caption="People Table" aria-label="People" />
196
+ <Table aria-label="People">
197
+ <Table.Caption>Team Members</Table.Caption>
198
+ <Table.Head>
199
+ <Table.Row>
200
+ <Table.HeaderCell>Name</Table.HeaderCell>
201
+ <Table.HeaderCell>Role</Table.HeaderCell>
202
+ </Table.Row>
203
+ </Table.Head>
204
+ <Table.Body>
205
+ <Table.Row>
206
+ <Table.Cell>Alice</Table.Cell>
207
+ <Table.Cell>Engineer</Table.Cell>
208
+ </Table.Row>
209
+ <Table.Row>
210
+ <Table.Cell>Bob</Table.Cell>
211
+ <Table.Cell>Designer</Table.Cell>
212
+ </Table.Row>
213
+ </Table.Body>
214
+ </Table>
126
215
  );
216
+
127
217
  await expectNoA11yViolations(container);
128
218
  });
129
219
  });