@akinon/ai-modal-table 1.0.2 → 1.0.4
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/dist/cjs/ai-table/components/__tests__/content.test.js +46 -0
- package/dist/cjs/ai-table/components/__tests__/content.test.tsx +62 -0
- package/dist/cjs/ai-table/components/content.d.ts.map +1 -1
- package/dist/cjs/ai-table/components/content.js +14 -10
- package/dist/cjs/ai-table/components/mapper.js +1 -1
- package/dist/cjs/ai-table/constants/index.d.ts +4 -0
- package/dist/cjs/ai-table/constants/index.d.ts.map +1 -1
- package/dist/cjs/ai-table/constants/index.js +5 -1
- package/dist/cjs/ai-table/utils/render-edit-fields/__tests__/index.test.d.ts +2 -0
- package/dist/cjs/ai-table/utils/render-edit-fields/__tests__/index.test.d.ts.map +1 -0
- package/dist/cjs/ai-table/utils/render-edit-fields/__tests__/index.test.js +220 -0
- package/dist/cjs/ai-table/utils/render-edit-fields/__tests__/index.test.tsx +283 -0
- package/dist/cjs/ai-table/utils/render-edit-fields/index.d.ts +13 -0
- package/dist/cjs/ai-table/utils/render-edit-fields/index.d.ts.map +1 -0
- package/dist/cjs/ai-table/utils/render-edit-fields/index.js +65 -0
- package/dist/cjs/types/index.d.ts +20 -4
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/esm/ai-table/components/__tests__/content.test.js +46 -0
- package/dist/esm/ai-table/components/__tests__/content.test.tsx +62 -0
- package/dist/esm/ai-table/components/content.d.ts.map +1 -1
- package/dist/esm/ai-table/components/content.js +14 -10
- package/dist/esm/ai-table/components/mapper.js +1 -1
- package/dist/esm/ai-table/constants/index.d.ts +4 -0
- package/dist/esm/ai-table/constants/index.d.ts.map +1 -1
- package/dist/esm/ai-table/constants/index.js +4 -0
- package/dist/esm/ai-table/utils/render-edit-fields/__tests__/index.test.d.ts +2 -0
- package/dist/esm/ai-table/utils/render-edit-fields/__tests__/index.test.d.ts.map +1 -0
- package/dist/esm/ai-table/utils/render-edit-fields/__tests__/index.test.js +218 -0
- package/dist/esm/ai-table/utils/render-edit-fields/__tests__/index.test.tsx +283 -0
- package/dist/esm/ai-table/utils/render-edit-fields/index.d.ts +13 -0
- package/dist/esm/ai-table/utils/render-edit-fields/index.d.ts.map +1 -0
- package/dist/esm/ai-table/utils/render-edit-fields/index.js +60 -0
- package/dist/esm/types/index.d.ts +20 -4
- package/dist/esm/types/index.d.ts.map +1 -1
- package/package.json +13 -13
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
14
|
+
import userEvent from '@testing-library/user-event';
|
|
15
|
+
import * as React from 'react';
|
|
16
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
17
|
+
import { EDIT_FIELD_TYPES } from '../../../constants';
|
|
18
|
+
import { getEditFieldConfig, renderEditField } from '../index';
|
|
19
|
+
vi.mock('@akinon/ui-input', () => ({
|
|
20
|
+
Input: (_a) => {
|
|
21
|
+
var { value, onChange, className } = _a, props = __rest(_a, ["value", "onChange", "className"]);
|
|
22
|
+
return (React.createElement("input", Object.assign({ "data-testid": "edit-input", value: value, onChange: onChange, className: className }, props)));
|
|
23
|
+
}
|
|
24
|
+
}));
|
|
25
|
+
vi.mock('@akinon/ui-select', () => ({
|
|
26
|
+
Select: (_a) => {
|
|
27
|
+
var { value, onChange, rootClassName, options } = _a, props = __rest(_a, ["value", "onChange", "rootClassName", "options"]);
|
|
28
|
+
const handleChange = (e) => {
|
|
29
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e.target.value);
|
|
30
|
+
};
|
|
31
|
+
return (React.createElement("select", Object.assign({ "data-testid": "edit-select", value: value !== null && value !== void 0 ? value : '', onChange: handleChange, className: rootClassName }, props), options === null || options === void 0 ? void 0 : options.map((opt) => (React.createElement("option", { key: opt.value, value: String(opt.value) }, opt.label)))));
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
describe('getEditFieldConfig', () => {
|
|
35
|
+
it('returns config with INPUT type when dataIndex matches string in editDataIndexes', () => {
|
|
36
|
+
const editDataIndexes = ['name', 'value'];
|
|
37
|
+
expect(getEditFieldConfig(editDataIndexes, 'name')).toEqual({
|
|
38
|
+
key: 'name',
|
|
39
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
40
|
+
});
|
|
41
|
+
expect(getEditFieldConfig(editDataIndexes, 'value')).toEqual({
|
|
42
|
+
key: 'value',
|
|
43
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
it('returns undefined when dataIndex does not match any string', () => {
|
|
47
|
+
const editDataIndexes = ['name', 'value'];
|
|
48
|
+
expect(getEditFieldConfig(editDataIndexes, 'other')).toBeUndefined();
|
|
49
|
+
expect(getEditFieldConfig(editDataIndexes, '')).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
it('returns config object when dataIndex matches object key (INPUT)', () => {
|
|
52
|
+
const editDataIndexes = [
|
|
53
|
+
{ key: 'name', type: EDIT_FIELD_TYPES.INPUT },
|
|
54
|
+
{
|
|
55
|
+
key: 'value',
|
|
56
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
57
|
+
attributes: { placeholder: 'Value' }
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
expect(getEditFieldConfig(editDataIndexes, 'name')).toEqual({
|
|
61
|
+
key: 'name',
|
|
62
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
63
|
+
});
|
|
64
|
+
expect(getEditFieldConfig(editDataIndexes, 'value')).toEqual({
|
|
65
|
+
key: 'value',
|
|
66
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
67
|
+
attributes: { placeholder: 'Value' }
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
it('returns config object when dataIndex matches object key (SELECT)', () => {
|
|
71
|
+
const selectConfig = {
|
|
72
|
+
key: 'status',
|
|
73
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
74
|
+
attributes: {
|
|
75
|
+
options: [
|
|
76
|
+
{ label: 'Active', value: 'active' },
|
|
77
|
+
{ label: 'Inactive', value: 'inactive' }
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const editDataIndexes = [selectConfig];
|
|
82
|
+
expect(getEditFieldConfig(editDataIndexes, 'status')).toEqual(selectConfig);
|
|
83
|
+
});
|
|
84
|
+
it('returns undefined for empty editDataIndexes', () => {
|
|
85
|
+
expect(getEditFieldConfig([], 'name')).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
it('returns first match when multiple entries could match', () => {
|
|
88
|
+
const editDataIndexes = [
|
|
89
|
+
'name',
|
|
90
|
+
{ key: 'name', type: EDIT_FIELD_TYPES.INPUT }
|
|
91
|
+
];
|
|
92
|
+
expect(getEditFieldConfig(editDataIndexes, 'name')).toEqual({
|
|
93
|
+
key: 'name',
|
|
94
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('renderEditField', () => {
|
|
99
|
+
it('renders Input for INPUT config', () => {
|
|
100
|
+
const onChange = vi.fn();
|
|
101
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
102
|
+
config: { key: 'name', type: EDIT_FIELD_TYPES.INPUT },
|
|
103
|
+
value: 'test',
|
|
104
|
+
onChange
|
|
105
|
+
})));
|
|
106
|
+
const input = screen.getByTestId('edit-input');
|
|
107
|
+
expect(input).toBeInTheDocument();
|
|
108
|
+
expect(input).toHaveValue('test');
|
|
109
|
+
});
|
|
110
|
+
it('calls onChange with input value when Input changes', () => {
|
|
111
|
+
const onChange = vi.fn();
|
|
112
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
113
|
+
config: { key: 'name', type: EDIT_FIELD_TYPES.INPUT },
|
|
114
|
+
value: '',
|
|
115
|
+
onChange
|
|
116
|
+
})));
|
|
117
|
+
const input = screen.getByTestId('edit-input');
|
|
118
|
+
fireEvent.change(input, { target: { value: 'new value' } });
|
|
119
|
+
expect(onChange).toHaveBeenCalledWith('new value');
|
|
120
|
+
});
|
|
121
|
+
it('passes attributes to Input (className, etc.)', () => {
|
|
122
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
123
|
+
config: {
|
|
124
|
+
key: 'name',
|
|
125
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
126
|
+
attributes: { className: 'custom-class', placeholder: 'Name' }
|
|
127
|
+
},
|
|
128
|
+
value: '',
|
|
129
|
+
onChange: vi.fn()
|
|
130
|
+
})));
|
|
131
|
+
const input = screen.getByTestId('edit-input');
|
|
132
|
+
expect(input).toHaveClass('custom-class');
|
|
133
|
+
expect(input).toHaveAttribute('placeholder', 'Name');
|
|
134
|
+
});
|
|
135
|
+
it('calls attributes.onChange and then onChange when both provided', async () => {
|
|
136
|
+
const user = userEvent.setup();
|
|
137
|
+
const onAttributeChange = vi.fn();
|
|
138
|
+
const onChange = vi.fn();
|
|
139
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
140
|
+
config: {
|
|
141
|
+
key: 'name',
|
|
142
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
143
|
+
attributes: { onChange: onAttributeChange }
|
|
144
|
+
},
|
|
145
|
+
value: '',
|
|
146
|
+
onChange
|
|
147
|
+
})));
|
|
148
|
+
const input = screen.getByTestId('edit-input');
|
|
149
|
+
await user.type(input, 'x');
|
|
150
|
+
expect(onAttributeChange).toHaveBeenCalled();
|
|
151
|
+
expect(onChange).toHaveBeenCalled();
|
|
152
|
+
});
|
|
153
|
+
it('renders Select for SELECT config', () => {
|
|
154
|
+
const onChange = vi.fn();
|
|
155
|
+
const options = [
|
|
156
|
+
{ label: 'Active', value: 'active' },
|
|
157
|
+
{ label: 'Inactive', value: 'inactive' }
|
|
158
|
+
];
|
|
159
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
160
|
+
config: {
|
|
161
|
+
key: 'status',
|
|
162
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
163
|
+
attributes: { options }
|
|
164
|
+
},
|
|
165
|
+
value: 'active',
|
|
166
|
+
onChange
|
|
167
|
+
})));
|
|
168
|
+
const select = screen.getByTestId('edit-select');
|
|
169
|
+
expect(select).toBeInTheDocument();
|
|
170
|
+
expect(select).toHaveValue('active');
|
|
171
|
+
});
|
|
172
|
+
it('calls onChange when Select value changes', async () => {
|
|
173
|
+
const user = userEvent.setup();
|
|
174
|
+
const onChange = vi.fn();
|
|
175
|
+
const options = [
|
|
176
|
+
{ label: 'Active', value: 'active' },
|
|
177
|
+
{ label: 'Inactive', value: 'inactive' }
|
|
178
|
+
];
|
|
179
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
180
|
+
config: {
|
|
181
|
+
key: 'status',
|
|
182
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
183
|
+
attributes: { options }
|
|
184
|
+
},
|
|
185
|
+
value: 'active',
|
|
186
|
+
onChange
|
|
187
|
+
})));
|
|
188
|
+
const select = screen.getByTestId('edit-select');
|
|
189
|
+
await user.selectOptions(select, 'inactive');
|
|
190
|
+
expect(onChange).toHaveBeenCalledWith('inactive');
|
|
191
|
+
});
|
|
192
|
+
it('passes rootClassName to Select', () => {
|
|
193
|
+
const options = [{ label: 'A', value: 'a' }];
|
|
194
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
195
|
+
config: {
|
|
196
|
+
key: 'status',
|
|
197
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
198
|
+
attributes: { options, rootClassName: 'select-root' }
|
|
199
|
+
},
|
|
200
|
+
value: '',
|
|
201
|
+
onChange: vi.fn()
|
|
202
|
+
})));
|
|
203
|
+
const select = screen.getByTestId('edit-select');
|
|
204
|
+
expect(select).toHaveClass('select-root');
|
|
205
|
+
});
|
|
206
|
+
it('defaults to Input for unknown config type', () => {
|
|
207
|
+
render(React.createElement(React.Fragment, null, renderEditField({
|
|
208
|
+
config: {
|
|
209
|
+
key: 'name',
|
|
210
|
+
type: 'unknown'
|
|
211
|
+
},
|
|
212
|
+
value: 'fallback',
|
|
213
|
+
onChange: vi.fn()
|
|
214
|
+
})));
|
|
215
|
+
expect(screen.getByTestId('edit-input')).toBeInTheDocument();
|
|
216
|
+
expect(screen.getByTestId('edit-input')).toHaveValue('fallback');
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { EDIT_FIELD_TYPES } from '../../../constants';
|
|
8
|
+
import { getEditFieldConfig, renderEditField } from '../index';
|
|
9
|
+
|
|
10
|
+
vi.mock('@akinon/ui-input', () => ({
|
|
11
|
+
Input: ({
|
|
12
|
+
value,
|
|
13
|
+
onChange,
|
|
14
|
+
className,
|
|
15
|
+
...props
|
|
16
|
+
}: {
|
|
17
|
+
value: string;
|
|
18
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
19
|
+
className?: string;
|
|
20
|
+
}) => (
|
|
21
|
+
<input
|
|
22
|
+
data-testid="edit-input"
|
|
23
|
+
value={value}
|
|
24
|
+
onChange={onChange}
|
|
25
|
+
className={className}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock('@akinon/ui-select', () => ({
|
|
32
|
+
Select: ({ value, onChange, rootClassName, options, ...props }: any) => {
|
|
33
|
+
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
34
|
+
onChange?.(e.target.value);
|
|
35
|
+
};
|
|
36
|
+
return (
|
|
37
|
+
<select
|
|
38
|
+
data-testid="edit-select"
|
|
39
|
+
value={value ?? ''}
|
|
40
|
+
onChange={handleChange}
|
|
41
|
+
className={rootClassName}
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
44
|
+
{options?.map((opt: { label: string; value: string | number }) => (
|
|
45
|
+
<option key={opt.value} value={String(opt.value)}>
|
|
46
|
+
{opt.label}
|
|
47
|
+
</option>
|
|
48
|
+
))}
|
|
49
|
+
</select>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
describe('getEditFieldConfig', () => {
|
|
55
|
+
it('returns config with INPUT type when dataIndex matches string in editDataIndexes', () => {
|
|
56
|
+
const editDataIndexes = ['name', 'value'];
|
|
57
|
+
expect(getEditFieldConfig(editDataIndexes, 'name')).toEqual({
|
|
58
|
+
key: 'name',
|
|
59
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
60
|
+
});
|
|
61
|
+
expect(getEditFieldConfig(editDataIndexes, 'value')).toEqual({
|
|
62
|
+
key: 'value',
|
|
63
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns undefined when dataIndex does not match any string', () => {
|
|
68
|
+
const editDataIndexes = ['name', 'value'];
|
|
69
|
+
expect(getEditFieldConfig(editDataIndexes, 'other')).toBeUndefined();
|
|
70
|
+
expect(getEditFieldConfig(editDataIndexes, '')).toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns config object when dataIndex matches object key (INPUT)', () => {
|
|
74
|
+
const editDataIndexes = [
|
|
75
|
+
{ key: 'name', type: EDIT_FIELD_TYPES.INPUT },
|
|
76
|
+
{
|
|
77
|
+
key: 'value',
|
|
78
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
79
|
+
attributes: { placeholder: 'Value' }
|
|
80
|
+
}
|
|
81
|
+
];
|
|
82
|
+
expect(getEditFieldConfig(editDataIndexes, 'name')).toEqual({
|
|
83
|
+
key: 'name',
|
|
84
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
85
|
+
});
|
|
86
|
+
expect(getEditFieldConfig(editDataIndexes, 'value')).toEqual({
|
|
87
|
+
key: 'value',
|
|
88
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
89
|
+
attributes: { placeholder: 'Value' }
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('returns config object when dataIndex matches object key (SELECT)', () => {
|
|
94
|
+
const selectConfig = {
|
|
95
|
+
key: 'status',
|
|
96
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
97
|
+
attributes: {
|
|
98
|
+
options: [
|
|
99
|
+
{ label: 'Active', value: 'active' },
|
|
100
|
+
{ label: 'Inactive', value: 'inactive' }
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const editDataIndexes = [selectConfig];
|
|
105
|
+
expect(getEditFieldConfig(editDataIndexes, 'status')).toEqual(selectConfig);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('returns undefined for empty editDataIndexes', () => {
|
|
109
|
+
expect(getEditFieldConfig([], 'name')).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('returns first match when multiple entries could match', () => {
|
|
113
|
+
const editDataIndexes = [
|
|
114
|
+
'name',
|
|
115
|
+
{ key: 'name', type: EDIT_FIELD_TYPES.INPUT }
|
|
116
|
+
];
|
|
117
|
+
expect(getEditFieldConfig(editDataIndexes, 'name')).toEqual({
|
|
118
|
+
key: 'name',
|
|
119
|
+
type: EDIT_FIELD_TYPES.INPUT
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('renderEditField', () => {
|
|
125
|
+
it('renders Input for INPUT config', () => {
|
|
126
|
+
const onChange = vi.fn();
|
|
127
|
+
render(
|
|
128
|
+
<>
|
|
129
|
+
{renderEditField({
|
|
130
|
+
config: { key: 'name', type: EDIT_FIELD_TYPES.INPUT },
|
|
131
|
+
value: 'test',
|
|
132
|
+
onChange
|
|
133
|
+
})}
|
|
134
|
+
</>
|
|
135
|
+
);
|
|
136
|
+
const input = screen.getByTestId('edit-input');
|
|
137
|
+
expect(input).toBeInTheDocument();
|
|
138
|
+
expect(input).toHaveValue('test');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('calls onChange with input value when Input changes', () => {
|
|
142
|
+
const onChange = vi.fn();
|
|
143
|
+
render(
|
|
144
|
+
<>
|
|
145
|
+
{renderEditField({
|
|
146
|
+
config: { key: 'name', type: EDIT_FIELD_TYPES.INPUT },
|
|
147
|
+
value: '',
|
|
148
|
+
onChange
|
|
149
|
+
})}
|
|
150
|
+
</>
|
|
151
|
+
);
|
|
152
|
+
const input = screen.getByTestId('edit-input');
|
|
153
|
+
fireEvent.change(input, { target: { value: 'new value' } });
|
|
154
|
+
expect(onChange).toHaveBeenCalledWith('new value');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('passes attributes to Input (className, etc.)', () => {
|
|
158
|
+
render(
|
|
159
|
+
<>
|
|
160
|
+
{renderEditField({
|
|
161
|
+
config: {
|
|
162
|
+
key: 'name',
|
|
163
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
164
|
+
attributes: { className: 'custom-class', placeholder: 'Name' }
|
|
165
|
+
},
|
|
166
|
+
value: '',
|
|
167
|
+
onChange: vi.fn()
|
|
168
|
+
})}
|
|
169
|
+
</>
|
|
170
|
+
);
|
|
171
|
+
const input = screen.getByTestId('edit-input');
|
|
172
|
+
expect(input).toHaveClass('custom-class');
|
|
173
|
+
expect(input).toHaveAttribute('placeholder', 'Name');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('calls attributes.onChange and then onChange when both provided', async () => {
|
|
177
|
+
const user = userEvent.setup();
|
|
178
|
+
const onAttributeChange = vi.fn();
|
|
179
|
+
const onChange = vi.fn();
|
|
180
|
+
render(
|
|
181
|
+
<>
|
|
182
|
+
{renderEditField({
|
|
183
|
+
config: {
|
|
184
|
+
key: 'name',
|
|
185
|
+
type: EDIT_FIELD_TYPES.INPUT,
|
|
186
|
+
attributes: { onChange: onAttributeChange }
|
|
187
|
+
},
|
|
188
|
+
value: '',
|
|
189
|
+
onChange
|
|
190
|
+
})}
|
|
191
|
+
</>
|
|
192
|
+
);
|
|
193
|
+
const input = screen.getByTestId('edit-input');
|
|
194
|
+
await user.type(input, 'x');
|
|
195
|
+
expect(onAttributeChange).toHaveBeenCalled();
|
|
196
|
+
expect(onChange).toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('renders Select for SELECT config', () => {
|
|
200
|
+
const onChange = vi.fn();
|
|
201
|
+
const options = [
|
|
202
|
+
{ label: 'Active', value: 'active' },
|
|
203
|
+
{ label: 'Inactive', value: 'inactive' }
|
|
204
|
+
];
|
|
205
|
+
render(
|
|
206
|
+
<>
|
|
207
|
+
{renderEditField({
|
|
208
|
+
config: {
|
|
209
|
+
key: 'status',
|
|
210
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
211
|
+
attributes: { options }
|
|
212
|
+
},
|
|
213
|
+
value: 'active',
|
|
214
|
+
onChange
|
|
215
|
+
})}
|
|
216
|
+
</>
|
|
217
|
+
);
|
|
218
|
+
const select = screen.getByTestId('edit-select');
|
|
219
|
+
expect(select).toBeInTheDocument();
|
|
220
|
+
expect(select).toHaveValue('active');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('calls onChange when Select value changes', async () => {
|
|
224
|
+
const user = userEvent.setup();
|
|
225
|
+
const onChange = vi.fn();
|
|
226
|
+
const options = [
|
|
227
|
+
{ label: 'Active', value: 'active' },
|
|
228
|
+
{ label: 'Inactive', value: 'inactive' }
|
|
229
|
+
];
|
|
230
|
+
render(
|
|
231
|
+
<>
|
|
232
|
+
{renderEditField({
|
|
233
|
+
config: {
|
|
234
|
+
key: 'status',
|
|
235
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
236
|
+
attributes: { options }
|
|
237
|
+
},
|
|
238
|
+
value: 'active',
|
|
239
|
+
onChange
|
|
240
|
+
})}
|
|
241
|
+
</>
|
|
242
|
+
);
|
|
243
|
+
const select = screen.getByTestId('edit-select');
|
|
244
|
+
await user.selectOptions(select, 'inactive');
|
|
245
|
+
expect(onChange).toHaveBeenCalledWith('inactive');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('passes rootClassName to Select', () => {
|
|
249
|
+
const options = [{ label: 'A', value: 'a' }];
|
|
250
|
+
render(
|
|
251
|
+
<>
|
|
252
|
+
{renderEditField({
|
|
253
|
+
config: {
|
|
254
|
+
key: 'status',
|
|
255
|
+
type: EDIT_FIELD_TYPES.SELECT,
|
|
256
|
+
attributes: { options, rootClassName: 'select-root' }
|
|
257
|
+
},
|
|
258
|
+
value: '',
|
|
259
|
+
onChange: vi.fn()
|
|
260
|
+
})}
|
|
261
|
+
</>
|
|
262
|
+
);
|
|
263
|
+
const select = screen.getByTestId('edit-select');
|
|
264
|
+
expect(select).toHaveClass('select-root');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('defaults to Input for unknown config type', () => {
|
|
268
|
+
render(
|
|
269
|
+
<>
|
|
270
|
+
{renderEditField({
|
|
271
|
+
config: {
|
|
272
|
+
key: 'name',
|
|
273
|
+
type: 'unknown' as any
|
|
274
|
+
},
|
|
275
|
+
value: 'fallback',
|
|
276
|
+
onChange: vi.fn()
|
|
277
|
+
})}
|
|
278
|
+
</>
|
|
279
|
+
);
|
|
280
|
+
expect(screen.getByTestId('edit-input')).toBeInTheDocument();
|
|
281
|
+
expect(screen.getByTestId('edit-input')).toHaveValue('fallback');
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { EditDataIndex, EditDataIndexConfig } from '../../../types';
|
|
3
|
+
type GetEditFieldConfig = (editDataIndexes: EditDataIndex[], dataIndex: string) => EditDataIndexConfig | undefined;
|
|
4
|
+
export declare const getEditFieldConfig: GetEditFieldConfig;
|
|
5
|
+
type RenderEditFieldParams = {
|
|
6
|
+
config: EditDataIndexConfig;
|
|
7
|
+
value: unknown;
|
|
8
|
+
onChange: (value: unknown) => void;
|
|
9
|
+
};
|
|
10
|
+
type RenderEditField = (params: RenderEditFieldParams) => React.ReactNode;
|
|
11
|
+
export declare const renderEditField: RenderEditField;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ai-table/utils/render-edit-fields/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AAGxB,KAAK,kBAAkB,GAAG,CACxB,eAAe,EAAE,aAAa,EAAE,EAChC,SAAS,EAAE,MAAM,KACd,mBAAmB,GAAG,SAAS,CAAC;AAErC,eAAO,MAAM,kBAAkB,EAAE,kBAmBhC,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF,KAAK,eAAe,GAAG,CAAC,MAAM,EAAE,qBAAqB,KAAK,KAAK,CAAC,SAAS,CAAC;AA+D1E,eAAO,MAAM,eAAe,EAAE,eAO7B,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { Input } from '@akinon/ui-input';
|
|
13
|
+
import { Select } from '@akinon/ui-select';
|
|
14
|
+
import cn from 'classnames';
|
|
15
|
+
import * as React from 'react';
|
|
16
|
+
import { EDIT_FIELD_TYPES } from '../../constants';
|
|
17
|
+
export const getEditFieldConfig = (editDataIndexes, dataIndex) => {
|
|
18
|
+
for (const item of editDataIndexes) {
|
|
19
|
+
switch (typeof item) {
|
|
20
|
+
case 'string':
|
|
21
|
+
if (item === dataIndex) {
|
|
22
|
+
return { key: item, type: EDIT_FIELD_TYPES.INPUT };
|
|
23
|
+
}
|
|
24
|
+
break;
|
|
25
|
+
default:
|
|
26
|
+
if (item.key === dataIndex) {
|
|
27
|
+
return item;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
};
|
|
33
|
+
const renderInputField = ({ config, value, onChange }) => {
|
|
34
|
+
var _a;
|
|
35
|
+
const _b = (_a = config.attributes) !== null && _a !== void 0 ? _a : {}, { onChange: onAttributeChange, className } = _b, restAttributes = __rest(_b, ["onChange", "className"]);
|
|
36
|
+
const handleChange = (e) => {
|
|
37
|
+
onAttributeChange === null || onAttributeChange === void 0 ? void 0 : onAttributeChange(e);
|
|
38
|
+
onChange(e.target.value);
|
|
39
|
+
};
|
|
40
|
+
const allClassNames = cn('w-44 h-9', className);
|
|
41
|
+
return (React.createElement(Input, Object.assign({ className: allClassNames, onChange: handleChange, value: value }, restAttributes)));
|
|
42
|
+
};
|
|
43
|
+
const renderSelectField = ({ config, value, onChange }) => {
|
|
44
|
+
const _a = config.attributes, { onChange: onAttributeChange, rootClassName } = _a, restAttributes = __rest(_a, ["onChange", "rootClassName"]);
|
|
45
|
+
const handleChange = (selectedValue, option) => {
|
|
46
|
+
onAttributeChange === null || onAttributeChange === void 0 ? void 0 : onAttributeChange(selectedValue, option);
|
|
47
|
+
onChange(selectedValue);
|
|
48
|
+
};
|
|
49
|
+
const allRootClassNames = cn('w-44', rootClassName);
|
|
50
|
+
return (React.createElement(Select, Object.assign({ rootClassName: allRootClassNames, onChange: handleChange, value: value }, restAttributes)));
|
|
51
|
+
};
|
|
52
|
+
const FIELD_RENDERERS = {
|
|
53
|
+
[EDIT_FIELD_TYPES.INPUT]: renderInputField,
|
|
54
|
+
[EDIT_FIELD_TYPES.SELECT]: renderSelectField
|
|
55
|
+
};
|
|
56
|
+
export const renderEditField = ({ config, value, onChange }) => {
|
|
57
|
+
var _a;
|
|
58
|
+
const renderer = (_a = FIELD_RENDERERS[config.type]) !== null && _a !== void 0 ? _a : renderInputField;
|
|
59
|
+
return renderer({ config, value, onChange });
|
|
60
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { InputProps } from '@akinon/ui-input';
|
|
1
2
|
import type { ModalProps } from '@akinon/ui-modal';
|
|
2
|
-
import {
|
|
3
|
+
import type { SelectProps } from '@akinon/ui-select';
|
|
4
|
+
import { EDIT_FIELD_TYPES, MAPPER_ITEM_TYPES } from '../ai-table/constants';
|
|
3
5
|
type MapperItemTypes = (typeof MAPPER_ITEM_TYPES)[keyof typeof MAPPER_ITEM_TYPES];
|
|
4
6
|
export type HandleMapperItemChange = ({ index, key, value }: {
|
|
5
7
|
index: number;
|
|
@@ -52,6 +54,19 @@ export interface MapperConfig {
|
|
|
52
54
|
*/
|
|
53
55
|
dataIndex: string;
|
|
54
56
|
}
|
|
57
|
+
type EditDataIndexBase = {
|
|
58
|
+
key: string;
|
|
59
|
+
};
|
|
60
|
+
export type EditDataIndexInput = EditDataIndexBase & {
|
|
61
|
+
type: typeof EDIT_FIELD_TYPES.INPUT;
|
|
62
|
+
attributes?: InputProps;
|
|
63
|
+
};
|
|
64
|
+
export type EditDataIndexSelect = EditDataIndexBase & {
|
|
65
|
+
type: typeof EDIT_FIELD_TYPES.SELECT;
|
|
66
|
+
attributes: SelectProps;
|
|
67
|
+
};
|
|
68
|
+
export type EditDataIndexConfig = EditDataIndexInput | EditDataIndexSelect;
|
|
69
|
+
export type EditDataIndex = string | EditDataIndexConfig;
|
|
55
70
|
export interface TableBaseProps {
|
|
56
71
|
/**
|
|
57
72
|
* The unique key field name for identifying rows
|
|
@@ -70,13 +85,14 @@ export interface TableBaseProps {
|
|
|
70
85
|
*/
|
|
71
86
|
filters: Filter[];
|
|
72
87
|
/**
|
|
73
|
-
* Array of data keys that should be editable in the table
|
|
88
|
+
* Array of data keys or config objects that should be editable in the table.
|
|
89
|
+
* Use a string for default Input rendering, or an object with { key, type, attributes } for custom components.
|
|
74
90
|
*/
|
|
75
|
-
editDataIndexes?:
|
|
91
|
+
editDataIndexes?: EditDataIndex[];
|
|
76
92
|
/**
|
|
77
93
|
* Callback fired when a row is edited. Returns the row ID and the modified data. To work properly, editDataIndexes prop must be provided
|
|
78
94
|
*/
|
|
79
|
-
onEdit?: (pk: number | string, payload: Record<string,
|
|
95
|
+
onEdit?: (pk: number | string, payload: Record<string, unknown>) => void;
|
|
80
96
|
/**
|
|
81
97
|
* Function to render custom action buttons for each row. Receives the row ID.
|
|
82
98
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
|