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.
Files changed (73) hide show
  1. package/README.md +2 -2
  2. package/apps/storybook/.storybook/preview.tsx +1 -1
  3. package/apps/storybook/stories/catalog/app-frame.stories.tsx +213 -1
  4. package/apps/storybook/stories/catalog/checkbox.stories.tsx +7 -2
  5. package/apps/storybook/stories/catalog/data-table.stories.tsx +226 -3
  6. package/apps/storybook/stories/catalog/dialog.stories.tsx +2 -2
  7. package/apps/storybook/stories/catalog/icon-system.stories.tsx +11 -0
  8. package/apps/storybook/stories/catalog/menu.stories.tsx +3 -3
  9. package/apps/web/styles.css +0 -1
  10. package/docs/design-system/brand-theme.md +8 -0
  11. package/docs/design-system/components.md +38 -0
  12. package/docs/design-system/inventory.md +1 -0
  13. package/docs/design-system/visual-qa.md +6 -0
  14. package/package.json +1 -1
  15. package/packages/data-ui/package.json +6 -1
  16. package/packages/data-ui/src/data-table.tsx +2 -99
  17. package/packages/data-ui/src/pagination.tsx +2 -2
  18. package/packages/data-ui/src/table/adapters/tanstack.test.ts +116 -0
  19. package/packages/data-ui/src/table/adapters/tanstack.ts +179 -0
  20. package/packages/data-ui/src/table/contracts.ts +133 -0
  21. package/packages/data-ui/src/table/data-table-visual.evidence.browser.test.tsx +157 -0
  22. package/packages/data-ui/src/table/data-table.browser.test.tsx +180 -0
  23. package/packages/data-ui/src/table/data-table.tsx +524 -0
  24. package/packages/design-tokens/src/tailwind.css +5 -0
  25. package/packages/design-tokens/src/tokens.css +10 -4
  26. package/packages/design-tokens/src/tokens.json +8 -4
  27. package/packages/design-tokens/src/tokens.mjs +8 -4
  28. package/packages/icons/package.json +6 -1
  29. package/packages/icons/src/brands/nocfo.svg +8 -0
  30. package/packages/icons/src/nocfo-logo.browser.test.tsx +14 -0
  31. package/packages/icons/src/nocfo-logo.tsx +23 -0
  32. package/packages/registry/catalog-index.json +2 -2
  33. package/packages/registry/manifest.json +60 -18
  34. package/packages/registry/r/activity-feed.json +1 -1
  35. package/packages/registry/r/agent-composer.json +7 -1
  36. package/packages/registry/r/agent-message.json +1 -1
  37. package/packages/registry/r/agent-plan.json +1 -1
  38. package/packages/registry/r/agent-workspace.json +1 -1
  39. package/packages/registry/r/app-frame.json +9 -2
  40. package/packages/registry/r/approval-request.json +7 -1
  41. package/packages/registry/r/calendar.json +6 -5
  42. package/packages/registry/r/chart-frame.json +1 -1
  43. package/packages/registry/r/chart.json +1 -1
  44. package/packages/registry/r/dashboard-01.json +22 -2
  45. package/packages/registry/r/dashboard-02.json +22 -2
  46. package/packages/registry/r/dashboard-shell.json +1 -1
  47. package/packages/registry/r/data-table.json +22 -2
  48. package/packages/registry/r/date-picker.json +6 -5
  49. package/packages/registry/r/filter-bar.json +1 -1
  50. package/packages/registry/r/icon-system.json +18 -5
  51. package/packages/registry/r/inline-cta.json +6 -6
  52. package/packages/registry/r/metric-card.json +1 -1
  53. package/packages/registry/r/pagination.json +7 -5
  54. package/packages/registry/r/run-inspector.json +1 -1
  55. package/packages/registry/r/sidebar-navigation.json +5 -2
  56. package/packages/registry/r/tool-call.json +1 -1
  57. package/packages/registry/r/workflow-canvas.json +1 -1
  58. package/packages/registry/r/workflow-node.json +1 -1
  59. package/packages/registry/registry.json +12 -3
  60. package/packages/registry/search-index.json +23 -8
  61. package/packages/ui/package.json +5 -1
  62. package/packages/ui/src/app-frame.browser.test.tsx +6 -0
  63. package/packages/ui/src/app-frame.tsx +9 -2
  64. package/packages/ui/src/badge.tsx +4 -4
  65. package/packages/ui/src/calendar.tsx +1 -1
  66. package/packages/ui/src/checkbox.tsx +1 -1
  67. package/packages/ui/src/code.tsx +3 -1
  68. package/packages/ui/src/field.tsx +1 -1
  69. package/packages/ui/src/file-uploader.tsx +1 -1
  70. package/packages/ui/src/progress-steps.tsx +1 -1
  71. package/packages/ui/src/sidebar-navigation.tsx +11 -1
  72. package/scripts/generate-manifest.mjs +71 -0
  73. package/vitest.config.ts +5 -0
@@ -17,7 +17,7 @@
17
17
  }
18
18
  ],
19
19
  "registryDependencies": [],
20
- "dependencies": ["clsx", "tailwind-merge"],
20
+ "dependencies": ["@noe/ui", "clsx", "tailwind-merge"],
21
21
  "status": "available",
22
22
  "maturity": "beta",
23
23
  "package": "@noe/workflow-ui",
@@ -22,7 +22,7 @@
22
22
  }
23
23
  ],
24
24
  "registryDependencies": [],
25
- "dependencies": ["clsx", "tailwind-merge"],
25
+ "dependencies": ["@noe/ui", "clsx", "tailwind-merge"],
26
26
  "status": "available",
27
27
  "maturity": "beta",
28
28
  "package": "@noe/workflow-ui",
@@ -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", "data-table"],
140
- "searchTerms": ["data-table", "data table", "data"]
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-29T09:30:43.933Z",
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": "Data Table Data component. data-table provides data behavior with RTL, i18n and accessibility built-in.",
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", "data-table"],
388
- "searchTerms": ["data-table", "data table", "data"],
389
- "useWhen": ["using data table in Data"],
390
- "doNotUseWhen": ["when data table semantics do not match"],
391
- "alternatives": [],
392
- "relatedPatterns": [],
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"
@@ -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
- ? 'lg:grid-cols-[17.5rem_minmax(0,1fr)] xl:grid-cols-[17.5rem_minmax(0,1fr)_18rem]'
46
- : 'lg:grid-cols-[15rem_minmax(0,1fr)] xl:grid-cols-[15rem_minmax(0,1fr)_18rem]',
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-accent',
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-destructive',
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-warning-foreground',
18
- error: 'border-destructive/30 bg-destructive/10 text-destructive',
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: {
@@ -317,7 +317,7 @@ export function Calendar({
317
317
  isSelected
318
318
  ? 'bg-accent text-accent-foreground'
319
319
  : isToday
320
- ? 'border border-accent text-accent'
320
+ ? 'border border-accent text-foreground'
321
321
  : 'text-foreground hover:bg-muted',
322
322
  )}
323
323
  >
@@ -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 ?? 'Checkbox'}</span>
14
+ <span>{label}</span>
15
15
  </label>
16
16
  );
17
17
  }
@@ -105,7 +105,9 @@ export function Terminal({
105
105
  <span
106
106
  className={cn(
107
107
  'block',
108
- line.stream === 'stderr' ? 'text-destructive' : 'text-background/90',
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-destructive', className)}
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-accent">
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-accent'
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 className="min-w-0 flex-1 truncate">{item.label}</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
  },