@object-ui/components 0.5.0 → 3.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/.turbo/turbo-build.log +12 -25
- package/CHANGELOG.md +32 -0
- package/dist/index.css +1 -1
- package/dist/index.js +23987 -22576
- package/dist/index.umd.cjs +30 -30
- package/dist/src/custom/action-param-dialog.d.ts +21 -0
- package/dist/src/custom/index.d.ts +4 -0
- package/dist/src/custom/navigation-overlay.d.ts +50 -0
- package/dist/src/custom/view-skeleton.d.ts +37 -0
- package/dist/src/custom/view-states.d.ts +33 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/renderers/action/action-button.d.ts +11 -0
- package/dist/src/renderers/action/action-group.d.ts +25 -0
- package/dist/src/renderers/action/action-icon.d.ts +10 -0
- package/dist/src/renderers/action/action-menu.d.ts +19 -0
- package/dist/src/renderers/action/index.d.ts +0 -0
- package/dist/src/renderers/action/resolve-icon.d.ts +6 -0
- package/package.json +20 -19
- package/src/__tests__/PageRendererRegions.test.tsx +664 -55
- package/src/__tests__/__snapshots__/snapshot-critical.test.tsx.snap +811 -0
- package/src/__tests__/__snapshots__/snapshot.test.tsx.snap +327 -0
- package/src/__tests__/accessibility.test.tsx +137 -0
- package/src/__tests__/api-consistency.test.tsx +596 -0
- package/src/__tests__/color-contrast.test.tsx +212 -0
- package/src/__tests__/compliance.test.tsx +72 -0
- package/src/__tests__/edge-cases.test.tsx +285 -0
- package/src/__tests__/navigation-overlay.test.tsx +273 -0
- package/src/__tests__/snapshot-critical.test.tsx +317 -0
- package/src/__tests__/snapshot.test.tsx +205 -0
- package/src/__tests__/view-compliance.test.tsx +153 -0
- package/src/__tests__/wcag-audit.test.tsx +493 -0
- package/src/custom/action-param-dialog.tsx +264 -0
- package/src/custom/index.ts +4 -0
- package/src/custom/navigation-overlay.tsx +296 -0
- package/src/custom/view-skeleton.tsx +243 -0
- package/src/custom/view-states.tsx +153 -0
- package/src/index.ts +1 -0
- package/src/renderers/action/action-button.tsx +147 -0
- package/src/renderers/action/action-group.tsx +270 -0
- package/src/renderers/action/action-icon.tsx +150 -0
- package/src/renderers/action/action-menu.tsx +203 -0
- package/src/renderers/action/index.ts +18 -0
- package/src/renderers/action/resolve-icon.ts +35 -0
- package/src/renderers/complex/__tests__/data-table-batch-editing.test.tsx +275 -0
- package/src/renderers/complex/__tests__/data-table-cell-renderer.test.tsx +120 -0
- package/src/renderers/complex/__tests__/data-table-editing.test.tsx +221 -0
- package/src/renderers/complex/data-table.tsx +269 -33
- package/src/renderers/complex/resizable.tsx +20 -17
- package/src/renderers/data-display/list.tsx +1 -1
- package/src/renderers/data-display/table.tsx +1 -1
- package/src/renderers/data-display/tree-view.tsx +2 -1
- package/src/renderers/form/form.tsx +33 -10
- package/src/renderers/index.ts +1 -0
- package/src/renderers/layout/aspect-ratio.tsx +1 -1
- package/src/renderers/layout/page.tsx +416 -52
- package/src/renderers/navigation/sidebar.tsx +6 -0
- package/src/renderers/placeholders.tsx +2 -2
- package/src/stories/MockedData.stories.tsx +87 -37
- package/src/stories-json/Accessibility.mdx +297 -0
- package/src/stories-json/EdgeCases.stories.tsx +160 -0
- package/src/stories-json/GettingStarted.mdx +89 -0
- package/src/stories-json/Introduction.mdx +127 -0
- package/src/stories-json/accordion.stories.tsx +1 -1
- package/src/stories-json/aggrid.stories.tsx +1 -1
- package/src/stories-json/alert.stories.tsx +1 -1
- package/src/stories-json/aspect-ratio.stories.tsx +1 -1
- package/src/stories-json/avatar.stories.tsx +1 -1
- package/src/stories-json/badge.stories.tsx +1 -1
- package/src/stories-json/breadcrumb.stories.tsx +1 -1
- package/src/stories-json/button-group.stories.tsx +1 -1
- package/src/stories-json/button.stories.tsx +1 -1
- package/src/stories-json/calendar.stories.tsx +1 -1
- package/src/stories-json/card.stories.tsx +1 -1
- package/src/stories-json/carousel.stories.tsx +1 -1
- package/src/stories-json/charts.stories.tsx +1 -1
- package/src/stories-json/chatbot.stories.tsx +1 -1
- package/src/stories-json/code-editor.stories.tsx +1 -1
- package/src/stories-json/collapsible.stories.tsx +1 -1
- package/src/stories-json/controls.stories.tsx +1 -1
- package/src/stories-json/crm-live-data.stories.tsx +154 -0
- package/src/stories-json/data-table.stories.tsx +80 -4
- package/src/stories-json/data_display_extras.stories.tsx +1 -1
- package/src/stories-json/date-picker.stories.tsx +1 -1
- package/src/stories-json/detail-view.stories.tsx +1 -1
- package/src/stories-json/dialog.stories.tsx +1 -1
- package/src/stories-json/feedback_extras.stories.tsx +1 -1
- package/src/stories-json/feedback_others.stories.tsx +1 -1
- package/src/stories-json/form-variants.stories.tsx +210 -0
- package/src/stories-json/form_advanced.stories.tsx +1 -1
- package/src/stories-json/form_extras.stories.tsx +1 -1
- package/src/stories-json/grid.stories.tsx +1 -1
- package/src/stories-json/icon.stories.tsx +1 -1
- package/src/stories-json/input.stories.tsx +1 -1
- package/src/stories-json/kanban.stories.tsx +1 -1
- package/src/stories-json/layout_extended.stories.tsx +1 -1
- package/src/stories-json/layout_flex.stories.tsx +1 -1
- package/src/stories-json/list-view.stories.tsx +1 -1
- package/src/stories-json/markdown.stories.tsx +1 -1
- package/src/stories-json/menus.stories.tsx +1 -1
- package/src/stories-json/metric-card.stories.tsx +1 -1
- package/src/stories-json/navigation-menu.stories.tsx +1 -1
- package/src/stories-json/object-aggrid-advanced.stories.tsx +389 -0
- package/src/stories-json/object-aggrid.stories.tsx +1 -1
- package/src/stories-json/object-form.stories.tsx +1 -1
- package/src/stories-json/object-gantt.stories.tsx +1 -1
- package/src/stories-json/object-grid.stories.tsx +159 -1
- package/src/stories-json/object-map.stories.tsx +1 -1
- package/src/stories-json/object-view.stories.tsx +1 -1
- package/src/stories-json/overlay_extras.stories.tsx +1 -1
- package/src/stories-json/overlay_others.stories.tsx +1 -1
- package/src/stories-json/resizable.stories.tsx +1 -1
- package/src/stories-json/select.stories.tsx +1 -1
- package/src/stories-json/separator.stories.tsx +1 -1
- package/src/stories-json/statistic.stories.tsx +1 -1
- package/src/stories-json/tabs.stories.tsx +1 -1
- package/src/stories-json/timeline.stories.tsx +1 -1
- package/src/stories-json/typography.stories.tsx +1 -1
- package/src/ui/slider.tsx +6 -2
- package/src/stories/Introduction.mdx +0 -34
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Meta } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Getting Started" />
|
|
4
|
+
|
|
5
|
+
# ObjectUI Component Library
|
|
6
|
+
|
|
7
|
+
ObjectUI is a **server-driven UI engine** that renders enterprise interfaces from JSON metadata.
|
|
8
|
+
Built on **React**, **Tailwind CSS**, and **Shadcn UI (Radix)**, it bridges the speed of
|
|
9
|
+
low-code with the design quality of hand-crafted components.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Core packages
|
|
15
|
+
pnpm add @object-ui/types @object-ui/core @object-ui/react
|
|
16
|
+
|
|
17
|
+
# UI layer
|
|
18
|
+
pnpm add @object-ui/components @object-ui/fields @object-ui/layout
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Peer dependencies — make sure these are in your project:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add react react-dom tailwindcss
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Usage
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { ObjectRenderer } from '@object-ui/react';
|
|
31
|
+
|
|
32
|
+
const schema = {
|
|
33
|
+
type: 'Card',
|
|
34
|
+
props: { title: 'Hello' },
|
|
35
|
+
children: [
|
|
36
|
+
{
|
|
37
|
+
type: 'Text',
|
|
38
|
+
props: { content: 'Rendered from JSON!' },
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default function App() {
|
|
44
|
+
return <ObjectRenderer schema={schema} />;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `ObjectRenderer` resolves each node in the schema tree against the **Component Registry**,
|
|
49
|
+
producing a fully interactive React tree.
|
|
50
|
+
|
|
51
|
+
## Accessibility
|
|
52
|
+
|
|
53
|
+
Every component in ObjectUI follows the **WAI-ARIA** authoring practices provided by
|
|
54
|
+
[Radix UI](https://www.radix-ui.com/). This means:
|
|
55
|
+
|
|
56
|
+
- Correct ARIA roles and attributes out of the box
|
|
57
|
+
- Full keyboard navigation support
|
|
58
|
+
- Focus management for overlays, dialogs, and menus
|
|
59
|
+
- Screen-reader-friendly labels and live regions
|
|
60
|
+
|
|
61
|
+
Run the Storybook accessibility addon (included) to audit any story for violations.
|
|
62
|
+
|
|
63
|
+
## Theme Configuration
|
|
64
|
+
|
|
65
|
+
ObjectUI uses Tailwind CSS for styling. Customise the design tokens through your
|
|
66
|
+
`tailwind.config` and CSS variables:
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
@layer base {
|
|
70
|
+
:root {
|
|
71
|
+
--background: 0 0% 100%;
|
|
72
|
+
--foreground: 222 84% 5%;
|
|
73
|
+
--primary: 222 47% 11%;
|
|
74
|
+
--primary-foreground: 210 40% 98%;
|
|
75
|
+
/* ...other tokens */
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
All Shadcn-based components pick up these tokens automatically — no prop drilling required.
|
|
81
|
+
|
|
82
|
+
## Explore
|
|
83
|
+
|
|
84
|
+
Use the sidebar to browse every component. Each story shows:
|
|
85
|
+
|
|
86
|
+
- **Live preview** — interact directly in the canvas
|
|
87
|
+
- **Controls** — tweak props in real time
|
|
88
|
+
- **Docs** — auto-generated API tables (when tagged with `autodocs`)
|
|
89
|
+
- **Accessibility** — axe audit panel
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Meta } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Getting Started/Introduction" />
|
|
4
|
+
|
|
5
|
+
# ObjectUI — Universal Server-Driven UI Engine
|
|
6
|
+
|
|
7
|
+
ObjectUI is a **JSON-to-UI rendering engine** for React that transforms declarative schemas
|
|
8
|
+
into fully interactive enterprise interfaces. Built on **Shadcn UI**, **Radix Primitives**,
|
|
9
|
+
and **Tailwind CSS**, it bridges the speed of low-code platforms with the design quality of
|
|
10
|
+
hand-crafted components.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Why ObjectUI?
|
|
15
|
+
|
|
16
|
+
| Benefit | Description |
|
|
17
|
+
|---|---|
|
|
18
|
+
| **Schema-Driven** | Define your UI as JSON — render it anywhere with zero custom JSX. |
|
|
19
|
+
| **Enterprise-Ready** | Dashboards, data grids, Kanbans, forms, and CRUDs out of the box. |
|
|
20
|
+
| **Accessible by Default** | Every component inherits WAI-ARIA patterns from Radix UI. |
|
|
21
|
+
| **Themeable** | Tailwind CSS design tokens + CSS variables — no prop drilling required. |
|
|
22
|
+
| **Backend Agnostic** | Works with any API or data source; no server lock-in. |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Architecture Overview
|
|
27
|
+
|
|
28
|
+
ObjectUI follows a layered architecture where each layer has a single responsibility:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
┌─────────────────────────────────────────────────────┐
|
|
32
|
+
│ Your Application │
|
|
33
|
+
├─────────────────────────────────────────────────────┤
|
|
34
|
+
│ @object-ui/react (Runtime) │
|
|
35
|
+
│ ObjectRenderer · SchemaRenderer · Hooks │
|
|
36
|
+
├──────────┬──────────────────────────┬───────────────┤
|
|
37
|
+
│ Plugins │ @object-ui/fields │ @object-ui/ │
|
|
38
|
+
│ Grid, │ Text, Number, Select, │ layout │
|
|
39
|
+
│ Kanban, │ Date, Checkbox, … │ AppShell, │
|
|
40
|
+
│ Charts, │ │ Sidebar, │
|
|
41
|
+
│ Form, … │ │ Header │
|
|
42
|
+
├──────────┴──────────────────────────┴───────────────┤
|
|
43
|
+
│ @object-ui/components (Atoms) │
|
|
44
|
+
│ Button · Card · Badge · Dialog · Tabs · … │
|
|
45
|
+
├─────────────────────────────────────────────────────┤
|
|
46
|
+
│ @object-ui/core (Engine) │
|
|
47
|
+
│ Component Registry · Schema Validation · │
|
|
48
|
+
│ Expression Evaluation · Data Binding │
|
|
49
|
+
├─────────────────────────────────────────────────────┤
|
|
50
|
+
│ @object-ui/types (Protocol) │
|
|
51
|
+
│ ComponentSchema · ActionSchema · FieldSchema │
|
|
52
|
+
│ Pure TypeScript — 0 deps │
|
|
53
|
+
└─────────────────────────────────────────────────────┘
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Layer Breakdown
|
|
57
|
+
|
|
58
|
+
1. **`@object-ui/types`** — Pure TypeScript interfaces that define the JSON protocol.
|
|
59
|
+
Zero runtime dependencies.
|
|
60
|
+
2. **`@object-ui/core`** — The rendering engine: component registry, schema validation,
|
|
61
|
+
expression evaluation (`visible: "${data.age > 18}"`), and data binding. No UI code.
|
|
62
|
+
3. **`@object-ui/components`** — Shadcn-based UI atoms (Button, Card, Badge, Dialog, etc.).
|
|
63
|
+
Pure presentation — no business logic.
|
|
64
|
+
4. **`@object-ui/fields`** — Standard field renderers for form inputs (Text, Number,
|
|
65
|
+
Select, Date). Each field implements the `FieldWidgetProps` contract.
|
|
66
|
+
5. **`@object-ui/layout`** — Page-level structure components (AppShell, Header, Sidebar).
|
|
67
|
+
Routing-aware composition.
|
|
68
|
+
6. **`@object-ui/plugin-*`** — Complex view widgets (Grid, Kanban, Charts, Map, Gantt,
|
|
69
|
+
Form, Timeline). Heavy third-party dependencies are isolated here.
|
|
70
|
+
7. **`@object-ui/react`** — The public runtime entry point that ties everything together.
|
|
71
|
+
Provides `ObjectRenderer`, hooks, and provider components.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { ObjectRenderer } from '@object-ui/react';
|
|
79
|
+
|
|
80
|
+
const schema = {
|
|
81
|
+
type: 'Card',
|
|
82
|
+
props: { title: 'Welcome' },
|
|
83
|
+
children: [
|
|
84
|
+
{ type: 'Text', props: { content: 'Rendered from JSON!' } },
|
|
85
|
+
{ type: 'Button', props: { label: 'Click me', variant: 'default' } },
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export default function App() {
|
|
90
|
+
return <ObjectRenderer schema={schema} />;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The `ObjectRenderer` walks the schema tree, resolves each node against the **Component
|
|
95
|
+
Registry**, and produces a fully interactive React component tree.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Browsing This Storybook
|
|
100
|
+
|
|
101
|
+
Use the **sidebar** on the left to explore every component. Stories are organised into:
|
|
102
|
+
|
|
103
|
+
| Section | What You'll Find |
|
|
104
|
+
|---|---|
|
|
105
|
+
| **Getting Started** | Introduction, data binding guides, and setup instructions. |
|
|
106
|
+
| **Primitives** | Base Shadcn atoms — Buttons, Cards, Badges, Inputs, Dialogs, and more. |
|
|
107
|
+
| **Fields** | Form field renderers — text, number, select, date, checkbox, etc. |
|
|
108
|
+
| **Plugins** | Complex view widgets — Data Grid, Kanban, Charts, Map, Gantt, Timeline. |
|
|
109
|
+
| **Templates** | Pre-built page layouts — Dashboards, Reports, Sidebar navigation. |
|
|
110
|
+
| **Apps** | Full application demos (e.g. CRM) composed from multiple plugins. |
|
|
111
|
+
|
|
112
|
+
Each story provides:
|
|
113
|
+
|
|
114
|
+
- **Canvas** — Live interactive preview.
|
|
115
|
+
- **Controls** — Tweak JSON props in real time via the controls panel.
|
|
116
|
+
- **Docs** — Auto-generated API reference (for stories tagged `autodocs`).
|
|
117
|
+
- **Accessibility** — axe-core audit panel to check WCAG compliance.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Documentation
|
|
122
|
+
|
|
123
|
+
Full documentation, migration guides, and API references are available at the
|
|
124
|
+
[ObjectUI docs site](https://objectui.dev).
|
|
125
|
+
|
|
126
|
+
For source code and contribution guidelines, visit the
|
|
127
|
+
[GitHub repository](https://github.com/objectstack-ai/objectui).
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Data Display/Accordion',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'padded' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Feedback/Alert',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'padded' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Data Display/Aspect Ratio',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Data Display/Avatar',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Data Display/Badge',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Navigation/Breadcrumb',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/General/Button Group',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Data Display/Carousel',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Data Display/Collapsible',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -3,7 +3,7 @@ import { SchemaRenderer } from '../SchemaRenderer';
|
|
|
3
3
|
import type { BaseSchema } from '@object-ui/types';
|
|
4
4
|
|
|
5
5
|
const meta = {
|
|
6
|
-
title: '
|
|
6
|
+
title: 'Primitives/Data Entry/Controls',
|
|
7
7
|
component: SchemaRenderer,
|
|
8
8
|
parameters: { layout: 'centered' },
|
|
9
9
|
tags: ['autodocs'],
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React, { useEffect, useState, useCallback } from 'react';
|
|
3
|
+
import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
|
|
4
|
+
import type { BaseSchema } from '@object-ui/types';
|
|
5
|
+
import { createStorybookDataSource } from '@storybook-config/datasource';
|
|
6
|
+
|
|
7
|
+
const meta: Meta = {
|
|
8
|
+
title: 'Plugins/Data Views/CRM Live Data',
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'padded',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
const dataSource = createStorybookDataSource();
|
|
17
|
+
|
|
18
|
+
// ==========================================
|
|
19
|
+
// Helper: Fetches data from MSW and renders a schema
|
|
20
|
+
// ==========================================
|
|
21
|
+
const LiveDataView = ({
|
|
22
|
+
objectName,
|
|
23
|
+
schema,
|
|
24
|
+
title
|
|
25
|
+
}: {
|
|
26
|
+
objectName: string;
|
|
27
|
+
schema: (data: any[]) => BaseSchema;
|
|
28
|
+
title: string;
|
|
29
|
+
}) => {
|
|
30
|
+
const [data, setData] = useState<any[]>([]);
|
|
31
|
+
const [loading, setLoading] = useState(true);
|
|
32
|
+
const [error, setError] = useState<string | null>(null);
|
|
33
|
+
|
|
34
|
+
const fetchData = useCallback(async () => {
|
|
35
|
+
setLoading(true);
|
|
36
|
+
setError(null);
|
|
37
|
+
try {
|
|
38
|
+
const result = await dataSource.find(objectName, { $top: 50 });
|
|
39
|
+
setData(result.data as any[]);
|
|
40
|
+
} catch (err: any) {
|
|
41
|
+
console.error(`[CRM Live] Failed to fetch ${objectName}:`, err);
|
|
42
|
+
setError(err?.message || `Failed to fetch ${objectName}`);
|
|
43
|
+
} finally {
|
|
44
|
+
setLoading(false);
|
|
45
|
+
}
|
|
46
|
+
}, [objectName]);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
fetchData();
|
|
50
|
+
}, [fetchData]);
|
|
51
|
+
|
|
52
|
+
if (loading) return <div className="p-8 text-muted-foreground">Loading {title} from MSW API...</div>;
|
|
53
|
+
if (error) return (
|
|
54
|
+
<div className="p-8 space-y-2">
|
|
55
|
+
<div className="text-destructive font-medium">API Error: {error}</div>
|
|
56
|
+
<div className="text-sm text-muted-foreground">
|
|
57
|
+
Ensure the ObjectStack MSW runtime is running. Check browser console for logs.
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<SchemaRendererProvider dataSource={dataSource}>
|
|
64
|
+
<div className="space-y-4">
|
|
65
|
+
<h2 className="text-lg font-semibold">{title} ({data.length} records)</h2>
|
|
66
|
+
<SchemaRenderer schema={schema(data)} />
|
|
67
|
+
</div>
|
|
68
|
+
</SchemaRendererProvider>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// ==========================================
|
|
73
|
+
// Contact Grid from MSW
|
|
74
|
+
// ==========================================
|
|
75
|
+
export const ContactGrid: StoryObj = {
|
|
76
|
+
name: 'Contact Grid (Live)',
|
|
77
|
+
render: () => (
|
|
78
|
+
<LiveDataView
|
|
79
|
+
objectName="contact"
|
|
80
|
+
title="Contacts"
|
|
81
|
+
schema={(data) => ({
|
|
82
|
+
type: 'object-grid',
|
|
83
|
+
objectName: 'contact',
|
|
84
|
+
columns: [
|
|
85
|
+
{ field: 'name', header: 'Name', sortable: true, filterable: true },
|
|
86
|
+
{ field: 'email', header: 'Email', sortable: true },
|
|
87
|
+
{ field: 'title', header: 'Title' },
|
|
88
|
+
{ field: 'department', header: 'Department' },
|
|
89
|
+
{ field: 'status', header: 'Status', sortable: true },
|
|
90
|
+
],
|
|
91
|
+
data,
|
|
92
|
+
pagination: true,
|
|
93
|
+
pageSize: 10,
|
|
94
|
+
className: 'w-full',
|
|
95
|
+
} as any)}
|
|
96
|
+
/>
|
|
97
|
+
),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// ==========================================
|
|
101
|
+
// Account Grid from MSW
|
|
102
|
+
// ==========================================
|
|
103
|
+
export const AccountGrid: StoryObj = {
|
|
104
|
+
name: 'Account Grid (Live)',
|
|
105
|
+
render: () => (
|
|
106
|
+
<LiveDataView
|
|
107
|
+
objectName="account"
|
|
108
|
+
title="Accounts"
|
|
109
|
+
schema={(data) => ({
|
|
110
|
+
type: 'object-grid',
|
|
111
|
+
objectName: 'account',
|
|
112
|
+
columns: [
|
|
113
|
+
{ field: 'name', header: 'Company', sortable: true, filterable: true },
|
|
114
|
+
{ field: 'industry', header: 'Industry', sortable: true },
|
|
115
|
+
{ field: 'type', header: 'Type' },
|
|
116
|
+
{ field: 'employees', header: 'Employees', type: 'number' },
|
|
117
|
+
{ field: 'phone', header: 'Phone' },
|
|
118
|
+
],
|
|
119
|
+
data,
|
|
120
|
+
pagination: true,
|
|
121
|
+
pageSize: 10,
|
|
122
|
+
className: 'w-full',
|
|
123
|
+
} as any)}
|
|
124
|
+
/>
|
|
125
|
+
),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// ==========================================
|
|
129
|
+
// Opportunity Grid from MSW
|
|
130
|
+
// ==========================================
|
|
131
|
+
export const OpportunityGrid: StoryObj = {
|
|
132
|
+
name: 'Opportunity Grid (Live)',
|
|
133
|
+
render: () => (
|
|
134
|
+
<LiveDataView
|
|
135
|
+
objectName="opportunity"
|
|
136
|
+
title="Opportunities"
|
|
137
|
+
schema={(data) => ({
|
|
138
|
+
type: 'object-grid',
|
|
139
|
+
objectName: 'opportunity',
|
|
140
|
+
columns: [
|
|
141
|
+
{ field: 'name', header: 'Opportunity', sortable: true },
|
|
142
|
+
{ field: 'amount', header: 'Amount', type: 'currency', sortable: true },
|
|
143
|
+
{ field: 'stage', header: 'Stage', sortable: true },
|
|
144
|
+
{ field: 'probability', header: 'Probability' },
|
|
145
|
+
{ field: 'close_date', header: 'Close Date', type: 'date' },
|
|
146
|
+
],
|
|
147
|
+
data,
|
|
148
|
+
pagination: true,
|
|
149
|
+
pageSize: 10,
|
|
150
|
+
className: 'w-full',
|
|
151
|
+
} as any)}
|
|
152
|
+
/>
|
|
153
|
+
),
|
|
154
|
+
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { SchemaRenderer } from '
|
|
2
|
+
import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
|
|
3
|
+
import type { BaseSchema } from '@object-ui/types';
|
|
4
|
+
import { createStorybookDataSource } from '@storybook-config/datasource';
|
|
3
5
|
|
|
4
6
|
const meta: Meta = {
|
|
5
|
-
title: '
|
|
7
|
+
title: 'Primitives/Data Display/Table',
|
|
6
8
|
component: SchemaRenderer,
|
|
7
9
|
tags: ['autodocs'],
|
|
8
10
|
argTypes: {
|
|
@@ -13,6 +15,15 @@ const meta: Meta = {
|
|
|
13
15
|
export default meta;
|
|
14
16
|
type Story = StoryObj<typeof meta>;
|
|
15
17
|
|
|
18
|
+
// Create a DataSource instance that connects to MSW
|
|
19
|
+
const dataSource = createStorybookDataSource();
|
|
20
|
+
|
|
21
|
+
const renderStory = (args: any) => (
|
|
22
|
+
<SchemaRendererProvider dataSource={dataSource}>
|
|
23
|
+
<SchemaRenderer schema={args as unknown as BaseSchema} />
|
|
24
|
+
</SchemaRendererProvider>
|
|
25
|
+
);
|
|
26
|
+
|
|
16
27
|
export const DataTable: Story = {
|
|
17
28
|
args: {
|
|
18
29
|
type: 'data-table',
|
|
@@ -29,7 +40,7 @@ export const DataTable: Story = {
|
|
|
29
40
|
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'User' }
|
|
30
41
|
]
|
|
31
42
|
},
|
|
32
|
-
render:
|
|
43
|
+
render: renderStory
|
|
33
44
|
};
|
|
34
45
|
|
|
35
46
|
export const FullFeatures: Story = {
|
|
@@ -56,5 +67,70 @@ export const FullFeatures: Story = {
|
|
|
56
67
|
method: i % 2 === 0 ? 'Credit Card' : 'PayPal'
|
|
57
68
|
}))
|
|
58
69
|
},
|
|
59
|
-
render:
|
|
70
|
+
render: renderStory
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const EditableTable: Story = {
|
|
74
|
+
args: {
|
|
75
|
+
type: 'data-table',
|
|
76
|
+
caption: 'Editable Product Inventory - Simple Cell Editing',
|
|
77
|
+
searchable: false,
|
|
78
|
+
pagination: false,
|
|
79
|
+
editable: true,
|
|
80
|
+
columns: [
|
|
81
|
+
{ header: 'SKU', accessorKey: 'sku', width: '100px', editable: false },
|
|
82
|
+
{ header: 'Product Name', accessorKey: 'name' },
|
|
83
|
+
{ header: 'Price', accessorKey: 'price' },
|
|
84
|
+
{ header: 'Stock', accessorKey: 'stock' }
|
|
85
|
+
],
|
|
86
|
+
data: [
|
|
87
|
+
{ sku: 'PROD-001', name: 'Laptop', price: '$1299.99', stock: 15 },
|
|
88
|
+
{ sku: 'PROD-002', name: 'Mouse', price: '$29.99', stock: 120 },
|
|
89
|
+
{ sku: 'PROD-003', name: 'Keyboard', price: '$79.99', stock: 45 },
|
|
90
|
+
{ sku: 'PROD-004', name: 'Monitor', price: '$399.99', stock: 22 }
|
|
91
|
+
],
|
|
92
|
+
onCellChange: (rowIndex: number, columnKey: string, newValue: any, row: any) => {
|
|
93
|
+
console.log('Cell edited:', { rowIndex, columnKey, newValue, row });
|
|
94
|
+
alert(`Updated ${columnKey} to "${newValue}" for ${row.name}`);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
render: renderStory
|
|
60
98
|
};
|
|
99
|
+
|
|
100
|
+
export const BatchEditTable: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
type: 'data-table',
|
|
103
|
+
caption: 'Batch Edit Mode - Edit Multiple Rows & Save Together (💡 Double-click cells to edit, then see save buttons appear)',
|
|
104
|
+
searchable: false,
|
|
105
|
+
pagination: false,
|
|
106
|
+
editable: true,
|
|
107
|
+
rowActions: true,
|
|
108
|
+
columns: [
|
|
109
|
+
{ header: 'ID', accessorKey: 'id', width: '60px', editable: false },
|
|
110
|
+
{ header: 'Product Name', accessorKey: 'name' },
|
|
111
|
+
{ header: 'Category', accessorKey: 'category' },
|
|
112
|
+
{ header: 'Price', accessorKey: 'price' },
|
|
113
|
+
{ header: 'Stock', accessorKey: 'stock' }
|
|
114
|
+
],
|
|
115
|
+
data: [
|
|
116
|
+
{ id: 1, name: 'Wireless Mouse', category: 'Electronics', price: '$29.99', stock: 50 },
|
|
117
|
+
{ id: 2, name: 'USB-C Cable', category: 'Accessories', price: '$12.99', stock: 100 },
|
|
118
|
+
{ id: 3, name: 'Laptop Stand', category: 'Accessories', price: '$45.99', stock: 25 },
|
|
119
|
+
{ id: 4, name: 'Webcam HD', category: 'Electronics', price: '$79.99', stock: 15 },
|
|
120
|
+
{ id: 5, name: 'Desk Lamp', category: 'Furniture', price: '$34.99', stock: 30 }
|
|
121
|
+
],
|
|
122
|
+
onRowSave: async (rowIndex: number, changes: Record<string, any>, row: any) => {
|
|
123
|
+
console.log('Saving single row:', { rowIndex, changes, row });
|
|
124
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
125
|
+
alert(`✓ Saved changes for "${row.name}":\n${JSON.stringify(changes, null, 2)}`);
|
|
126
|
+
},
|
|
127
|
+
onBatchSave: async (allChanges: Array<{ rowIndex: number; changes: Record<string, any>; row: any }>) => {
|
|
128
|
+
console.log('Batch saving all rows:', allChanges);
|
|
129
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
130
|
+
const summary = allChanges.map(c => `${c.row.name}: ${Object.keys(c.changes).length} field(s)`).join('\n');
|
|
131
|
+
alert(`✓ Batch saved ${allChanges.length} rows:\n\n${summary}`);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
render: renderStory
|
|
135
|
+
};
|
|
136
|
+
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
2
2
|
import { SchemaRenderer } from '../SchemaRenderer';
|
|
3
3
|
|
|
4
4
|
const meta: Meta = {
|
|
5
|
-
title: '
|
|
5
|
+
title: 'Primitives/Data Display/Extras',
|
|
6
6
|
component: SchemaRenderer,
|
|
7
7
|
tags: ['autodocs'],
|
|
8
8
|
argTypes: {
|