noecosystem-design 0.2.3 → 0.4.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 +2 -2
- package/apps/storybook/.storybook/preview.tsx +1 -1
- package/apps/storybook/stories/catalog/app-frame.stories.tsx +213 -1
- package/apps/storybook/stories/catalog/checkbox.stories.tsx +7 -2
- package/apps/storybook/stories/catalog/data-table.stories.tsx +226 -3
- package/apps/storybook/stories/catalog/dialog.stories.tsx +2 -2
- package/apps/storybook/stories/catalog/icon-system.stories.tsx +11 -0
- package/apps/storybook/stories/catalog/menu.stories.tsx +3 -3
- package/apps/web/styles.css +0 -1
- package/docs/design-system/brand-theme.md +8 -0
- package/docs/design-system/components.md +38 -0
- package/docs/design-system/inventory.md +1 -0
- package/docs/design-system/visual-qa.md +6 -0
- package/package.json +1 -1
- package/packages/data-ui/package.json +6 -1
- package/packages/data-ui/src/data-table.tsx +2 -99
- package/packages/data-ui/src/pagination.tsx +2 -2
- package/packages/data-ui/src/table/adapters/tanstack.test.ts +116 -0
- package/packages/data-ui/src/table/adapters/tanstack.ts +179 -0
- package/packages/data-ui/src/table/contracts.ts +133 -0
- package/packages/data-ui/src/table/data-table-visual.evidence.browser.test.tsx +157 -0
- package/packages/data-ui/src/table/data-table.browser.test.tsx +180 -0
- package/packages/data-ui/src/table/data-table.tsx +524 -0
- package/packages/design-tokens/src/tailwind.css +5 -0
- package/packages/design-tokens/src/tokens.css +10 -4
- package/packages/design-tokens/src/tokens.json +8 -4
- package/packages/design-tokens/src/tokens.mjs +8 -4
- package/packages/icons/package.json +6 -1
- package/packages/icons/src/brands/nocfo.svg +8 -0
- package/packages/icons/src/nocfo-logo.browser.test.tsx +14 -0
- package/packages/icons/src/nocfo-logo.tsx +23 -0
- package/packages/registry/catalog-index.json +2 -2
- package/packages/registry/manifest.json +60 -18
- package/packages/registry/r/activity-feed.json +1 -1
- package/packages/registry/r/agent-composer.json +7 -1
- package/packages/registry/r/agent-message.json +1 -1
- package/packages/registry/r/agent-plan.json +1 -1
- package/packages/registry/r/agent-workspace.json +1 -1
- package/packages/registry/r/app-frame.json +9 -2
- package/packages/registry/r/approval-request.json +7 -1
- package/packages/registry/r/calendar.json +6 -5
- package/packages/registry/r/chart-frame.json +1 -1
- package/packages/registry/r/chart.json +1 -1
- package/packages/registry/r/dashboard-01.json +22 -2
- package/packages/registry/r/dashboard-02.json +22 -2
- package/packages/registry/r/dashboard-shell.json +1 -1
- package/packages/registry/r/data-table.json +22 -2
- package/packages/registry/r/date-picker.json +6 -5
- package/packages/registry/r/filter-bar.json +1 -1
- package/packages/registry/r/icon-system.json +18 -5
- package/packages/registry/r/inline-cta.json +6 -6
- package/packages/registry/r/metric-card.json +1 -1
- package/packages/registry/r/pagination.json +7 -5
- package/packages/registry/r/run-inspector.json +1 -1
- package/packages/registry/r/sidebar-navigation.json +5 -2
- package/packages/registry/r/tool-call.json +1 -1
- package/packages/registry/r/workflow-canvas.json +1 -1
- package/packages/registry/r/workflow-node.json +1 -1
- package/packages/registry/registry.json +12 -3
- package/packages/registry/search-index.json +23 -8
- package/packages/ui/package.json +5 -1
- package/packages/ui/src/app-frame.browser.test.tsx +6 -0
- package/packages/ui/src/app-frame.tsx +9 -2
- package/packages/ui/src/badge.tsx +4 -4
- package/packages/ui/src/calendar.tsx +1 -1
- package/packages/ui/src/checkbox.tsx +1 -1
- package/packages/ui/src/code.tsx +3 -1
- package/packages/ui/src/field.tsx +1 -1
- package/packages/ui/src/file-uploader.tsx +1 -1
- package/packages/ui/src/progress-steps.tsx +1 -1
- package/packages/ui/src/sidebar-navigation.tsx +11 -1
- package/scripts/generate-manifest.mjs +71 -0
- package/vitest.config.ts +5 -0
|
@@ -133,11 +133,20 @@
|
|
|
133
133
|
"target": "packages/data-ui/src/data-table.tsx"
|
|
134
134
|
}
|
|
135
135
|
],
|
|
136
|
-
"dependencies": ["@noe/ui"],
|
|
136
|
+
"dependencies": ["@noe/ui", "@tanstack/react-table"],
|
|
137
137
|
"peerDependencies": ["react"],
|
|
138
138
|
"manifestId": "data-table",
|
|
139
|
-
"capabilityAliases": ["data table", "
|
|
140
|
-
"searchTerms": [
|
|
139
|
+
"capabilityAliases": ["data table", "sortable table", "server-side table", "grid"],
|
|
140
|
+
"searchTerms": [
|
|
141
|
+
"data-table",
|
|
142
|
+
"table",
|
|
143
|
+
"sorting",
|
|
144
|
+
"filtering",
|
|
145
|
+
"pagination",
|
|
146
|
+
"selection",
|
|
147
|
+
"server-side",
|
|
148
|
+
"aria-sort"
|
|
149
|
+
]
|
|
141
150
|
},
|
|
142
151
|
{
|
|
143
152
|
"name": "metric-card",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-08-
|
|
2
|
+
"generatedAt": "2026-08-30T12:09:40.536Z",
|
|
3
3
|
"items": [
|
|
4
4
|
{
|
|
5
5
|
"id": "bidi-text",
|
|
@@ -381,15 +381,30 @@
|
|
|
381
381
|
"id": "data-table",
|
|
382
382
|
"package": "@noe/data-ui",
|
|
383
383
|
"title": "Data Table",
|
|
384
|
-
"description": "
|
|
384
|
+
"description": "Semantic data table with TanStack Table v9 as a headless engine behind the internal NOE adapter: sorting, NOE filter operators, controlled pagination, stable-id selection, loading skeletons and server mode.",
|
|
385
385
|
"category": "Data",
|
|
386
386
|
"status": "available",
|
|
387
|
-
"capabilityAliases": ["data table", "
|
|
388
|
-
"searchTerms": [
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
387
|
+
"capabilityAliases": ["data table", "sortable table", "server-side table", "grid"],
|
|
388
|
+
"searchTerms": [
|
|
389
|
+
"data-table",
|
|
390
|
+
"table",
|
|
391
|
+
"sorting",
|
|
392
|
+
"filtering",
|
|
393
|
+
"pagination",
|
|
394
|
+
"selection",
|
|
395
|
+
"server-side",
|
|
396
|
+
"aria-sort"
|
|
397
|
+
],
|
|
398
|
+
"useWhen": [
|
|
399
|
+
"tabular product data with sorting, filtering, pagination or row selection",
|
|
400
|
+
"server-driven pages composed from toDataQuery against Django APIs"
|
|
401
|
+
],
|
|
402
|
+
"doNotUseWhen": [
|
|
403
|
+
"spreadsheet-style cell editing grids",
|
|
404
|
+
"static lists without tabular semantics"
|
|
405
|
+
],
|
|
406
|
+
"alternatives": ["DataList"],
|
|
407
|
+
"relatedPatterns": ["filter-bar", "pagination", "empty-state"],
|
|
393
408
|
"exportName": "DataTable",
|
|
394
409
|
"registryItem": "data-table",
|
|
395
410
|
"maturity": "beta"
|
package/packages/ui/package.json
CHANGED
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
".": "./src/index.ts",
|
|
25
25
|
"./bidi": "./src/bidi.tsx",
|
|
26
26
|
"./button": "./src/button.tsx",
|
|
27
|
-
"./lib/cn": "./src/lib/cn.ts"
|
|
27
|
+
"./lib/cn": "./src/lib/cn.ts",
|
|
28
|
+
"./checkbox": "./src/checkbox.tsx",
|
|
29
|
+
"./skeleton": "./src/skeleton.tsx",
|
|
30
|
+
"./empty-state": "./src/empty-state.tsx",
|
|
31
|
+
"./input": "./src/input.tsx"
|
|
28
32
|
}
|
|
29
33
|
}
|
|
@@ -27,6 +27,7 @@ test('finance app frame keeps a flat, logical RTL sidebar structure', async () =
|
|
|
27
27
|
],
|
|
28
28
|
},
|
|
29
29
|
]}
|
|
30
|
+
wrapLabels
|
|
30
31
|
/>
|
|
31
32
|
}
|
|
32
33
|
variant="finance"
|
|
@@ -43,5 +44,10 @@ test('finance app frame keeps a flat, logical RTL sidebar structure', async () =
|
|
|
43
44
|
.element(screen.getByRole('link', { name: 'داشبورد' }))
|
|
44
45
|
.toHaveAttribute('aria-current', 'page');
|
|
45
46
|
await expect.element(screen.getByTestId('dashboard-icon')).toBeVisible();
|
|
47
|
+
await expect.element(screen.getByText('داشبورد')).toHaveClass('whitespace-normal');
|
|
46
48
|
await expect.element(screen.getByRole('main')).toHaveAttribute('id', 'finance-main');
|
|
49
|
+
const layoutGrid = frame.element().querySelector(':scope > div');
|
|
50
|
+
expect(layoutGrid).not.toBeNull();
|
|
51
|
+
expect(layoutGrid?.getAttribute('data-has-inspector')).toBeNull();
|
|
52
|
+
expect(layoutGrid?.className).not.toContain('_18rem');
|
|
47
53
|
});
|
|
@@ -42,9 +42,16 @@ export function AppFrame({
|
|
|
42
42
|
className={cn(
|
|
43
43
|
'grid',
|
|
44
44
|
isFinanceShell
|
|
45
|
-
?
|
|
46
|
-
|
|
45
|
+
? cn(
|
|
46
|
+
'lg:grid-cols-[17.5rem_minmax(0,1fr)]',
|
|
47
|
+
inspector && 'xl:grid-cols-[17.5rem_minmax(0,1fr)_18rem]',
|
|
48
|
+
)
|
|
49
|
+
: cn(
|
|
50
|
+
'lg:grid-cols-[15rem_minmax(0,1fr)]',
|
|
51
|
+
inspector && 'xl:grid-cols-[15rem_minmax(0,1fr)_18rem]',
|
|
52
|
+
),
|
|
47
53
|
)}
|
|
54
|
+
data-has-inspector={inspector ? true : undefined}
|
|
48
55
|
>
|
|
49
56
|
{mobileNavigation ? (
|
|
50
57
|
<nav
|
|
@@ -8,14 +8,14 @@ export const badgeVariants = cva(
|
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
|
-
default: 'border-accent/30 bg-accent/10 text-
|
|
11
|
+
default: 'border-accent/30 bg-accent/10 text-foreground',
|
|
12
12
|
outline: 'border-border bg-transparent text-foreground',
|
|
13
13
|
secondary: 'border-transparent bg-muted text-foreground',
|
|
14
|
-
destructive: 'border-destructive/30 bg-destructive/10 text-
|
|
14
|
+
destructive: 'border-destructive/30 bg-destructive/10 text-foreground',
|
|
15
15
|
info: 'border-info/30 bg-info/10 text-info',
|
|
16
16
|
success: 'border-success/30 bg-success/10 text-success',
|
|
17
|
-
warning: 'border-warning/40 bg-warning/15 text-
|
|
18
|
-
error: 'border-destructive/30 bg-destructive/10 text-
|
|
17
|
+
warning: 'border-warning/40 bg-warning/15 text-foreground',
|
|
18
|
+
error: 'border-destructive/30 bg-destructive/10 text-foreground',
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
defaultVariants: {
|
|
@@ -11,7 +11,7 @@ export function Checkbox({ className, label, ...props }: CheckboxProps) {
|
|
|
11
11
|
className="size-4 rounded-sm border border-border bg-background focus-visible:outline-2 focus-visible:outline-focus-ring"
|
|
12
12
|
{...props}
|
|
13
13
|
/>
|
|
14
|
-
<span>{label
|
|
14
|
+
<span>{label}</span>
|
|
15
15
|
</label>
|
|
16
16
|
);
|
|
17
17
|
}
|
package/packages/ui/src/code.tsx
CHANGED
|
@@ -105,7 +105,9 @@ export function Terminal({
|
|
|
105
105
|
<span
|
|
106
106
|
className={cn(
|
|
107
107
|
'block',
|
|
108
|
-
line.stream === 'stderr'
|
|
108
|
+
line.stream === 'stderr'
|
|
109
|
+
? 'text-[var(--noe-primitive-color-coral-400)]'
|
|
110
|
+
: 'text-background/90',
|
|
109
111
|
)}
|
|
110
112
|
data-stream={line.stream ?? 'stdout'}
|
|
111
113
|
key={line.key}
|
|
@@ -97,7 +97,7 @@ export function FieldError({
|
|
|
97
97
|
}: React.ComponentProps<typeof FieldPrimitive.Error>) {
|
|
98
98
|
return (
|
|
99
99
|
<FieldPrimitive.Error
|
|
100
|
-
className={cn('text-xs leading-normal text-
|
|
100
|
+
className={cn('text-xs font-medium leading-normal text-foreground', className)}
|
|
101
101
|
data-slot="field-error"
|
|
102
102
|
{...props}
|
|
103
103
|
/>
|
|
@@ -11,7 +11,7 @@ export function FileUploader({ className, ...props }: React.ComponentProps<'div'
|
|
|
11
11
|
{...props}
|
|
12
12
|
>
|
|
13
13
|
Drag files here or{' '}
|
|
14
|
-
<button type="button" className="text-
|
|
14
|
+
<button type="button" className="font-medium text-foreground underline">
|
|
15
15
|
browse
|
|
16
16
|
</button>
|
|
17
17
|
</div>
|
|
@@ -54,7 +54,7 @@ export function ProgressSteps({
|
|
|
54
54
|
isCompleted
|
|
55
55
|
? 'border-success/20 bg-success/10 text-success'
|
|
56
56
|
: isCurrent
|
|
57
|
-
? 'border-accent bg-accent/10 text-
|
|
57
|
+
? 'border-accent bg-accent/10 text-foreground'
|
|
58
58
|
: 'border-border bg-surface text-muted-foreground',
|
|
59
59
|
)}
|
|
60
60
|
>
|
|
@@ -22,11 +22,14 @@ export interface SidebarNavigationSection {
|
|
|
22
22
|
export interface SidebarNavigationProps extends Omit<React.ComponentProps<'nav'>, 'children'> {
|
|
23
23
|
label: string;
|
|
24
24
|
sections: readonly SidebarNavigationSection[];
|
|
25
|
+
/** Allow long bilingual labels to wrap in dense finance navigation. */
|
|
26
|
+
wrapLabels?: boolean;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export function SidebarNavigation({
|
|
28
30
|
label,
|
|
29
31
|
sections,
|
|
32
|
+
wrapLabels = false,
|
|
30
33
|
className,
|
|
31
34
|
...props
|
|
32
35
|
}: SidebarNavigationProps) {
|
|
@@ -70,7 +73,14 @@ export function SidebarNavigation({
|
|
|
70
73
|
{item.icon}
|
|
71
74
|
</span>
|
|
72
75
|
) : null}
|
|
73
|
-
<span
|
|
76
|
+
<span
|
|
77
|
+
className={cn(
|
|
78
|
+
'min-w-0 flex-1',
|
|
79
|
+
wrapLabels ? 'whitespace-normal break-words' : 'truncate',
|
|
80
|
+
)}
|
|
81
|
+
>
|
|
82
|
+
{item.label}
|
|
83
|
+
</span>
|
|
74
84
|
{item.endAdornment ? (
|
|
75
85
|
<span className="ms-auto shrink-0 text-current">{item.endAdornment}</span>
|
|
76
86
|
) : null}
|
|
@@ -119,6 +119,76 @@ function toTitle(id) {
|
|
|
119
119
|
|
|
120
120
|
// Enrichment database — semantic, agent-friendly metadata per spec
|
|
121
121
|
const enrichments = {
|
|
122
|
+
'data-table': {
|
|
123
|
+
description:
|
|
124
|
+
'Semantic data table with TanStack Table v9 as a headless engine behind the internal NOE adapter: sorting, NOE filter operators, controlled pagination, stable-id selection, loading skeletons and server mode.',
|
|
125
|
+
capabilityAliases: ['data table', 'sortable table', 'server-side table', 'grid'],
|
|
126
|
+
searchTerms: [
|
|
127
|
+
'data-table',
|
|
128
|
+
'table',
|
|
129
|
+
'sorting',
|
|
130
|
+
'filtering',
|
|
131
|
+
'pagination',
|
|
132
|
+
'selection',
|
|
133
|
+
'server-side',
|
|
134
|
+
'aria-sort',
|
|
135
|
+
],
|
|
136
|
+
useWhen: [
|
|
137
|
+
'tabular product data with sorting, filtering, pagination or row selection',
|
|
138
|
+
'server-driven pages composed from toDataQuery against Django APIs',
|
|
139
|
+
],
|
|
140
|
+
doNotUseWhen: [
|
|
141
|
+
'spreadsheet-style cell editing grids',
|
|
142
|
+
'static lists without tabular semantics',
|
|
143
|
+
],
|
|
144
|
+
alternatives: ['DataList'],
|
|
145
|
+
variants: ['regular', 'compact'],
|
|
146
|
+
states: ['default', 'hover', 'loading', 'selected', 'empty', 'focus-visible'],
|
|
147
|
+
tokenContracts: [
|
|
148
|
+
'color.border',
|
|
149
|
+
'color.foreground',
|
|
150
|
+
'color.muted-foreground',
|
|
151
|
+
'surface.sunken',
|
|
152
|
+
'muted',
|
|
153
|
+
],
|
|
154
|
+
propsSummary: {
|
|
155
|
+
columns: 'DataColumn<Row>[]',
|
|
156
|
+
rows: 'Row[]',
|
|
157
|
+
caption: 'string',
|
|
158
|
+
empty: 'ReactNode',
|
|
159
|
+
sorting: 'readonly DataSort[]',
|
|
160
|
+
filters: 'readonly FilterSpec[]',
|
|
161
|
+
page: 'PageSpec',
|
|
162
|
+
selection: 'readonly RowId[]',
|
|
163
|
+
density: "'compact' | 'regular'",
|
|
164
|
+
loading: 'boolean',
|
|
165
|
+
manualSorting: 'boolean',
|
|
166
|
+
manualFiltering: 'boolean',
|
|
167
|
+
manualPagination: 'boolean',
|
|
168
|
+
totalRowCount: 'number',
|
|
169
|
+
},
|
|
170
|
+
accessibility: {
|
|
171
|
+
role: 'table',
|
|
172
|
+
keyboard: [
|
|
173
|
+
'Tab moves into the scrollable region',
|
|
174
|
+
'Enter/Space on header toggles sort',
|
|
175
|
+
'Enter/Space on checkbox toggles row selection',
|
|
176
|
+
],
|
|
177
|
+
announcements: ['aria-sort on sorted column headers', 'aria-live pagination status'],
|
|
178
|
+
focusBehavior: ['visible ring 2px', 'scroll container focusable for keyboard overflow'],
|
|
179
|
+
},
|
|
180
|
+
internationalization: {
|
|
181
|
+
messageKeys: [],
|
|
182
|
+
rtl: 'required',
|
|
183
|
+
bidiNotes: [
|
|
184
|
+
'logical start/end alignment only',
|
|
185
|
+
'technical cells render dir=ltr via TechnicalValue',
|
|
186
|
+
'pagination controls use caller-provided accessible labels',
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
relatedPatterns: ['filter-bar', 'pagination', 'empty-state'],
|
|
190
|
+
untitledMapping: null,
|
|
191
|
+
},
|
|
122
192
|
'bidi-text': {
|
|
123
193
|
description:
|
|
124
194
|
'Mixed-direction content primitives for user text, URLs, hashes, commands and technical identifiers with explicit LTR isolation.',
|
|
@@ -815,6 +885,7 @@ function defaultEnrichment(auditItem) {
|
|
|
815
885
|
const registryDeps = [];
|
|
816
886
|
const peerDeps = ['react'];
|
|
817
887
|
if (pkg === '@noe/data-ui') deps.push('@noe/ui');
|
|
888
|
+
if (auditItem.id === 'data-table') deps.push('@tanstack/react-table');
|
|
818
889
|
if (pkg === '@noe/ai-ui') deps.push('@noe/ui');
|
|
819
890
|
if (pkg === '@noe/workflow-ui') deps.push('@noe/ui');
|
|
820
891
|
if (auditItem.id === 'pagination' || auditItem.id === 'fab' || auditItem.id === 'filter-bar')
|
package/vitest.config.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { dirname } from 'node:path';
|
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
|
|
4
4
|
import react from '@vitejs/plugin-react';
|
|
5
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
5
6
|
import { playwright } from '@vitest/browser-playwright';
|
|
6
7
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
|
7
8
|
import { defineConfig } from 'vitest/config';
|
|
@@ -46,6 +47,9 @@ export default defineConfig({
|
|
|
46
47
|
},
|
|
47
48
|
},
|
|
48
49
|
{
|
|
50
|
+
// Tailwind expands `@import "tailwindcss"` so component browser tests and
|
|
51
|
+
// visual evidence render with the real NOE utility classes, matching Storybook.
|
|
52
|
+
plugins: [tailwindcss()],
|
|
49
53
|
optimizeDeps: {
|
|
50
54
|
entries: ['apps/**/*.browser.test.tsx', 'packages/**/*.browser.test.tsx'],
|
|
51
55
|
},
|
|
@@ -83,6 +87,7 @@ export default defineConfig({
|
|
|
83
87
|
provider: playwright(),
|
|
84
88
|
headless: true,
|
|
85
89
|
instances: [{ browser: 'chromium' }],
|
|
90
|
+
viewport: { width: 1280, height: 900 },
|
|
86
91
|
},
|
|
87
92
|
},
|
|
88
93
|
},
|