@axinom/mosaic-ui 0.64.0-rc.14 → 0.64.0-rc.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-ui",
3
- "version": "0.64.0-rc.14",
3
+ "version": "0.64.0-rc.16",
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": "2d29bbc90e5ee2b89810523b9afb2eac1647bd07"
115
+ "gitHead": "36be53de98dc95441930f738df41d7fb21192bde"
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 data-testid="FieldSelection">{children}</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: jest.fn(),
33
- setFieldTouched: jest.fn(),
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 { container, getByTestId } = render(
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(container.firstChild).toBeEmptyDOMElement();
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 { container, getByTestId } = render(
160
- BulkEditFormFieldsConfigConverter(config),
161
- );
217
+ const { getByTestId } = render(BulkEditFormFieldsConfigConverter(config));
162
218
 
163
219
  expect(getByTestId('FieldSelection')).toBeInTheDocument();
164
- expect(container.firstChild).toBeEmptyDOMElement();
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
- Array: CustomTagsField, // Map array types to CustomTagsComponent
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
- ? 'Array' // Use 'Array' as the key for array types
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
- acc = {
24
- ...acc,
25
- [field.originalFieldName]: fieldValues.map((item: string) => ({
26
- name: item,
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
- acc = {
40
- ...acc,
41
- [field.originalFieldName]: fieldValues.map((item: string) => ({
42
- name: item,
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>);
@@ -1,3 +1,4 @@
1
+ export { EnumType as BulkEditEnumType } from 'json-to-graphql-query';
1
2
  export type * from './BulkEdit.model';
2
3
  export { BulkEditContext, BulkEditContextType } from './BulkEditContext';
3
4
  export {
@@ -2,6 +2,7 @@ export {
2
2
  BulkEditConfig,
3
3
  BulkEditContext,
4
4
  BulkEditContextType,
5
+ BulkEditEnumType,
5
6
  BulkEditFieldConfig,
6
7
  BulkEditFieldConfigMap,
7
8
  BulkEditFormFieldsConfigConverter,
@@ -32,46 +32,46 @@
32
32
  background-color: white;
33
33
 
34
34
  z-index: 2;
35
- }
36
- }
37
35
 
38
- .dropDown {
39
- height: unset;
40
- width: unset;
36
+ .dropDown {
37
+ height: unset;
38
+ width: unset;
41
39
 
42
- display: grid;
43
- grid-template-columns: 1fr 40px;
44
- grid-template-rows: 1fr;
40
+ display: grid;
41
+ grid-template-columns: 1fr 40px;
42
+ grid-template-rows: 1fr;
45
43
 
46
- // icon
47
- div:first-child {
48
- display: grid;
49
- align-items: center;
50
- grid-row: 1 / span 1;
51
- justify-items: start;
52
- grid-column: 2 / span 1;
53
- padding: 0px;
44
+ // icon
45
+ div:first-child {
46
+ display: grid;
47
+ align-items: center;
48
+ grid-row: 1 / span 1;
49
+ justify-items: start;
50
+ grid-column: 2 / span 1;
51
+ padding: 0px;
54
52
 
55
- svg,
56
- img {
57
- height: 30px;
58
- }
59
- }
53
+ svg,
54
+ img {
55
+ height: 30px;
56
+ }
57
+ }
60
58
 
61
- // label
62
- div:last-child {
63
- display: grid;
64
- align-items: center;
65
- grid-column: 1 / span 1;
66
- justify-items: start;
59
+ // label
60
+ div:last-child {
61
+ display: grid;
62
+ align-items: center;
63
+ grid-column: 1 / span 1;
64
+ justify-items: start;
67
65
 
68
- padding: 0px 0px 0px 20px;
66
+ padding: 0px 0px 0px 20px;
69
67
 
70
- span {
71
- height: 30px;
68
+ span {
69
+ height: 30px;
72
70
 
73
- display: grid;
74
- align-items: center;
71
+ display: grid;
72
+ align-items: center;
73
+ }
74
+ }
75
75
  }
76
76
  }
77
77
  }