@object-ui/components 0.5.0 → 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.
Files changed (96) hide show
  1. package/.turbo/turbo-build.log +12 -25
  2. package/CHANGELOG.md +13 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.js +23366 -22221
  5. package/dist/index.umd.cjs +30 -30
  6. package/dist/src/custom/action-param-dialog.d.ts +21 -0
  7. package/dist/src/custom/index.d.ts +2 -0
  8. package/dist/src/custom/navigation-overlay.d.ts +50 -0
  9. package/dist/src/index.d.ts +1 -0
  10. package/dist/src/renderers/action/action-button.d.ts +11 -0
  11. package/dist/src/renderers/action/action-group.d.ts +25 -0
  12. package/dist/src/renderers/action/action-icon.d.ts +10 -0
  13. package/dist/src/renderers/action/action-menu.d.ts +19 -0
  14. package/dist/src/renderers/action/index.d.ts +0 -0
  15. package/dist/src/renderers/action/resolve-icon.d.ts +6 -0
  16. package/package.json +9 -8
  17. package/src/__tests__/PageRendererRegions.test.tsx +664 -55
  18. package/src/__tests__/compliance.test.tsx +72 -0
  19. package/src/__tests__/navigation-overlay.test.tsx +273 -0
  20. package/src/__tests__/view-compliance.test.tsx +153 -0
  21. package/src/custom/action-param-dialog.tsx +264 -0
  22. package/src/custom/index.ts +2 -0
  23. package/src/custom/navigation-overlay.tsx +296 -0
  24. package/src/index.ts +1 -0
  25. package/src/renderers/action/action-button.tsx +147 -0
  26. package/src/renderers/action/action-group.tsx +270 -0
  27. package/src/renderers/action/action-icon.tsx +150 -0
  28. package/src/renderers/action/action-menu.tsx +203 -0
  29. package/src/renderers/action/index.ts +18 -0
  30. package/src/renderers/action/resolve-icon.ts +35 -0
  31. package/src/renderers/complex/__tests__/data-table-batch-editing.test.tsx +275 -0
  32. package/src/renderers/complex/__tests__/data-table-cell-renderer.test.tsx +120 -0
  33. package/src/renderers/complex/__tests__/data-table-editing.test.tsx +221 -0
  34. package/src/renderers/complex/data-table.tsx +242 -21
  35. package/src/renderers/form/form.tsx +23 -4
  36. package/src/renderers/index.ts +1 -0
  37. package/src/renderers/layout/page.tsx +416 -52
  38. package/src/renderers/navigation/sidebar.tsx +6 -0
  39. package/src/renderers/placeholders.tsx +2 -2
  40. package/src/stories/Introduction.mdx +54 -27
  41. package/src/stories/MockedData.stories.tsx +87 -37
  42. package/src/stories-json/accordion.stories.tsx +1 -1
  43. package/src/stories-json/aggrid.stories.tsx +1 -1
  44. package/src/stories-json/alert.stories.tsx +1 -1
  45. package/src/stories-json/aspect-ratio.stories.tsx +1 -1
  46. package/src/stories-json/avatar.stories.tsx +1 -1
  47. package/src/stories-json/badge.stories.tsx +1 -1
  48. package/src/stories-json/breadcrumb.stories.tsx +1 -1
  49. package/src/stories-json/button-group.stories.tsx +1 -1
  50. package/src/stories-json/button.stories.tsx +1 -1
  51. package/src/stories-json/calendar.stories.tsx +1 -1
  52. package/src/stories-json/card.stories.tsx +1 -1
  53. package/src/stories-json/carousel.stories.tsx +1 -1
  54. package/src/stories-json/charts.stories.tsx +1 -1
  55. package/src/stories-json/chatbot.stories.tsx +1 -1
  56. package/src/stories-json/code-editor.stories.tsx +1 -1
  57. package/src/stories-json/collapsible.stories.tsx +1 -1
  58. package/src/stories-json/controls.stories.tsx +1 -1
  59. package/src/stories-json/crm-live-data.stories.tsx +154 -0
  60. package/src/stories-json/data-table.stories.tsx +80 -4
  61. package/src/stories-json/data_display_extras.stories.tsx +1 -1
  62. package/src/stories-json/date-picker.stories.tsx +1 -1
  63. package/src/stories-json/detail-view.stories.tsx +1 -1
  64. package/src/stories-json/dialog.stories.tsx +1 -1
  65. package/src/stories-json/feedback_extras.stories.tsx +1 -1
  66. package/src/stories-json/feedback_others.stories.tsx +1 -1
  67. package/src/stories-json/form-variants.stories.tsx +210 -0
  68. package/src/stories-json/form_advanced.stories.tsx +1 -1
  69. package/src/stories-json/form_extras.stories.tsx +1 -1
  70. package/src/stories-json/grid.stories.tsx +1 -1
  71. package/src/stories-json/icon.stories.tsx +1 -1
  72. package/src/stories-json/input.stories.tsx +1 -1
  73. package/src/stories-json/kanban.stories.tsx +1 -1
  74. package/src/stories-json/layout_extended.stories.tsx +1 -1
  75. package/src/stories-json/layout_flex.stories.tsx +1 -1
  76. package/src/stories-json/list-view.stories.tsx +1 -1
  77. package/src/stories-json/markdown.stories.tsx +1 -1
  78. package/src/stories-json/menus.stories.tsx +1 -1
  79. package/src/stories-json/metric-card.stories.tsx +1 -1
  80. package/src/stories-json/navigation-menu.stories.tsx +1 -1
  81. package/src/stories-json/object-aggrid-advanced.stories.tsx +389 -0
  82. package/src/stories-json/object-aggrid.stories.tsx +1 -1
  83. package/src/stories-json/object-form.stories.tsx +1 -1
  84. package/src/stories-json/object-gantt.stories.tsx +1 -1
  85. package/src/stories-json/object-grid.stories.tsx +159 -1
  86. package/src/stories-json/object-map.stories.tsx +1 -1
  87. package/src/stories-json/object-view.stories.tsx +1 -1
  88. package/src/stories-json/overlay_extras.stories.tsx +1 -1
  89. package/src/stories-json/overlay_others.stories.tsx +1 -1
  90. package/src/stories-json/resizable.stories.tsx +1 -1
  91. package/src/stories-json/select.stories.tsx +1 -1
  92. package/src/stories-json/separator.stories.tsx +1 -1
  93. package/src/stories-json/statistic.stories.tsx +1 -1
  94. package/src/stories-json/tabs.stories.tsx +1 -1
  95. package/src/stories-json/timeline.stories.tsx +1 -1
  96. package/src/stories-json/typography.stories.tsx +1 -1
@@ -1,71 +1,121 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
- import React, { useEffect, useState } from 'react';
2
+ import React, { useEffect, useState, useCallback } from 'react';
3
3
  import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
4
- import { PageSchema } from '@object-ui/types';
4
+ import type { BaseSchema } from '@object-ui/types';
5
+ import { createStorybookDataSource } from '@storybook-config/datasource';
5
6
 
6
- // Define a schema that binds to fetched data
7
- const contactDetailSchema: PageSchema = {
7
+ // ==========================================
8
+ // Story 1: Static Data Binding
9
+ // ==========================================
10
+ // Demonstrates how JSON schemas can reference data via expressions like ${data.field}
11
+
12
+ const staticDataSchema: BaseSchema = {
8
13
  type: "page",
9
- props: { title: "Contact Details (Mocked)" },
14
+ props: { title: "Static Data Binding" },
10
15
  children: [
11
16
  {
12
17
  type: "page:header",
13
18
  props: {
14
- title: "${data.name}",
15
- description: "${data.title} at ${data.company}"
19
+ title: "Sarah Connors",
20
+ description: "Lead Engineer at Cyberdyne Systems"
16
21
  }
17
22
  },
18
23
  {
19
- type: "view:simple",
20
- props: { columns: 2, className: "mt-4 border p-4 rounded" },
24
+ type: "grid",
25
+ props: { cols: 2, gap: 4, className: "mt-4" },
21
26
  children: [
22
- { type: "field:text", bind: "name", props: { label: "Full Name", readonly: true } },
23
- { type: "field:email", bind: "email", props: { label: "Email", readonly: true } },
24
- { type: "field:text", bind: "status", props: { label: "Status", readonly: true } }
27
+ { type: "card", title: "Full Name", children: [{ type: "text", content: "Sarah Connors" }] },
28
+ { type: "card", title: "Email", children: [{ type: "text", content: "sarah@cyberdyne.com" }] },
29
+ { type: "card", title: "Status", children: [{ type: "badge", props: { variant: "default", children: "Active" } }] },
30
+ { type: "card", title: "Department", children: [{ type: "text", content: "Engineering" }] },
25
31
  ]
26
32
  }
27
33
  ]
28
34
  };
29
35
 
30
- // A wrapper component that fetches data from the MSW mock
31
- const DataFetcher = () => {
32
- const [data, setData] = useState<any>(null);
36
+ // ==========================================
37
+ // Story 2: MSW-Backed Data (Live API)
38
+ // ==========================================
39
+ // The ObjectStack kernel seeds CRM data via MSW. This story fetches contacts from /api/v1.
33
40
 
34
- useEffect(() => {
35
- // Simulate a fetch call locally since MSW might not be configured in this environment
36
- const mockData = {
37
- name: "Sarah Connors",
38
- title: "Lead Engineer",
39
- company: "Cyberdyne Systems",
40
- email: "sarah@cyberdyne.com",
41
- status: "Active"
42
- };
41
+ const dataSource = createStorybookDataSource();
43
42
 
44
- const timer = setTimeout(() => {
45
- setData(mockData);
46
- }, 800);
43
+ const ContactListFromAPI = () => {
44
+ const [contacts, setContacts] = useState<any[]>([]);
45
+ const [loading, setLoading] = useState(true);
46
+ const [error, setError] = useState<string | null>(null);
47
47
 
48
- return () => clearTimeout(timer);
48
+ const fetchContacts = useCallback(async () => {
49
+ setLoading(true);
50
+ setError(null);
51
+ try {
52
+ const result = await dataSource.find('contact', { $top: 10 });
53
+ setContacts(result.data as any[]);
54
+ } catch (err: any) {
55
+ console.error('[Data Binding] Failed to fetch contacts:', err);
56
+ setError(err?.message || 'Failed to fetch data from MSW API');
57
+ } finally {
58
+ setLoading(false);
59
+ }
49
60
  }, []);
50
61
 
51
- if (!data) return <div className="p-4">Loading data from MSW...</div>;
62
+ useEffect(() => {
63
+ fetchContacts();
64
+ }, [fetchContacts]);
65
+
66
+ if (loading) return <div className="p-8 text-muted-foreground">Loading contacts from MSW API...</div>;
67
+ if (error) return (
68
+ <div className="p-8 space-y-2">
69
+ <div className="text-destructive font-medium">API Error: {error}</div>
70
+ <div className="text-sm text-muted-foreground">
71
+ This story requires the ObjectStack MSW runtime to be running.
72
+ Check the browser console for [Storybook MSW] logs.
73
+ </div>
74
+ </div>
75
+ );
76
+
77
+ const gridSchema: BaseSchema = {
78
+ type: 'object-grid',
79
+ objectName: 'contact',
80
+ columns: [
81
+ { field: 'name', header: 'Name', sortable: true },
82
+ { field: 'email', header: 'Email', sortable: true },
83
+ { field: 'title', header: 'Title' },
84
+ { field: 'status', header: 'Status' },
85
+ ],
86
+ data: contacts,
87
+ className: 'w-full'
88
+ } as any;
52
89
 
53
90
  return (
54
- <SchemaRendererProvider dataSource={data}>
55
- <SchemaRenderer schema={contactDetailSchema} />
91
+ <SchemaRendererProvider dataSource={dataSource}>
92
+ <div className="space-y-4">
93
+ <div className="flex items-center justify-between">
94
+ <h2 className="text-lg font-semibold">Contacts from MSW ({contacts.length} records)</h2>
95
+ </div>
96
+ <SchemaRenderer schema={gridSchema} />
97
+ </div>
56
98
  </SchemaRendererProvider>
57
99
  );
58
100
  };
59
101
 
102
+ // ==========================================
103
+ // Meta
104
+ // ==========================================
105
+
60
106
  const meta: Meta = {
61
- title: 'Guide/Mocked Data',
62
- component: DataFetcher,
107
+ title: 'Getting Started/Data Binding',
63
108
  parameters: {
64
- // We can also override handlers per story here if needed
65
- // msw: { handlers: [...] }
66
- }
109
+ layout: 'padded',
110
+ },
67
111
  };
68
112
 
69
113
  export default meta;
70
114
 
71
- export const Default: StoryObj = {};
115
+ export const StaticData: StoryObj = {
116
+ render: () => <SchemaRenderer schema={staticDataSchema} />,
117
+ };
118
+
119
+ export const LiveAPI: StoryObj = {
120
+ render: () => <ContactListFromAPI />,
121
+ };
@@ -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: 'Components/Accordion',
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: 'Views/Data Grid (AgGrid)',
6
+ title: 'Plugins/Data Views/AgGrid',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'padded',
@@ -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: 'Components/Alert',
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: 'Components/Aspect Ratio',
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: 'Components/Avatar',
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: 'Components/Badge',
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: 'Components/Breadcrumb',
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: 'Components/Button Group',
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: 'Components/Button',
6
+ title: 'Primitives/General/Button',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'centered',
@@ -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: 'Components/Calendar',
6
+ title: 'Plugins/Scheduling/Calendar',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'padded',
@@ -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: 'Components/Card',
6
+ title: 'Primitives/Data Display/Card',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'padded',
@@ -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: 'Components/Carousel',
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: 'Views/Charts',
6
+ title: 'Plugins/Rich Content/Charts',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'padded',
@@ -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: 'Views/Chatbot',
6
+ title: 'Plugins/Rich Content/Chatbot',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'centered',
@@ -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: 'Views/Code Editor',
6
+ title: 'Plugins/Rich Content/Code Editor',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'padded',
@@ -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: 'Components/Collapsible',
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: 'Components/Controls',
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 '../SchemaRenderer';
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: 'Components/Table',
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: (args) => <SchemaRenderer schema={args} />
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: (args) => <SchemaRenderer schema={args} />
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: 'Components/Data Display Extras',
5
+ title: 'Primitives/Data Display/Extras',
6
6
  component: SchemaRenderer,
7
7
  tags: ['autodocs'],
8
8
  argTypes: {
@@ -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: 'Components/Date Picker',
6
+ title: 'Primitives/Data Entry/Date Picker',
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: 'Views/DetailView',
6
+ title: 'Plugins/Data Views/Detail View',
7
7
  component: SchemaRenderer,
8
8
  parameters: {
9
9
  layout: 'padded',
@@ -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: 'Components/Dialog',
6
+ title: 'Primitives/Feedback/Dialog',
7
7
  component: SchemaRenderer,
8
8
  parameters: { layout: 'centered' },
9
9
  tags: ['autodocs'],
@@ -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: 'Components/Feedback Extras',
5
+ title: 'Primitives/Feedback/Extras',
6
6
  component: SchemaRenderer,
7
7
  tags: ['autodocs'],
8
8
  };
@@ -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: 'Components/Feedback Others',
6
+ title: 'Primitives/Feedback/Others',
7
7
  component: SchemaRenderer,
8
8
  parameters: { layout: 'centered' },
9
9
  tags: ['autodocs'],