@axinom/mosaic-ui 0.64.0-rc.7 → 0.65.0-rc.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/dist/components/Explorer/BulkEdit/FormFieldsConfigConverter.d.ts +1 -1
- package/dist/components/Explorer/BulkEdit/GenerateMutation.d.ts.map +1 -1
- package/dist/components/Explorer/BulkEdit/index.d.ts +1 -0
- package/dist/components/Explorer/BulkEdit/index.d.ts.map +1 -1
- package/dist/components/Explorer/QuickEdit/useQuickEdit.d.ts.map +1 -1
- package/dist/components/Explorer/index.d.ts +1 -1
- package/dist/components/Explorer/index.d.ts.map +1 -1
- package/dist/components/InfoTooltip/InfoTooltip.d.ts.map +1 -1
- package/dist/components/InlineMenu/InlineMenu.d.ts.map +1 -1
- package/dist/components/List/List.d.ts +6 -1
- package/dist/components/List/List.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRow.d.ts +5 -20
- package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/components/List/ListRowRenderer/ListRowRenderer.d.ts +22 -0
- package/dist/components/List/ListRowRenderer/ListRowRenderer.d.ts.map +1 -0
- package/dist/index.es.js +4 -5
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Explorer/BulkEdit/FormFieldsConfigConverter.spec.tsx +158 -14
- package/src/components/Explorer/BulkEdit/FormFieldsConfigConverter.tsx +2 -2
- package/src/components/Explorer/BulkEdit/GenerateMutation.spec.tsx +209 -0
- package/src/components/Explorer/BulkEdit/GenerateMutation.tsx +78 -12
- package/src/components/Explorer/BulkEdit/index.ts +1 -0
- package/src/components/Explorer/QuickEdit/useQuickEdit.tsx +1 -0
- package/src/components/Explorer/index.ts +1 -0
- package/src/components/InfoTooltip/InfoTooltip.scss +65 -65
- package/src/components/InfoTooltip/InfoTooltip.tsx +35 -31
- package/src/components/InlineMenu/InlineMenu.tsx +39 -33
- package/src/components/List/List.spec.tsx +209 -1
- package/src/components/List/List.stories.tsx +76 -0
- package/src/components/List/List.tsx +52 -31
- package/src/components/List/ListRow/ListRow.scss +5 -0
- package/src/components/List/ListRow/ListRow.spec.tsx +97 -155
- package/src/components/List/ListRow/ListRow.tsx +31 -57
- package/src/components/List/ListRowRenderer/ListRowRenderer.spec.tsx +353 -0
- package/src/components/List/ListRowRenderer/ListRowRenderer.tsx +68 -0
- package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.scss +32 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0-rc.0",
|
|
4
4
|
"description": "UI components for building Axinom Mosaic applications",
|
|
5
5
|
"author": "Axinom",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -112,5 +112,5 @@
|
|
|
112
112
|
"publishConfig": {
|
|
113
113
|
"access": "public"
|
|
114
114
|
},
|
|
115
|
-
"gitHead": "
|
|
115
|
+
"gitHead": "51cebd936bdc8ab8ca7ef6f35a1152ba64c0fefa"
|
|
116
116
|
}
|
|
@@ -17,24 +17,43 @@ jest.mock('../../FormElements', () => ({
|
|
|
17
17
|
}));
|
|
18
18
|
|
|
19
19
|
jest.mock('../../FieldSelection', () => ({
|
|
20
|
-
FieldSelection: jest.fn(({ children }) => (
|
|
21
|
-
<div
|
|
20
|
+
FieldSelection: jest.fn(({ children, onFieldRemoved, onFieldAdded }) => (
|
|
21
|
+
<div
|
|
22
|
+
data-testid="FieldSelection"
|
|
23
|
+
data-on-field-removed={!!onFieldRemoved}
|
|
24
|
+
data-on-field-added={!!onFieldAdded}
|
|
25
|
+
>
|
|
26
|
+
{children}
|
|
27
|
+
</div>
|
|
22
28
|
)),
|
|
23
29
|
}));
|
|
24
30
|
|
|
31
|
+
const mockSetFieldValue = jest.fn();
|
|
32
|
+
const mockSetFieldTouched = jest.fn();
|
|
33
|
+
const mockSetErrors = jest.fn();
|
|
34
|
+
const mockValidateForm = jest.fn();
|
|
35
|
+
|
|
25
36
|
jest.mock('formik', () => ({
|
|
26
|
-
Field: jest.fn(({ name, label, as: Component }) => (
|
|
27
|
-
<div data-testid={`Field-${name}`}>
|
|
37
|
+
Field: jest.fn(({ name, label, as: Component, validate }) => (
|
|
38
|
+
<div data-testid={`Field-${name}`} data-has-validation={!!validate}>
|
|
28
39
|
<Component name={name} label={label} />
|
|
29
40
|
</div>
|
|
30
41
|
)),
|
|
31
42
|
useFormikContext: jest.fn(() => ({
|
|
32
|
-
setFieldValue:
|
|
33
|
-
setFieldTouched:
|
|
43
|
+
setFieldValue: mockSetFieldValue,
|
|
44
|
+
setFieldTouched: mockSetFieldTouched,
|
|
45
|
+
setErrors: mockSetErrors,
|
|
46
|
+
errors: {},
|
|
47
|
+
validateForm: mockValidateForm,
|
|
48
|
+
values: {},
|
|
34
49
|
})),
|
|
35
50
|
}));
|
|
36
51
|
|
|
37
52
|
describe('BulkEditFormFieldsConfigConverter', () => {
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
jest.clearAllMocks();
|
|
55
|
+
});
|
|
56
|
+
|
|
38
57
|
it('renders a Formik Field wrapping SingleLineTextField for String type', () => {
|
|
39
58
|
const config: BulkEditFieldConfigMap = {
|
|
40
59
|
title: {
|
|
@@ -48,7 +67,19 @@ describe('BulkEditFormFieldsConfigConverter', () => {
|
|
|
48
67
|
const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
|
|
49
68
|
|
|
50
69
|
expect(getByTestId('Field-title')).toBeInTheDocument();
|
|
70
|
+
expect(getByTestId('Field-title')).toHaveAttribute(
|
|
71
|
+
'data-has-validation',
|
|
72
|
+
'true',
|
|
73
|
+
);
|
|
51
74
|
expect(getByTestId('SingleLineTextField')).toHaveTextContent('title-Title');
|
|
75
|
+
expect(getByTestId('FieldSelection')).toHaveAttribute(
|
|
76
|
+
'data-on-field-removed',
|
|
77
|
+
'true',
|
|
78
|
+
);
|
|
79
|
+
expect(getByTestId('FieldSelection')).toHaveAttribute(
|
|
80
|
+
'data-on-field-added',
|
|
81
|
+
'true',
|
|
82
|
+
);
|
|
52
83
|
});
|
|
53
84
|
|
|
54
85
|
it('renders a Formik Field wrapping CheckboxField for Boolean type', () => {
|
|
@@ -64,6 +95,10 @@ describe('BulkEditFormFieldsConfigConverter', () => {
|
|
|
64
95
|
const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
|
|
65
96
|
|
|
66
97
|
expect(getByTestId('Field-isArchived')).toBeInTheDocument();
|
|
98
|
+
expect(getByTestId('Field-isArchived')).toHaveAttribute(
|
|
99
|
+
'data-has-validation',
|
|
100
|
+
'true',
|
|
101
|
+
);
|
|
67
102
|
expect(getByTestId('CheckboxField')).toHaveTextContent(
|
|
68
103
|
'isArchived-Is Archived',
|
|
69
104
|
);
|
|
@@ -86,6 +121,10 @@ describe('BulkEditFormFieldsConfigConverter', () => {
|
|
|
86
121
|
const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
|
|
87
122
|
|
|
88
123
|
expect(getByTestId('Field-tags')).toBeInTheDocument();
|
|
124
|
+
expect(getByTestId('Field-tags')).toHaveAttribute(
|
|
125
|
+
'data-has-validation',
|
|
126
|
+
'true',
|
|
127
|
+
);
|
|
89
128
|
expect(getByTestId('CustomTagsField')).toHaveTextContent('tags-Tags');
|
|
90
129
|
});
|
|
91
130
|
|
|
@@ -127,6 +166,20 @@ describe('BulkEditFormFieldsConfigConverter', () => {
|
|
|
127
166
|
|
|
128
167
|
expect(getByTestId('Field-tags')).toBeInTheDocument();
|
|
129
168
|
expect(getByTestId('CustomTagsField')).toHaveTextContent('tags-Tags');
|
|
169
|
+
|
|
170
|
+
// Check that all fields have validation
|
|
171
|
+
expect(getByTestId('Field-title')).toHaveAttribute(
|
|
172
|
+
'data-has-validation',
|
|
173
|
+
'true',
|
|
174
|
+
);
|
|
175
|
+
expect(getByTestId('Field-isArchived')).toHaveAttribute(
|
|
176
|
+
'data-has-validation',
|
|
177
|
+
'true',
|
|
178
|
+
);
|
|
179
|
+
expect(getByTestId('Field-tags')).toHaveAttribute(
|
|
180
|
+
'data-has-validation',
|
|
181
|
+
'true',
|
|
182
|
+
);
|
|
130
183
|
});
|
|
131
184
|
|
|
132
185
|
it('logs a warning for unsupported field types', () => {
|
|
@@ -140,15 +193,20 @@ describe('BulkEditFormFieldsConfigConverter', () => {
|
|
|
140
193
|
},
|
|
141
194
|
};
|
|
142
195
|
|
|
143
|
-
const {
|
|
144
|
-
BulkEditFormFieldsConfigConverter(config),
|
|
145
|
-
);
|
|
196
|
+
const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
|
|
146
197
|
|
|
147
198
|
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
148
199
|
'Bulk Edit: No component found for field type: UnsupportedType',
|
|
149
200
|
);
|
|
150
201
|
expect(getByTestId('FieldSelection')).toBeInTheDocument();
|
|
151
|
-
expect(
|
|
202
|
+
expect(getByTestId('FieldSelection')).toHaveAttribute(
|
|
203
|
+
'data-on-field-removed',
|
|
204
|
+
'true',
|
|
205
|
+
);
|
|
206
|
+
expect(getByTestId('FieldSelection')).toHaveAttribute(
|
|
207
|
+
'data-on-field-added',
|
|
208
|
+
'true',
|
|
209
|
+
);
|
|
152
210
|
|
|
153
211
|
consoleWarnSpy.mockRestore();
|
|
154
212
|
});
|
|
@@ -156,11 +214,97 @@ describe('BulkEditFormFieldsConfigConverter', () => {
|
|
|
156
214
|
it('handles empty configuration gracefully', () => {
|
|
157
215
|
const config: BulkEditFieldConfigMap = {};
|
|
158
216
|
|
|
159
|
-
const {
|
|
160
|
-
BulkEditFormFieldsConfigConverter(config),
|
|
161
|
-
);
|
|
217
|
+
const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
|
|
162
218
|
|
|
163
219
|
expect(getByTestId('FieldSelection')).toBeInTheDocument();
|
|
164
|
-
expect(
|
|
220
|
+
expect(getByTestId('FieldSelection')).toHaveAttribute(
|
|
221
|
+
'data-on-field-removed',
|
|
222
|
+
'true',
|
|
223
|
+
);
|
|
224
|
+
expect(getByTestId('FieldSelection')).toHaveAttribute(
|
|
225
|
+
'data-on-field-added',
|
|
226
|
+
'true',
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('validates field values correctly', () => {
|
|
231
|
+
const config: BulkEditFieldConfigMap = {
|
|
232
|
+
title: {
|
|
233
|
+
type: 'String',
|
|
234
|
+
label: 'Title',
|
|
235
|
+
originalFieldName: 'title',
|
|
236
|
+
action: 'set',
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
render(BulkEditFormFieldsConfigConverter(config));
|
|
241
|
+
|
|
242
|
+
// Get the validation function from the Field mock
|
|
243
|
+
const mockField = jest.mocked(jest.requireMock('formik').Field);
|
|
244
|
+
const fieldCall = mockField.mock.calls[0][0] as any;
|
|
245
|
+
const validateFn = fieldCall.validate;
|
|
246
|
+
|
|
247
|
+
// Test validation for empty values
|
|
248
|
+
expect(validateFn(null)).toBe('Please provide a value');
|
|
249
|
+
expect(validateFn(undefined)).toBe('Please provide a value');
|
|
250
|
+
expect(validateFn('')).toBe('Please provide a value');
|
|
251
|
+
expect(validateFn([])).toBe('Please provide a value');
|
|
252
|
+
|
|
253
|
+
// Test validation for valid values
|
|
254
|
+
expect(validateFn('valid value')).toBeUndefined();
|
|
255
|
+
expect(validateFn(['valid', 'array'])).toBeUndefined();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('supports BigFloat type with SingleLineTextField', () => {
|
|
259
|
+
const config: BulkEditFieldConfigMap = {
|
|
260
|
+
price: {
|
|
261
|
+
type: 'BigFloat',
|
|
262
|
+
label: 'Price',
|
|
263
|
+
originalFieldName: 'price',
|
|
264
|
+
action: 'set',
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
|
|
269
|
+
|
|
270
|
+
expect(getByTestId('Field-price')).toBeInTheDocument();
|
|
271
|
+
expect(getByTestId('SingleLineTextField')).toHaveTextContent('price-Price');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('calls useFormikContext hooks correctly', () => {
|
|
275
|
+
const config: BulkEditFieldConfigMap = {
|
|
276
|
+
title: {
|
|
277
|
+
type: 'String',
|
|
278
|
+
label: 'Title',
|
|
279
|
+
originalFieldName: 'title',
|
|
280
|
+
action: 'set',
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
render(BulkEditFormFieldsConfigConverter(config));
|
|
285
|
+
|
|
286
|
+
// Verify that useFormikContext was called
|
|
287
|
+
expect(mockSetFieldValue).toBeDefined();
|
|
288
|
+
expect(mockSetFieldTouched).toBeDefined();
|
|
289
|
+
expect(mockSetErrors).toBeDefined();
|
|
290
|
+
expect(mockValidateForm).toBeDefined();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('handles field clearing functionality through FieldSelection callbacks', () => {
|
|
294
|
+
const config: BulkEditFieldConfigMap = {
|
|
295
|
+
title: {
|
|
296
|
+
type: 'String',
|
|
297
|
+
label: 'Title',
|
|
298
|
+
originalFieldName: 'title',
|
|
299
|
+
action: 'set',
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
|
|
304
|
+
const fieldSelection = getByTestId('FieldSelection');
|
|
305
|
+
|
|
306
|
+
// Verify that the FieldSelection component receives the callback props
|
|
307
|
+
expect(fieldSelection).toHaveAttribute('data-on-field-removed', 'true');
|
|
308
|
+
expect(fieldSelection).toHaveAttribute('data-on-field-added', 'true');
|
|
165
309
|
});
|
|
166
310
|
});
|
|
@@ -13,7 +13,7 @@ export const defaultComponentMap = {
|
|
|
13
13
|
String: SingleLineTextField,
|
|
14
14
|
BigFloat: SingleLineTextField,
|
|
15
15
|
Boolean: CheckboxField,
|
|
16
|
-
|
|
16
|
+
Tags: CustomTagsField, // Map array types to CustomTagsComponent
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export const BulkEditFormFieldsConfigConverter = (
|
|
@@ -73,7 +73,7 @@ export const BulkEditFormFieldsConfigConverter = (
|
|
|
73
73
|
|
|
74
74
|
// Determine the type of the field
|
|
75
75
|
const fieldType = Array.isArray(fieldConfig.type)
|
|
76
|
-
? '
|
|
76
|
+
? 'Tags' // Treat single String type as Tags
|
|
77
77
|
: fieldConfig.type;
|
|
78
78
|
|
|
79
79
|
const Component =
|
|
@@ -138,4 +138,213 @@ describe('generateBulkEditMutation', () => {
|
|
|
138
138
|
),
|
|
139
139
|
).toThrow('No valid fields to generate mutation');
|
|
140
140
|
});
|
|
141
|
+
|
|
142
|
+
it('should include filter parameter when provided', () => {
|
|
143
|
+
const sampleValues = {
|
|
144
|
+
altText: 'Sample Alt Text',
|
|
145
|
+
};
|
|
146
|
+
const filter = { id: { in: ['1', '2', '3'] } };
|
|
147
|
+
|
|
148
|
+
const result = generateBulkEditMutation(
|
|
149
|
+
BulkEditImagesAsyncFormFieldsConfig,
|
|
150
|
+
sampleValues,
|
|
151
|
+
filter,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
expect(result).toBe(
|
|
155
|
+
`mutation { bulkEditImagesAsync (set: {altText: "Sample Alt Text"}, filter: {id: {in: ["1", "2", "3"]}}) { filterMatchedIds } }`,
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should use custom key names when provided in config', () => {
|
|
160
|
+
const configWithCustomKeys: BulkEditConfig = {
|
|
161
|
+
mutation: 'bulkEditCustom',
|
|
162
|
+
keys: {
|
|
163
|
+
add: 'customAdd',
|
|
164
|
+
remove: 'customRemove',
|
|
165
|
+
set: 'customSet',
|
|
166
|
+
filter: 'customFilter',
|
|
167
|
+
},
|
|
168
|
+
fields: {
|
|
169
|
+
tagsAdd: {
|
|
170
|
+
type: [{ name: 'String!' }],
|
|
171
|
+
label: 'Tags Add',
|
|
172
|
+
originalFieldName: 'tags',
|
|
173
|
+
action: 'customAdd',
|
|
174
|
+
},
|
|
175
|
+
tagsRemove: {
|
|
176
|
+
type: [{ name: 'String!' }],
|
|
177
|
+
label: 'Tags Remove',
|
|
178
|
+
originalFieldName: 'tags',
|
|
179
|
+
action: 'customRemove',
|
|
180
|
+
},
|
|
181
|
+
title: {
|
|
182
|
+
type: 'String',
|
|
183
|
+
label: 'Title',
|
|
184
|
+
originalFieldName: 'title',
|
|
185
|
+
action: 'customSet',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const sampleValues = {
|
|
191
|
+
tagsAdd: ['tag1'],
|
|
192
|
+
tagsRemove: ['tag2'],
|
|
193
|
+
title: 'Custom Title',
|
|
194
|
+
};
|
|
195
|
+
const filter = { active: true };
|
|
196
|
+
|
|
197
|
+
const result = generateBulkEditMutation(
|
|
198
|
+
configWithCustomKeys,
|
|
199
|
+
sampleValues,
|
|
200
|
+
filter,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
expect(result).toBe(
|
|
204
|
+
`mutation { bulkEditCustom (customAdd: {tags: [{name: "tag1"}]}, customRemove: {tags: [{name: "tag2"}]}, customSet: {title: "Custom Title"}, customFilter: {active: true}) { filterMatchedIds } }`,
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should handle object values in related entities arrays', () => {
|
|
209
|
+
const configWithObjectValues: BulkEditConfig = {
|
|
210
|
+
mutation: 'bulkEditObjects',
|
|
211
|
+
fields: {
|
|
212
|
+
categoriesAdd: {
|
|
213
|
+
type: [{ id: 'String!', name: 'String!' }],
|
|
214
|
+
label: 'Categories Add',
|
|
215
|
+
originalFieldName: 'categories',
|
|
216
|
+
action: 'relatedEntitiesToAdd',
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const sampleValues = {
|
|
222
|
+
categoriesAdd: [
|
|
223
|
+
{ id: '1', name: 'Category 1' },
|
|
224
|
+
{ id: '2', name: 'Category 2' },
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const result = generateBulkEditMutation(
|
|
229
|
+
configWithObjectValues,
|
|
230
|
+
sampleValues,
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
expect(result).toBe(
|
|
234
|
+
`mutation { bulkEditObjects (relatedEntitiesToAdd: {categories: [{id: "1", name: "Category 1"}, {id: "2", name: "Category 2"}]}) { filterMatchedIds } }`,
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should handle null and undefined values correctly', () => {
|
|
239
|
+
const sampleValues = {
|
|
240
|
+
altText: null,
|
|
241
|
+
focalX: undefined,
|
|
242
|
+
title: '',
|
|
243
|
+
isArchived: false,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const result = generateBulkEditMutation(
|
|
247
|
+
BulkEditImagesAsyncFormFieldsConfig,
|
|
248
|
+
sampleValues,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// Note: null and undefined values are excluded by the implementation's fieldValues !== undefined && fieldValues !== null check
|
|
252
|
+
// Only non-null/undefined values are included in the 'set' operation
|
|
253
|
+
expect(result).toBe(
|
|
254
|
+
`mutation { bulkEditImagesAsync (set: {isArchived: false, title: ""}) { filterMatchedIds } }`,
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should include null values explicitly in set operations when they are the only value', () => {
|
|
259
|
+
const sampleValues = {
|
|
260
|
+
altText: null,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// This test demonstrates the current behavior - null values are excluded
|
|
264
|
+
// This might be a bug in the implementation, as setting a field to null might be intentional
|
|
265
|
+
expect(() =>
|
|
266
|
+
generateBulkEditMutation(
|
|
267
|
+
BulkEditImagesAsyncFormFieldsConfig,
|
|
268
|
+
sampleValues,
|
|
269
|
+
),
|
|
270
|
+
).toThrow('No valid fields to generate mutation');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('should handle zero and false values correctly (they should be included)', () => {
|
|
274
|
+
const sampleValues = {
|
|
275
|
+
focalX: 0,
|
|
276
|
+
focalY: 0,
|
|
277
|
+
isArchived: false,
|
|
278
|
+
title: '',
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const result = generateBulkEditMutation(
|
|
282
|
+
BulkEditImagesAsyncFormFieldsConfig,
|
|
283
|
+
sampleValues,
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
expect(result).toBe(
|
|
287
|
+
`mutation { bulkEditImagesAsync (set: {focalX: 0, focalY: 0, isArchived: false, title: ""}) { filterMatchedIds } }`,
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should handle empty arrays in related entities', () => {
|
|
292
|
+
const sampleValues = {
|
|
293
|
+
imagesTagsAdd: [],
|
|
294
|
+
imagesTagsRemove: ['tag1'],
|
|
295
|
+
altText: 'Test',
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const result = generateBulkEditMutation(
|
|
299
|
+
BulkEditImagesAsyncFormFieldsConfig,
|
|
300
|
+
sampleValues,
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// Note: Empty arrays are truthy, so they get included in the mutation
|
|
304
|
+
expect(result).toBe(
|
|
305
|
+
`mutation { bulkEditImagesAsync (relatedEntitiesToAdd: {imagesTags: []}, relatedEntitiesToRemove: {imagesTags: [{name: "tag1"}]}, set: {altText: "Test"}) { filterMatchedIds } }`,
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should handle mixed string and object values in the same field', () => {
|
|
310
|
+
const configWithMixedValues: BulkEditConfig = {
|
|
311
|
+
mutation: 'bulkEditMixed',
|
|
312
|
+
fields: {
|
|
313
|
+
itemsAdd: {
|
|
314
|
+
type: [{ id: 'String!' }],
|
|
315
|
+
label: 'Items Add',
|
|
316
|
+
originalFieldName: 'items',
|
|
317
|
+
action: 'relatedEntitiesToAdd',
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const sampleValues = {
|
|
323
|
+
itemsAdd: ['string-id', { id: 'object-id', extra: 'data' }],
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const result = generateBulkEditMutation(
|
|
327
|
+
configWithMixedValues,
|
|
328
|
+
sampleValues,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
expect(result).toBe(
|
|
332
|
+
`mutation { bulkEditMixed (relatedEntitiesToAdd: {items: [{id: "string-id"}, {id: "object-id", extra: "data"}]}) { filterMatchedIds } }`,
|
|
333
|
+
);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('should throw error when all provided values are falsy but not explicitly null/false', () => {
|
|
337
|
+
const sampleValues = {
|
|
338
|
+
imagesTagsAdd: undefined,
|
|
339
|
+
imagesTagsRemove: null,
|
|
340
|
+
altText: undefined,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
expect(() =>
|
|
344
|
+
generateBulkEditMutation(
|
|
345
|
+
BulkEditImagesAsyncFormFieldsConfig,
|
|
346
|
+
sampleValues,
|
|
347
|
+
),
|
|
348
|
+
).toThrow('No valid fields to generate mutation');
|
|
349
|
+
});
|
|
141
350
|
});
|
|
@@ -20,12 +20,45 @@ export const generateBulkEditMutation = <T extends Data>(
|
|
|
20
20
|
const fieldValues = values[key];
|
|
21
21
|
|
|
22
22
|
if (fieldValues) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
if (Array.isArray(fieldValues)) {
|
|
24
|
+
return {
|
|
25
|
+
...acc,
|
|
26
|
+
[field.originalFieldName]: [
|
|
27
|
+
...(Array.isArray(acc[field.originalFieldName])
|
|
28
|
+
? (acc[field.originalFieldName] as unknown[])
|
|
29
|
+
: []),
|
|
30
|
+
...fieldValues.map((item: string | object) => {
|
|
31
|
+
if (typeof item === 'object') {
|
|
32
|
+
return { ...item };
|
|
33
|
+
}
|
|
34
|
+
if (
|
|
35
|
+
Array.isArray(field.type) &&
|
|
36
|
+
field.type.length > 0 &&
|
|
37
|
+
field.type[0] &&
|
|
38
|
+
typeof field.type[0] === 'object' &&
|
|
39
|
+
Object.keys(field.type[0]).length > 0
|
|
40
|
+
) {
|
|
41
|
+
const key = Object.keys(field.type[0])[0];
|
|
42
|
+
return {
|
|
43
|
+
[key]: item,
|
|
44
|
+
};
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Invalid field type. Check generated bulk edit configuration. Generating graphql mutation for field: ${JSON.stringify(
|
|
48
|
+
field,
|
|
49
|
+
)}`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}),
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Assignment field is not an array. Generating graphql mutation for field: ${JSON.stringify(
|
|
58
|
+
field,
|
|
59
|
+
)}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
29
62
|
}
|
|
30
63
|
return acc;
|
|
31
64
|
}, {} as Record<string, unknown>);
|
|
@@ -36,12 +69,45 @@ export const generateBulkEditMutation = <T extends Data>(
|
|
|
36
69
|
const fieldValues = values[key];
|
|
37
70
|
|
|
38
71
|
if (fieldValues) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
72
|
+
if (Array.isArray(fieldValues)) {
|
|
73
|
+
return {
|
|
74
|
+
...acc,
|
|
75
|
+
[field.originalFieldName]: [
|
|
76
|
+
...(Array.isArray(acc[field.originalFieldName])
|
|
77
|
+
? (acc[field.originalFieldName] as unknown[])
|
|
78
|
+
: []),
|
|
79
|
+
...fieldValues.map((item: string | object) => {
|
|
80
|
+
if (typeof item === 'object') {
|
|
81
|
+
return { ...item };
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
Array.isArray(field.type) &&
|
|
85
|
+
field.type.length > 0 &&
|
|
86
|
+
field.type[0] &&
|
|
87
|
+
typeof field.type[0] === 'object' &&
|
|
88
|
+
Object.keys(field.type[0]).length > 0
|
|
89
|
+
) {
|
|
90
|
+
const key = Object.keys(field.type[0])[0];
|
|
91
|
+
return {
|
|
92
|
+
[key]: item,
|
|
93
|
+
};
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Invalid field type. Check generated bulk edit configuration. Generating graphql mutation for field: ${JSON.stringify(
|
|
97
|
+
field,
|
|
98
|
+
)}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}),
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Assignment field is not an array. Generating graphql mutation for field: ${JSON.stringify(
|
|
107
|
+
field,
|
|
108
|
+
)}`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
45
111
|
}
|
|
46
112
|
return acc;
|
|
47
113
|
}, {} as Record<string, unknown>);
|
|
@@ -142,6 +142,7 @@ export const useQuickEdit = <T extends Data>({
|
|
|
142
142
|
: PageHeaderActionType.Context,
|
|
143
143
|
onClick: async () => {
|
|
144
144
|
await saveCallbackRef.current?.();
|
|
145
|
+
setSelectedItem({ ...selectedItem });
|
|
145
146
|
setCurrentRegistration(registration);
|
|
146
147
|
updateDetailsLink(selectedItem, registration);
|
|
147
148
|
},
|