@mdxui/terminal 2.0.0
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.
- package/README.md +571 -0
- package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
- package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
- package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
- package/dist/chunk-3EFDH7PK.js +5235 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-3X5IR6WE.js +884 -0
- package/dist/chunk-4FV5ZDCE.js +5236 -0
- package/dist/chunk-4OVMSF2J.js +243 -0
- package/dist/chunk-63FEETIS.js +4048 -0
- package/dist/chunk-B43KP7XJ.js +884 -0
- package/dist/chunk-BMTJXWUV.js +655 -0
- package/dist/chunk-C3SVH4N7.js +882 -0
- package/dist/chunk-EVWR7Y47.js +874 -0
- package/dist/chunk-F6A5VWUC.js +1285 -0
- package/dist/chunk-FD7KW7GE.js +882 -0
- package/dist/chunk-GBQ6UD6I.js +655 -0
- package/dist/chunk-GMDD3M6U.js +5227 -0
- package/dist/chunk-JBHRXOXM.js +1058 -0
- package/dist/chunk-JFOO3EYO.js +1182 -0
- package/dist/chunk-JQ5H3WXL.js +1291 -0
- package/dist/chunk-JQD5NASE.js +234 -0
- package/dist/chunk-KRHJP5R7.js +592 -0
- package/dist/chunk-KWF6WVJE.js +962 -0
- package/dist/chunk-LHYQVN3H.js +1038 -0
- package/dist/chunk-M3TLQLGC.js +1032 -0
- package/dist/chunk-MVW4Q5OP.js +240 -0
- package/dist/chunk-NXCZSWLU.js +1294 -0
- package/dist/chunk-O25TNRO6.js +607 -0
- package/dist/chunk-PNECDA2I.js +884 -0
- package/dist/chunk-QIHWRLJR.js +962 -0
- package/dist/chunk-QW5YMQ7K.js +882 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RP2MVQLR.js +962 -0
- package/dist/chunk-TP6RXGXA.js +1087 -0
- package/dist/chunk-TQQSTITZ.js +655 -0
- package/dist/chunk-X24GWXQV.js +1281 -0
- package/dist/components/index.d.ts +802 -0
- package/dist/components/index.js +149 -0
- package/dist/data/index.d.ts +2554 -0
- package/dist/data/index.js +51 -0
- package/dist/forms/index.d.ts +1596 -0
- package/dist/forms/index.js +464 -0
- package/dist/index-CQRFZntR.d.ts +867 -0
- package/dist/index.d.ts +579 -0
- package/dist/index.js +786 -0
- package/dist/interactive-D0JkWosD.d.ts +217 -0
- package/dist/keyboard/index.d.ts +2 -0
- package/dist/keyboard/index.js +43 -0
- package/dist/renderers/index.d.ts +546 -0
- package/dist/renderers/index.js +2157 -0
- package/dist/storybook/index.d.ts +396 -0
- package/dist/storybook/index.js +641 -0
- package/dist/theme/index.d.ts +1339 -0
- package/dist/theme/index.js +123 -0
- package/dist/types-Bxu5PAgA.d.ts +710 -0
- package/dist/types-CIlop5Ji.d.ts +701 -0
- package/dist/types-Ca8p_p5X.d.ts +710 -0
- package/package.json +90 -0
- package/src/__tests__/components/data/card.test.ts +458 -0
- package/src/__tests__/components/data/list.test.ts +473 -0
- package/src/__tests__/components/data/metrics.test.ts +541 -0
- package/src/__tests__/components/data/table.test.ts +448 -0
- package/src/__tests__/components/input/field.test.ts +555 -0
- package/src/__tests__/components/input/form.test.ts +870 -0
- package/src/__tests__/components/input/search.test.ts +1238 -0
- package/src/__tests__/components/input/select.test.ts +658 -0
- package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
- package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
- package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
- package/src/__tests__/components/navigation/tabs.test.ts +995 -0
- package/src/__tests__/components.test.tsx +1197 -0
- package/src/__tests__/core/compiler.test.ts +986 -0
- package/src/__tests__/core/parser.test.ts +785 -0
- package/src/__tests__/core/tier-switcher.test.ts +1103 -0
- package/src/__tests__/core/types.test.ts +1398 -0
- package/src/__tests__/data/collections.test.ts +1337 -0
- package/src/__tests__/data/db.test.ts +1265 -0
- package/src/__tests__/data/reactive.test.ts +1010 -0
- package/src/__tests__/data/sync.test.ts +1614 -0
- package/src/__tests__/errors.test.ts +660 -0
- package/src/__tests__/forms/integration.test.ts +444 -0
- package/src/__tests__/integration.test.ts +905 -0
- package/src/__tests__/keyboard.test.ts +1791 -0
- package/src/__tests__/renderer.test.ts +489 -0
- package/src/__tests__/renderers/ansi-css.test.ts +948 -0
- package/src/__tests__/renderers/ansi.test.ts +1366 -0
- package/src/__tests__/renderers/ascii.test.ts +1360 -0
- package/src/__tests__/renderers/interactive.test.ts +2353 -0
- package/src/__tests__/renderers/markdown.test.ts +1483 -0
- package/src/__tests__/renderers/text.test.ts +1369 -0
- package/src/__tests__/renderers/unicode.test.ts +1307 -0
- package/src/__tests__/theme.test.ts +639 -0
- package/src/__tests__/utils/assertions.ts +685 -0
- package/src/__tests__/utils/index.ts +115 -0
- package/src/__tests__/utils/test-renderer.ts +381 -0
- package/src/__tests__/utils/utils.test.ts +560 -0
- package/src/components/containers/card.ts +56 -0
- package/src/components/containers/dialog.ts +53 -0
- package/src/components/containers/index.ts +9 -0
- package/src/components/containers/panel.ts +59 -0
- package/src/components/feedback/badge.ts +40 -0
- package/src/components/feedback/index.ts +8 -0
- package/src/components/feedback/spinner.ts +23 -0
- package/src/components/helpers.ts +81 -0
- package/src/components/index.ts +153 -0
- package/src/components/layout/breadcrumb.ts +31 -0
- package/src/components/layout/index.ts +10 -0
- package/src/components/layout/list.ts +29 -0
- package/src/components/layout/sidebar.ts +79 -0
- package/src/components/layout/table.ts +62 -0
- package/src/components/primitives/box.ts +95 -0
- package/src/components/primitives/button.ts +54 -0
- package/src/components/primitives/index.ts +11 -0
- package/src/components/primitives/input.ts +88 -0
- package/src/components/primitives/select.ts +97 -0
- package/src/components/primitives/text.ts +60 -0
- package/src/components/render.ts +155 -0
- package/src/components/templates/app.ts +43 -0
- package/src/components/templates/index.ts +8 -0
- package/src/components/templates/site.ts +54 -0
- package/src/components/types.ts +777 -0
- package/src/core/compiler.ts +718 -0
- package/src/core/parser.ts +127 -0
- package/src/core/tier-switcher.ts +607 -0
- package/src/core/types.ts +672 -0
- package/src/data/collection.ts +316 -0
- package/src/data/collections.ts +50 -0
- package/src/data/context.tsx +174 -0
- package/src/data/db.ts +127 -0
- package/src/data/hooks.ts +532 -0
- package/src/data/index.ts +138 -0
- package/src/data/reactive.ts +1225 -0
- package/src/data/saas-collections.ts +375 -0
- package/src/data/sync.ts +1213 -0
- package/src/data/types.ts +660 -0
- package/src/forms/converters.ts +512 -0
- package/src/forms/index.ts +133 -0
- package/src/forms/schemas.ts +403 -0
- package/src/forms/types.ts +476 -0
- package/src/index.ts +542 -0
- package/src/keyboard/focus.ts +748 -0
- package/src/keyboard/index.ts +96 -0
- package/src/keyboard/integration.ts +371 -0
- package/src/keyboard/manager.ts +377 -0
- package/src/keyboard/presets.ts +90 -0
- package/src/renderers/ansi-css.ts +576 -0
- package/src/renderers/ansi.ts +802 -0
- package/src/renderers/ascii.ts +680 -0
- package/src/renderers/breadcrumb.ts +480 -0
- package/src/renderers/command-palette.ts +802 -0
- package/src/renderers/components/field.ts +210 -0
- package/src/renderers/components/form.ts +327 -0
- package/src/renderers/components/index.ts +21 -0
- package/src/renderers/components/search.ts +449 -0
- package/src/renderers/components/select.ts +222 -0
- package/src/renderers/index.ts +101 -0
- package/src/renderers/interactive/component-handlers.ts +622 -0
- package/src/renderers/interactive/cursor-manager.ts +147 -0
- package/src/renderers/interactive/focus-manager.ts +279 -0
- package/src/renderers/interactive/index.ts +661 -0
- package/src/renderers/interactive/input-handler.ts +164 -0
- package/src/renderers/interactive/keyboard-handler.ts +212 -0
- package/src/renderers/interactive/mouse-handler.ts +167 -0
- package/src/renderers/interactive/state-manager.ts +109 -0
- package/src/renderers/interactive/types.ts +338 -0
- package/src/renderers/interactive-string.ts +299 -0
- package/src/renderers/interactive.ts +59 -0
- package/src/renderers/markdown.ts +950 -0
- package/src/renderers/sidebar.ts +549 -0
- package/src/renderers/tabs.ts +682 -0
- package/src/renderers/text.ts +791 -0
- package/src/renderers/unicode.ts +917 -0
- package/src/renderers/utils.ts +942 -0
- package/src/router/adapters.ts +383 -0
- package/src/router/types.ts +140 -0
- package/src/router/utils.ts +452 -0
- package/src/schemas.ts +205 -0
- package/src/storybook/index.ts +91 -0
- package/src/storybook/interactive-decorator.tsx +659 -0
- package/src/storybook/keyboard-simulator.ts +501 -0
- package/src/theme/ansi-codes.ts +80 -0
- package/src/theme/box-drawing.ts +132 -0
- package/src/theme/color-convert.ts +254 -0
- package/src/theme/color-support.ts +321 -0
- package/src/theme/index.ts +134 -0
- package/src/theme/strip-ansi.ts +50 -0
- package/src/theme/tailwind-map.ts +469 -0
- package/src/theme/text-styles.ts +206 -0
- package/src/theme/theme-system.ts +568 -0
- package/src/types.ts +103 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal Table Component Tests (RED phase)
|
|
3
|
+
*
|
|
4
|
+
* TDD RED Phase: These tests define the contract for the Table data component
|
|
5
|
+
* across all 6 render tiers (TEXT, MARKDOWN, ASCII, UNICODE, ANSI, INTERACTIVE).
|
|
6
|
+
*
|
|
7
|
+
* Table Component Features:
|
|
8
|
+
* - Column definitions with headers and keys
|
|
9
|
+
* - Row data rendering
|
|
10
|
+
* - Row selection (single/multi)
|
|
11
|
+
* - Column sorting
|
|
12
|
+
* - Row actions
|
|
13
|
+
* - Pagination
|
|
14
|
+
* - Empty state handling
|
|
15
|
+
*
|
|
16
|
+
* NOTE: These tests are expected to FAIL until implementation is complete.
|
|
17
|
+
* Run: pnpm --filter @mdxui/terminal test -- --run src/__tests__/components/data/table.test.ts
|
|
18
|
+
*/
|
|
19
|
+
import { describe, it, expect } from 'vitest'
|
|
20
|
+
|
|
21
|
+
import type { UINode, RenderContext, ThemeTokens, RenderTier } from '../../../core/types'
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Test Helpers
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
const ALL_TIERS: RenderTier[] = ['text', 'markdown', 'ascii', 'unicode', 'ansi', 'interactive']
|
|
28
|
+
|
|
29
|
+
function createTestTheme(): ThemeTokens {
|
|
30
|
+
return {
|
|
31
|
+
primary: '\x1b[34m',
|
|
32
|
+
secondary: '\x1b[36m',
|
|
33
|
+
muted: '\x1b[90m',
|
|
34
|
+
foreground: '\x1b[37m',
|
|
35
|
+
background: '\x1b[40m',
|
|
36
|
+
border: '\x1b[90m',
|
|
37
|
+
success: '\x1b[32m',
|
|
38
|
+
warning: '\x1b[33m',
|
|
39
|
+
error: '\x1b[31m',
|
|
40
|
+
info: '\x1b[34m',
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function createTestContext(tier: RenderTier, overrides?: Partial<RenderContext>): RenderContext {
|
|
45
|
+
return {
|
|
46
|
+
tier,
|
|
47
|
+
width: 80,
|
|
48
|
+
height: 24,
|
|
49
|
+
depth: 0,
|
|
50
|
+
theme: createTestTheme(),
|
|
51
|
+
interactive: tier === 'interactive',
|
|
52
|
+
...overrides,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createTableNode(props: Record<string, unknown>, data?: unknown[]): UINode {
|
|
57
|
+
return {
|
|
58
|
+
type: 'table',
|
|
59
|
+
props,
|
|
60
|
+
data,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Renderer import helper - uses dynamic import for interactive tier
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
async function renderForTier(node: UINode, tier: RenderTier, ctx?: RenderContext): Promise<string> {
|
|
69
|
+
const context = ctx ?? createTestContext(tier)
|
|
70
|
+
switch (tier) {
|
|
71
|
+
case 'text': {
|
|
72
|
+
const { renderText } = await import('../../../renderers/text')
|
|
73
|
+
return renderText(node)
|
|
74
|
+
}
|
|
75
|
+
case 'markdown': {
|
|
76
|
+
const { renderMarkdown } = await import('../../../renderers/markdown')
|
|
77
|
+
return renderMarkdown(node)
|
|
78
|
+
}
|
|
79
|
+
case 'ascii': {
|
|
80
|
+
const { renderASCII } = await import('../../../renderers/ascii')
|
|
81
|
+
return renderASCII(node, context)
|
|
82
|
+
}
|
|
83
|
+
case 'unicode': {
|
|
84
|
+
const { renderUnicode } = await import('../../../renderers/unicode')
|
|
85
|
+
return renderUnicode(node, context)
|
|
86
|
+
}
|
|
87
|
+
case 'ansi': {
|
|
88
|
+
const { renderANSI } = await import('../../../renderers/ansi')
|
|
89
|
+
return renderANSI(node)
|
|
90
|
+
}
|
|
91
|
+
case 'interactive': {
|
|
92
|
+
const { renderInteractive } = await import('../../../renderers/interactive')
|
|
93
|
+
return renderInteractive(node, context)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Basic Table Rendering Tests
|
|
100
|
+
// ============================================================================
|
|
101
|
+
|
|
102
|
+
describe('Table Component', () => {
|
|
103
|
+
describe('basic rendering across all tiers', () => {
|
|
104
|
+
const columns = [
|
|
105
|
+
{ key: 'name', header: 'Name' },
|
|
106
|
+
{ key: 'email', header: 'Email' },
|
|
107
|
+
{ key: 'role', header: 'Role' },
|
|
108
|
+
]
|
|
109
|
+
const data = [
|
|
110
|
+
{ name: 'Alice', email: 'alice@example.com', role: 'Admin' },
|
|
111
|
+
{ name: 'Bob', email: 'bob@example.com', role: 'User' },
|
|
112
|
+
{ name: 'Carol', email: 'carol@example.com', role: 'User' },
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
ALL_TIERS.forEach((tier) => {
|
|
116
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
117
|
+
it('renders column headers', async () => {
|
|
118
|
+
const node = createTableNode({ columns }, data)
|
|
119
|
+
const result = await renderForTier(node, tier)
|
|
120
|
+
|
|
121
|
+
expect(result).toContain('Name')
|
|
122
|
+
expect(result).toContain('Email')
|
|
123
|
+
expect(result).toContain('Role')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('renders row data', async () => {
|
|
127
|
+
const node = createTableNode({ columns }, data)
|
|
128
|
+
const result = await renderForTier(node, tier)
|
|
129
|
+
|
|
130
|
+
expect(result).toContain('Alice')
|
|
131
|
+
expect(result).toContain('alice@example.com')
|
|
132
|
+
expect(result).toContain('Admin')
|
|
133
|
+
expect(result).toContain('Bob')
|
|
134
|
+
expect(result).toContain('Carol')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('handles empty data array', async () => {
|
|
138
|
+
const node = createTableNode({ columns }, [])
|
|
139
|
+
const result = await renderForTier(node, tier)
|
|
140
|
+
|
|
141
|
+
expect(result).toContain('Name')
|
|
142
|
+
expect(typeof result).toBe('string')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('handles missing column values', async () => {
|
|
146
|
+
const node = createTableNode({ columns }, [{ name: 'Dave' }])
|
|
147
|
+
const result = await renderForTier(node, tier)
|
|
148
|
+
|
|
149
|
+
expect(result).toContain('Dave')
|
|
150
|
+
expect(typeof result).toBe('string')
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Column Configuration Tests
|
|
158
|
+
// ============================================================================
|
|
159
|
+
|
|
160
|
+
describe('column configuration', () => {
|
|
161
|
+
ALL_TIERS.forEach((tier) => {
|
|
162
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
163
|
+
it('respects column width', async () => {
|
|
164
|
+
const columns = [
|
|
165
|
+
{ key: 'id', header: 'ID', width: 5 },
|
|
166
|
+
{ key: 'name', header: 'Name', width: 20 },
|
|
167
|
+
]
|
|
168
|
+
const node = createTableNode({ columns }, [{ id: '1', name: 'Alice' }])
|
|
169
|
+
const result = await renderForTier(node, tier)
|
|
170
|
+
|
|
171
|
+
expect(result).toContain('ID')
|
|
172
|
+
expect(result).toContain('Name')
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('renders column with custom align', async () => {
|
|
176
|
+
const columns = [
|
|
177
|
+
{ key: 'price', header: 'Price', align: 'right' },
|
|
178
|
+
{ key: 'name', header: 'Name', align: 'left' },
|
|
179
|
+
]
|
|
180
|
+
const node = createTableNode({ columns }, [{ price: '$99.99', name: 'Widget' }])
|
|
181
|
+
const result = await renderForTier(node, tier)
|
|
182
|
+
|
|
183
|
+
expect(result).toContain('Price')
|
|
184
|
+
expect(result).toContain('$99.99')
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
// ============================================================================
|
|
191
|
+
// Row Selection Tests
|
|
192
|
+
// ============================================================================
|
|
193
|
+
|
|
194
|
+
describe('row selection', () => {
|
|
195
|
+
const columns = [
|
|
196
|
+
{ key: 'id', header: 'ID' },
|
|
197
|
+
{ key: 'name', header: 'Name' },
|
|
198
|
+
]
|
|
199
|
+
const data = [
|
|
200
|
+
{ id: '1', name: 'Alice' },
|
|
201
|
+
{ id: '2', name: 'Bob' },
|
|
202
|
+
{ id: '3', name: 'Carol' },
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
ALL_TIERS.forEach((tier) => {
|
|
206
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
207
|
+
it('renders selectable rows with selection indicator', async () => {
|
|
208
|
+
const node = createTableNode({ columns, selectable: true, selectedRows: ['1'] }, data)
|
|
209
|
+
const result = await renderForTier(node, tier)
|
|
210
|
+
|
|
211
|
+
expect(result).toContain('Alice')
|
|
212
|
+
expect(result.length).toBeGreaterThan(0)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('renders multi-select with checkbox indicators', async () => {
|
|
216
|
+
const node = createTableNode({ columns, selectable: 'multi', selectedRows: ['1', '3'] }, data)
|
|
217
|
+
const result = await renderForTier(node, tier)
|
|
218
|
+
|
|
219
|
+
expect(result).toContain('Alice')
|
|
220
|
+
expect(result).toContain('Carol')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('highlights currently selected row', async () => {
|
|
224
|
+
const node = createTableNode({ columns, selectable: true, selectedRows: ['2'] }, data)
|
|
225
|
+
const result = await renderForTier(node, tier)
|
|
226
|
+
|
|
227
|
+
expect(result).toContain('Bob')
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
// ============================================================================
|
|
234
|
+
// Sorting Tests
|
|
235
|
+
// ============================================================================
|
|
236
|
+
|
|
237
|
+
describe('column sorting', () => {
|
|
238
|
+
const columns = [
|
|
239
|
+
{ key: 'name', header: 'Name', sortable: true },
|
|
240
|
+
{ key: 'age', header: 'Age', sortable: true },
|
|
241
|
+
]
|
|
242
|
+
const data = [
|
|
243
|
+
{ name: 'Carol', age: 35 },
|
|
244
|
+
{ name: 'Alice', age: 28 },
|
|
245
|
+
{ name: 'Bob', age: 32 },
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
ALL_TIERS.forEach((tier) => {
|
|
249
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
250
|
+
it('shows sort indicator for sorted column (ascending)', async () => {
|
|
251
|
+
const node = createTableNode({ columns, sortBy: 'name', sortDirection: 'asc' }, data)
|
|
252
|
+
const result = await renderForTier(node, tier)
|
|
253
|
+
|
|
254
|
+
expect(result).toContain('Name')
|
|
255
|
+
expect(result.length).toBeGreaterThan(0)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('shows sort indicator for sorted column (descending)', async () => {
|
|
259
|
+
const node = createTableNode({ columns, sortBy: 'age', sortDirection: 'desc' }, data)
|
|
260
|
+
const result = await renderForTier(node, tier)
|
|
261
|
+
|
|
262
|
+
expect(result).toContain('Age')
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// ============================================================================
|
|
269
|
+
// Row Actions Tests
|
|
270
|
+
// ============================================================================
|
|
271
|
+
|
|
272
|
+
describe('row actions', () => {
|
|
273
|
+
const columns = [
|
|
274
|
+
{ key: 'name', header: 'Name' },
|
|
275
|
+
{ key: 'status', header: 'Status' },
|
|
276
|
+
]
|
|
277
|
+
const data = [
|
|
278
|
+
{ name: 'Task 1', status: 'Active' },
|
|
279
|
+
{ name: 'Task 2', status: 'Pending' },
|
|
280
|
+
]
|
|
281
|
+
const actions = [
|
|
282
|
+
{ key: 'edit', label: 'Edit', icon: 'pencil' },
|
|
283
|
+
{ key: 'delete', label: 'Delete', icon: 'trash' },
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
ALL_TIERS.forEach((tier) => {
|
|
287
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
288
|
+
it('renders action column', async () => {
|
|
289
|
+
const node = createTableNode({ columns, actions }, data)
|
|
290
|
+
const result = await renderForTier(node, tier)
|
|
291
|
+
|
|
292
|
+
expect(result).toContain('Task 1')
|
|
293
|
+
expect(result.length).toBeGreaterThan(0)
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('shows action labels or icons', async () => {
|
|
297
|
+
const node = createTableNode({ columns, actions, showActionLabels: true }, data)
|
|
298
|
+
const result = await renderForTier(node, tier)
|
|
299
|
+
|
|
300
|
+
expect(result).toContain('Task 1')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
// ============================================================================
|
|
307
|
+
// Tier-Specific Rendering Tests
|
|
308
|
+
// ============================================================================
|
|
309
|
+
|
|
310
|
+
describe('tier-specific rendering', () => {
|
|
311
|
+
const columns = [
|
|
312
|
+
{ key: 'name', header: 'Name' },
|
|
313
|
+
{ key: 'value', header: 'Value' },
|
|
314
|
+
]
|
|
315
|
+
const data = [
|
|
316
|
+
{ name: 'Item 1', value: '100' },
|
|
317
|
+
{ name: 'Item 2', value: '200' },
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
describe('TEXT tier', () => {
|
|
321
|
+
it('renders table without borders', async () => {
|
|
322
|
+
const node = createTableNode({ columns }, data)
|
|
323
|
+
const result = await renderForTier(node, 'text')
|
|
324
|
+
|
|
325
|
+
expect(result).toContain('Name')
|
|
326
|
+
expect(result).toContain('Item 1')
|
|
327
|
+
expect(result).not.toContain('|')
|
|
328
|
+
expect(result).not.toContain('+')
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
describe('MARKDOWN tier', () => {
|
|
333
|
+
it('renders as markdown table syntax', async () => {
|
|
334
|
+
const node = createTableNode({ columns }, data)
|
|
335
|
+
const result = await renderForTier(node, 'markdown')
|
|
336
|
+
|
|
337
|
+
expect(result).toContain('|')
|
|
338
|
+
expect(result).toContain('Name')
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('includes header separator row', async () => {
|
|
342
|
+
const node = createTableNode({ columns }, data)
|
|
343
|
+
const result = await renderForTier(node, 'markdown')
|
|
344
|
+
|
|
345
|
+
expect(result).toMatch(/[-|]+/)
|
|
346
|
+
})
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
describe('ASCII tier', () => {
|
|
350
|
+
it('renders table with ASCII box characters', async () => {
|
|
351
|
+
const node = createTableNode({ columns }, data)
|
|
352
|
+
const result = await renderForTier(node, 'ascii')
|
|
353
|
+
|
|
354
|
+
expect(result).toMatch(/[+\-|]/)
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
describe('UNICODE tier', () => {
|
|
359
|
+
it('renders table with unicode box-drawing characters', async () => {
|
|
360
|
+
const node = createTableNode({ columns, border: true }, data)
|
|
361
|
+
const result = await renderForTier(node, 'unicode')
|
|
362
|
+
|
|
363
|
+
expect(result).toMatch(/[─│┌┐└┘├┤┬┴┼]/)
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
describe('ANSI tier', () => {
|
|
368
|
+
it('renders with ANSI color codes', async () => {
|
|
369
|
+
const node = createTableNode({ columns }, data)
|
|
370
|
+
const result = await renderForTier(node, 'ansi')
|
|
371
|
+
|
|
372
|
+
expect(result).toMatch(/\x1b\[/)
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
it('applies header styling', async () => {
|
|
376
|
+
const node = createTableNode({ columns, headerStyle: { bold: true } }, data)
|
|
377
|
+
const result = await renderForTier(node, 'ansi')
|
|
378
|
+
|
|
379
|
+
expect(result).toMatch(/\x1b\[1m|\x1b\[\d+m/)
|
|
380
|
+
})
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
describe('INTERACTIVE tier', () => {
|
|
384
|
+
it('renders selectable rows with cursor', async () => {
|
|
385
|
+
const node = createTableNode({ columns, selectable: true, focusedRow: 0 }, data)
|
|
386
|
+
const result = await renderForTier(node, 'interactive')
|
|
387
|
+
|
|
388
|
+
expect(result).toContain('Item 1')
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
it('highlights focused row', async () => {
|
|
392
|
+
const node = createTableNode({ columns, focusedRow: 1 }, data)
|
|
393
|
+
const result = await renderForTier(node, 'interactive')
|
|
394
|
+
|
|
395
|
+
expect(result).toContain('Item 2')
|
|
396
|
+
})
|
|
397
|
+
})
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
// ============================================================================
|
|
401
|
+
// Edge Cases
|
|
402
|
+
// ============================================================================
|
|
403
|
+
|
|
404
|
+
describe('edge cases', () => {
|
|
405
|
+
ALL_TIERS.forEach((tier) => {
|
|
406
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
407
|
+
it('handles null values in data', async () => {
|
|
408
|
+
const columns = [{ key: 'name', header: 'Name' }]
|
|
409
|
+
const node = createTableNode({ columns }, [{ name: null }])
|
|
410
|
+
const result = await renderForTier(node, tier)
|
|
411
|
+
|
|
412
|
+
expect(typeof result).toBe('string')
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('handles very long cell content', async () => {
|
|
416
|
+
const columns = [{ key: 'content', header: 'Content', width: 20 }]
|
|
417
|
+
const node = createTableNode({ columns }, [
|
|
418
|
+
{ content: 'This is a very long string that should be truncated or wrapped' },
|
|
419
|
+
])
|
|
420
|
+
const result = await renderForTier(node, tier)
|
|
421
|
+
|
|
422
|
+
expect(result.length).toBeGreaterThan(0)
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('handles many columns', async () => {
|
|
426
|
+
const columns = Array.from({ length: 10 }, (_, i) => ({
|
|
427
|
+
key: `col${i}`,
|
|
428
|
+
header: `Column ${i}`,
|
|
429
|
+
}))
|
|
430
|
+
const node = createTableNode({ columns }, [
|
|
431
|
+
Object.fromEntries(columns.map((c) => [c.key, 'value'])),
|
|
432
|
+
])
|
|
433
|
+
const result = await renderForTier(node, tier)
|
|
434
|
+
|
|
435
|
+
expect(result).toContain('Column 0')
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
it('handles unicode in cell content', async () => {
|
|
439
|
+
const columns = [{ key: 'name', header: 'Name' }]
|
|
440
|
+
const node = createTableNode({ columns }, [{ name: 'Hello world' }])
|
|
441
|
+
const result = await renderForTier(node, tier)
|
|
442
|
+
|
|
443
|
+
expect(result).toContain('world')
|
|
444
|
+
})
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
})
|
|
448
|
+
})
|