@object-ui/plugin-grid 3.3.0 → 3.3.2
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/CHANGELOG.md +23 -0
- package/README.md +21 -1
- package/dist/index.js +631 -599
- package/dist/index.umd.cjs +8 -8
- package/package.json +44 -12
- package/.turbo/turbo-build.log +0 -32
- package/src/FormulaBar.tsx +0 -151
- package/src/GroupRow.tsx +0 -69
- package/src/ImportWizard.tsx +0 -412
- package/src/InlineEditing.tsx +0 -235
- package/src/ListColumnExtensions.test.tsx +0 -373
- package/src/ListColumnSchema.test.ts +0 -88
- package/src/ObjectGrid.EdgeCases.stories.tsx +0 -147
- package/src/ObjectGrid.msw.test.tsx +0 -130
- package/src/ObjectGrid.stories.tsx +0 -139
- package/src/ObjectGrid.tsx +0 -1598
- package/src/SplitPaneGrid.tsx +0 -120
- package/src/VirtualGrid.tsx +0 -183
- package/src/__tests__/GroupRow.test.tsx +0 -206
- package/src/__tests__/ImportPreview.test.tsx +0 -171
- package/src/__tests__/InlineEditing.test.tsx +0 -360
- package/src/__tests__/VirtualGrid.test.tsx +0 -438
- package/src/__tests__/accessibility.test.tsx +0 -254
- package/src/__tests__/accessorKey-inference.test.tsx +0 -132
- package/src/__tests__/airtable-style.test.tsx +0 -508
- package/src/__tests__/column-features.test.tsx +0 -490
- package/src/__tests__/grid-export.test.tsx +0 -121
- package/src/__tests__/mobile-card-view.test.tsx +0 -355
- package/src/__tests__/objectdef-enrichment.test.tsx +0 -566
- package/src/__tests__/performance-benchmark.test.tsx +0 -182
- package/src/__tests__/phase11-features.test.tsx +0 -418
- package/src/__tests__/row-bulk-actions.test.tsx +0 -413
- package/src/__tests__/row-height.test.tsx +0 -160
- package/src/__tests__/useGroupedData.test.ts +0 -165
- package/src/__tests__/view-states.test.tsx +0 -203
- package/src/components/BulkActionBar.tsx +0 -66
- package/src/components/RowActionMenu.tsx +0 -91
- package/src/index.test.tsx +0 -29
- package/src/index.tsx +0 -99
- package/src/useCellClipboard.ts +0 -136
- package/src/useColumnSummary.ts +0 -128
- package/src/useGradientColor.ts +0 -103
- package/src/useGroupReorder.ts +0 -123
- package/src/useGroupedData.ts +0 -187
- package/src/useRowColor.ts +0 -74
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -58
- package/vitest.config.ts +0 -13
- package/vitest.setup.ts +0 -1
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
|
|
3
|
-
import type { BaseSchema } from '@object-ui/types';
|
|
4
|
-
import { createStorybookDataSource } from '@storybook-config/datasource';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: 'Plugins/ObjectGrid/Edge Cases',
|
|
8
|
-
component: SchemaRenderer,
|
|
9
|
-
parameters: {
|
|
10
|
-
layout: 'padded',
|
|
11
|
-
},
|
|
12
|
-
tags: ['autodocs'],
|
|
13
|
-
argTypes: {
|
|
14
|
-
schema: { table: { disable: true } },
|
|
15
|
-
},
|
|
16
|
-
} satisfies Meta<any>;
|
|
17
|
-
|
|
18
|
-
export default meta;
|
|
19
|
-
type Story = StoryObj<typeof meta>;
|
|
20
|
-
|
|
21
|
-
const dataSource = createStorybookDataSource();
|
|
22
|
-
|
|
23
|
-
const renderStory = (args: any) => (
|
|
24
|
-
<SchemaRendererProvider dataSource={dataSource}>
|
|
25
|
-
<SchemaRenderer schema={args as unknown as BaseSchema} />
|
|
26
|
-
</SchemaRendererProvider>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
// ── Empty Data ────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
export const EmptyData: Story = {
|
|
32
|
-
name: 'Empty – No Rows',
|
|
33
|
-
render: renderStory,
|
|
34
|
-
args: {
|
|
35
|
-
type: 'object-grid',
|
|
36
|
-
objectName: 'Employee',
|
|
37
|
-
columns: [
|
|
38
|
-
{ field: 'id', header: 'ID', width: 80 },
|
|
39
|
-
{ field: 'name', header: 'Name' },
|
|
40
|
-
{ field: 'email', header: 'Email' },
|
|
41
|
-
{ field: 'department', header: 'Department' },
|
|
42
|
-
],
|
|
43
|
-
data: [],
|
|
44
|
-
pagination: false,
|
|
45
|
-
className: 'w-full',
|
|
46
|
-
} as any,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// ── Single Row ────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
export const SingleRow: Story = {
|
|
52
|
-
name: 'Single Row',
|
|
53
|
-
render: renderStory,
|
|
54
|
-
args: {
|
|
55
|
-
type: 'object-grid',
|
|
56
|
-
objectName: 'Employee',
|
|
57
|
-
columns: [
|
|
58
|
-
{ field: 'id', header: 'ID', width: 80 },
|
|
59
|
-
{ field: 'name', header: 'Name', sortable: true },
|
|
60
|
-
{ field: 'email', header: 'Email' },
|
|
61
|
-
{ field: 'department', header: 'Department' },
|
|
62
|
-
],
|
|
63
|
-
data: [
|
|
64
|
-
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering' },
|
|
65
|
-
],
|
|
66
|
-
pagination: false,
|
|
67
|
-
className: 'w-full',
|
|
68
|
-
} as any,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// ── Many Columns ──────────────────────────────────────────────
|
|
72
|
-
|
|
73
|
-
export const ManyColumns: Story = {
|
|
74
|
-
name: 'Many Columns (15+)',
|
|
75
|
-
render: renderStory,
|
|
76
|
-
args: {
|
|
77
|
-
type: 'object-grid',
|
|
78
|
-
objectName: 'WideTable',
|
|
79
|
-
columns: Array.from({ length: 18 }, (_, i) => ({
|
|
80
|
-
field: `col${i + 1}`,
|
|
81
|
-
header: `Column ${i + 1}`,
|
|
82
|
-
sortable: i < 5,
|
|
83
|
-
})),
|
|
84
|
-
data: Array.from({ length: 5 }, (_, row) => {
|
|
85
|
-
const record: Record<string, any> = {};
|
|
86
|
-
for (let c = 1; c <= 18; c++) {
|
|
87
|
-
record[`col${c}`] = `R${row + 1}-C${c}`;
|
|
88
|
-
}
|
|
89
|
-
return record;
|
|
90
|
-
}),
|
|
91
|
-
pagination: false,
|
|
92
|
-
className: 'w-full',
|
|
93
|
-
} as any,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// ── Very Long Cell Values ─────────────────────────────────────
|
|
97
|
-
|
|
98
|
-
const LONG_VALUE =
|
|
99
|
-
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
|
|
100
|
-
|
|
101
|
-
export const LongCellValues: Story = {
|
|
102
|
-
name: 'Very Long Cell Values',
|
|
103
|
-
render: renderStory,
|
|
104
|
-
args: {
|
|
105
|
-
type: 'object-grid',
|
|
106
|
-
objectName: 'Article',
|
|
107
|
-
columns: [
|
|
108
|
-
{ field: 'id', header: 'ID', width: 60 },
|
|
109
|
-
{ field: 'title', header: 'Title' },
|
|
110
|
-
{ field: 'abstract', header: 'Abstract' },
|
|
111
|
-
{ field: 'author', header: 'Author' },
|
|
112
|
-
],
|
|
113
|
-
data: [
|
|
114
|
-
{ id: 1, title: LONG_VALUE, abstract: LONG_VALUE + ' ' + LONG_VALUE, author: 'Dr. Extremely Long Author Name The Third Junior' },
|
|
115
|
-
{ id: 2, title: 'Short', abstract: 'Brief.', author: 'Bob' },
|
|
116
|
-
{ id: 3, title: LONG_VALUE, abstract: LONG_VALUE, author: LONG_VALUE },
|
|
117
|
-
],
|
|
118
|
-
pagination: false,
|
|
119
|
-
className: 'w-full',
|
|
120
|
-
} as any,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// ── Null / Undefined Values ───────────────────────────────────
|
|
124
|
-
|
|
125
|
-
export const NullAndUndefinedValues: Story = {
|
|
126
|
-
name: 'Null / Undefined Cell Values',
|
|
127
|
-
render: renderStory,
|
|
128
|
-
args: {
|
|
129
|
-
type: 'object-grid',
|
|
130
|
-
objectName: 'Sparse',
|
|
131
|
-
columns: [
|
|
132
|
-
{ field: 'id', header: 'ID', width: 60 },
|
|
133
|
-
{ field: 'name', header: 'Name' },
|
|
134
|
-
{ field: 'email', header: 'Email' },
|
|
135
|
-
{ field: 'phone', header: 'Phone' },
|
|
136
|
-
{ field: 'notes', header: 'Notes' },
|
|
137
|
-
],
|
|
138
|
-
data: [
|
|
139
|
-
{ id: 1, name: 'Alice', email: null, phone: undefined, notes: '' },
|
|
140
|
-
{ id: 2, name: null, email: 'bob@example.com', phone: null, notes: undefined },
|
|
141
|
-
{ id: 3, name: undefined, email: undefined, phone: undefined, notes: null },
|
|
142
|
-
{ id: 4, name: 'Dave', email: 'dave@example.com', phone: '+1-555-0100', notes: 'Complete record' },
|
|
143
|
-
],
|
|
144
|
-
pagination: false,
|
|
145
|
-
className: 'w-full',
|
|
146
|
-
} as any,
|
|
147
|
-
};
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
-
import { render, screen, waitFor, within } from '@testing-library/react';
|
|
3
|
-
import '@testing-library/jest-dom';
|
|
4
|
-
import { ObjectGrid } from './ObjectGrid';
|
|
5
|
-
import { ObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
6
|
-
import { setupServer } from 'msw/node';
|
|
7
|
-
import { http, HttpResponse } from 'msw';
|
|
8
|
-
import { registerAllFields } from '@object-ui/fields';
|
|
9
|
-
import React from 'react';
|
|
10
|
-
import { ContactObject } from '../../../examples/crm/src/objects/contact.object';
|
|
11
|
-
|
|
12
|
-
registerAllFields();
|
|
13
|
-
|
|
14
|
-
const BASE_URL = process.env.OBJECTSTACK_API_URL || 'http://localhost';
|
|
15
|
-
|
|
16
|
-
// --- Mock Data ---
|
|
17
|
-
|
|
18
|
-
const mockSchema = ContactObject;
|
|
19
|
-
|
|
20
|
-
const mockData = {
|
|
21
|
-
value: [
|
|
22
|
-
{ id: '1', name: 'John Doe', email: 'john@example.com', company: 'Acme Inc' },
|
|
23
|
-
{ id: '2', name: 'Jane Smith', email: 'jane@test.com', company: 'Globex' },
|
|
24
|
-
{ id: '3', name: 'Bob Wilson', email: 'bob@test.com', company: 'Acme Inc' }
|
|
25
|
-
],
|
|
26
|
-
count: 3,
|
|
27
|
-
'@odata.count': 3
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// --- MSW Setup ---
|
|
31
|
-
|
|
32
|
-
const handlers = [
|
|
33
|
-
// .well-known discovery endpoint (used by client.connect())
|
|
34
|
-
http.get(`${BASE_URL}/.well-known/objectstack`, () => {
|
|
35
|
-
return HttpResponse.json({
|
|
36
|
-
name: 'ObjectStack API',
|
|
37
|
-
version: '1.0',
|
|
38
|
-
endpoints: {
|
|
39
|
-
data: '/api/v1/data',
|
|
40
|
-
metadata: '/api/v1/meta'
|
|
41
|
-
},
|
|
42
|
-
capabilities: {
|
|
43
|
-
graphql: false,
|
|
44
|
-
search: false,
|
|
45
|
-
websockets: false,
|
|
46
|
-
files: true,
|
|
47
|
-
analytics: false,
|
|
48
|
-
hub: false
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}),
|
|
52
|
-
|
|
53
|
-
// OPTIONS handler for CORS preflight
|
|
54
|
-
http.options(`${BASE_URL}/*`, () => {
|
|
55
|
-
return new HttpResponse(null, {
|
|
56
|
-
status: 200,
|
|
57
|
-
headers: {
|
|
58
|
-
'Access-Control-Allow-Origin': '*',
|
|
59
|
-
'Access-Control-Allow-Methods': 'GET,HEAD,POST,PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH',
|
|
60
|
-
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
}),
|
|
64
|
-
|
|
65
|
-
// Health check / Connection check
|
|
66
|
-
http.get(`${BASE_URL}/api/v1`, () => {
|
|
67
|
-
return HttpResponse.json({ status: 'ok', version: '1.0.0' });
|
|
68
|
-
}),
|
|
69
|
-
|
|
70
|
-
// Schema: /api/v1/metadata/object/:name and /api/v1/meta/object/:name (client uses /meta)
|
|
71
|
-
http.get(`${BASE_URL}/api/v1/metadata/object/contact`, () => {
|
|
72
|
-
return HttpResponse.json(mockSchema);
|
|
73
|
-
}),
|
|
74
|
-
http.get(`${BASE_URL}/api/v1/meta/object/contact`, () => {
|
|
75
|
-
return HttpResponse.json(mockSchema);
|
|
76
|
-
}),
|
|
77
|
-
|
|
78
|
-
// Data Query: /api/v1/data/contact
|
|
79
|
-
http.get(`${BASE_URL}/api/v1/data/contact`, () => {
|
|
80
|
-
return HttpResponse.json(mockData);
|
|
81
|
-
})
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
const server = setupServer(...handlers);
|
|
85
|
-
|
|
86
|
-
// --- Test Suite ---
|
|
87
|
-
|
|
88
|
-
describe('ObjectGrid with ObjectStack/MSW', () => {
|
|
89
|
-
// Only start MSW if we are NOT using a real server
|
|
90
|
-
if (!process.env.OBJECTSTACK_API_URL) {
|
|
91
|
-
beforeAll(() => server.listen());
|
|
92
|
-
afterEach(() => server.resetHandlers());
|
|
93
|
-
afterAll(() => server.close());
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const dataSource = new ObjectStackAdapter({
|
|
97
|
-
baseUrl: BASE_URL,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('loads schema and data rows', async () => {
|
|
101
|
-
render(
|
|
102
|
-
<ObjectGrid
|
|
103
|
-
schema={{
|
|
104
|
-
type: 'object-grid',
|
|
105
|
-
objectName: 'contact',
|
|
106
|
-
columns: ['name', 'email', 'company'] // Explicit columns or auto-derived
|
|
107
|
-
}}
|
|
108
|
-
dataSource={dataSource}
|
|
109
|
-
/>
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
// Verify Column Headers (from schema or props)
|
|
113
|
-
await waitFor(() => {
|
|
114
|
-
// Changed from 'Full Name' to 'Name' to match CRM example schema
|
|
115
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
116
|
-
});
|
|
117
|
-
expect(screen.getByText('Email')).toBeInTheDocument();
|
|
118
|
-
expect(screen.getByText('Company')).toBeInTheDocument();
|
|
119
|
-
|
|
120
|
-
// Verify Data Rows
|
|
121
|
-
await waitFor(() => {
|
|
122
|
-
expect(screen.getByText('John Doe')).toBeInTheDocument();
|
|
123
|
-
});
|
|
124
|
-
expect(screen.getByText('jane@test.com')).toBeInTheDocument();
|
|
125
|
-
expect(screen.getByText('Globex')).toBeInTheDocument();
|
|
126
|
-
|
|
127
|
-
// Check Row Count (if grid displays it)
|
|
128
|
-
// expect(screen.getByText(/3 records/i)).toBeInTheDocument();
|
|
129
|
-
});
|
|
130
|
-
});
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
|
|
3
|
-
import type { BaseSchema } from '@object-ui/types';
|
|
4
|
-
import { createStorybookDataSource } from '@storybook-config/datasource';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: 'Plugins/ObjectGrid',
|
|
8
|
-
component: SchemaRenderer,
|
|
9
|
-
parameters: {
|
|
10
|
-
layout: 'padded',
|
|
11
|
-
},
|
|
12
|
-
tags: ['autodocs'],
|
|
13
|
-
argTypes: {
|
|
14
|
-
schema: { table: { disable: true } },
|
|
15
|
-
},
|
|
16
|
-
} satisfies Meta<any>;
|
|
17
|
-
|
|
18
|
-
export default meta;
|
|
19
|
-
type Story = StoryObj<typeof meta>;
|
|
20
|
-
|
|
21
|
-
const dataSource = createStorybookDataSource();
|
|
22
|
-
|
|
23
|
-
const renderStory = (args: any) => (
|
|
24
|
-
<SchemaRendererProvider dataSource={dataSource}>
|
|
25
|
-
<SchemaRenderer schema={args as unknown as BaseSchema} />
|
|
26
|
-
</SchemaRendererProvider>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
export const Default: Story = {
|
|
30
|
-
render: renderStory,
|
|
31
|
-
args: {
|
|
32
|
-
type: 'object-grid',
|
|
33
|
-
objectName: 'Employee',
|
|
34
|
-
columns: [
|
|
35
|
-
{ field: 'id', header: 'ID', width: 80 },
|
|
36
|
-
{ field: 'name', header: 'Name', sortable: true, filterable: true },
|
|
37
|
-
{ field: 'email', header: 'Email', sortable: true, filterable: true },
|
|
38
|
-
{ field: 'department', header: 'Department', sortable: true },
|
|
39
|
-
{ field: 'status', header: 'Status', sortable: true },
|
|
40
|
-
],
|
|
41
|
-
data: [
|
|
42
|
-
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering', status: 'Active' },
|
|
43
|
-
{ id: 2, name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing', status: 'Active' },
|
|
44
|
-
{ id: 3, name: 'Carol White', email: 'carol@example.com', department: 'Sales', status: 'Inactive' },
|
|
45
|
-
{ id: 4, name: 'Dave Brown', email: 'dave@example.com', department: 'Engineering', status: 'Active' },
|
|
46
|
-
{ id: 5, name: 'Eve Davis', email: 'eve@example.com', department: 'HR', status: 'Active' },
|
|
47
|
-
],
|
|
48
|
-
pagination: true,
|
|
49
|
-
pageSize: 10,
|
|
50
|
-
className: 'w-full',
|
|
51
|
-
} as any,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export const WithRowActions: Story = {
|
|
55
|
-
render: renderStory,
|
|
56
|
-
args: {
|
|
57
|
-
type: 'object-grid',
|
|
58
|
-
objectName: 'Task',
|
|
59
|
-
columns: [
|
|
60
|
-
{ field: 'id', header: 'ID', width: 80 },
|
|
61
|
-
{ field: 'title', header: 'Title', sortable: true, filterable: true },
|
|
62
|
-
{ field: 'assignee', header: 'Assignee', sortable: true },
|
|
63
|
-
{ field: 'priority', header: 'Priority', sortable: true },
|
|
64
|
-
{ field: 'status', header: 'Status', sortable: true },
|
|
65
|
-
],
|
|
66
|
-
actions: [
|
|
67
|
-
{ label: 'View', action: 'view' },
|
|
68
|
-
{ label: 'Edit', action: 'edit' },
|
|
69
|
-
{ label: 'Delete', action: 'delete', variant: 'destructive' },
|
|
70
|
-
],
|
|
71
|
-
data: [
|
|
72
|
-
{ id: 1, title: 'Fix login bug', assignee: 'Alice', priority: 'High', status: 'In Progress' },
|
|
73
|
-
{ id: 2, title: 'Add dark mode', assignee: 'Bob', priority: 'Medium', status: 'To Do' },
|
|
74
|
-
{ id: 3, title: 'Update docs', assignee: 'Carol', priority: 'Low', status: 'Done' },
|
|
75
|
-
],
|
|
76
|
-
pagination: true,
|
|
77
|
-
pageSize: 10,
|
|
78
|
-
className: 'w-full',
|
|
79
|
-
} as any,
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* CRM Deals Pipeline — demonstrates professional data formatting:
|
|
84
|
-
* - Currency with thousand separators (Amount column, right-aligned)
|
|
85
|
-
* - Percentage with progress bar (Probability column, right-aligned)
|
|
86
|
-
* - Formatted dates (Close Date column)
|
|
87
|
-
* - Colored badges for stage/status (Stage column)
|
|
88
|
-
* - Bold clickable name as primary link (Name column)
|
|
89
|
-
* - Empty value placeholder (Account column)
|
|
90
|
-
*/
|
|
91
|
-
export const CRMDeals: Story = {
|
|
92
|
-
name: 'CRM Deals Pipeline',
|
|
93
|
-
render: renderStory,
|
|
94
|
-
args: {
|
|
95
|
-
type: 'object-grid',
|
|
96
|
-
objectName: 'Deal',
|
|
97
|
-
columns: [
|
|
98
|
-
{ field: 'name', label: 'Name', link: true, sortable: true },
|
|
99
|
-
{ field: 'account', label: 'Account' },
|
|
100
|
-
{ field: 'stage', label: 'Stage', type: 'select', sortable: true },
|
|
101
|
-
{ field: 'amount', label: 'Amount', type: 'currency', sortable: true },
|
|
102
|
-
{ field: 'probability', label: 'Probability', type: 'percent', sortable: true },
|
|
103
|
-
{ field: 'close_date', label: 'Close Date', type: 'date', sortable: true },
|
|
104
|
-
],
|
|
105
|
-
data: [
|
|
106
|
-
{ id: '1', name: 'ObjectStack Enterprise License', account: '', stage: 'Closed Won', amount: 150000, probability: 100, close_date: '2024-01-15T00:00:00.000Z' },
|
|
107
|
-
{ id: '2', name: 'Cloud Migration Project', account: 'Acme Corp', stage: 'Negotiation', amount: 85000, probability: 60, close_date: '2024-03-20T00:00:00.000Z' },
|
|
108
|
-
{ id: '3', name: 'Annual Support Renewal', account: '', stage: 'Proposal', amount: 42000, probability: 80, close_date: '2024-02-28T00:00:00.000Z' },
|
|
109
|
-
{ id: '4', name: 'Custom Integration Development', account: 'TechFlow Inc', stage: 'Qualification', amount: 230000, probability: 30, close_date: '2024-06-15T00:00:00.000Z' },
|
|
110
|
-
{ id: '5', name: 'Data Analytics Platform', account: '', stage: 'Closed Lost', amount: 95000, probability: 0, close_date: '2024-01-10T00:00:00.000Z' },
|
|
111
|
-
{ id: '6', name: 'Security Audit Contract', account: 'SecureNet', stage: 'Closed Won', amount: 67500, probability: 100, close_date: '2024-02-01T00:00:00.000Z' },
|
|
112
|
-
{ id: '7', name: 'Mobile App Development', account: '', stage: 'Discovery', amount: 180000, probability: 15, close_date: '2024-08-30T00:00:00.000Z' },
|
|
113
|
-
],
|
|
114
|
-
pagination: false,
|
|
115
|
-
className: 'w-full',
|
|
116
|
-
} as any,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
export const EditableGrid: Story = {
|
|
120
|
-
render: renderStory,
|
|
121
|
-
args: {
|
|
122
|
-
type: 'object-grid',
|
|
123
|
-
objectName: 'Product',
|
|
124
|
-
columns: [
|
|
125
|
-
{ field: 'sku', header: 'SKU', width: 100, editable: false },
|
|
126
|
-
{ field: 'name', header: 'Name', sortable: true },
|
|
127
|
-
{ field: 'price', header: 'Price', sortable: true },
|
|
128
|
-
{ field: 'stock', header: 'Stock', sortable: true },
|
|
129
|
-
],
|
|
130
|
-
data: [
|
|
131
|
-
{ sku: 'SKU-001', name: 'Widget A', price: '$19.99', stock: 50 },
|
|
132
|
-
{ sku: 'SKU-002', name: 'Widget B', price: '$29.99', stock: 30 },
|
|
133
|
-
{ sku: 'SKU-003', name: 'Widget C', price: '$9.99', stock: 120 },
|
|
134
|
-
],
|
|
135
|
-
editable: true,
|
|
136
|
-
pagination: false,
|
|
137
|
-
className: 'w-full',
|
|
138
|
-
} as any,
|
|
139
|
-
};
|