box-ui-elements 24.0.0-beta.4 → 24.0.0-beta.6
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/explorer.css +1 -1
- package/dist/explorer.js +1 -1
- package/dist/openwith.js +1 -1
- package/dist/picker.js +1 -1
- package/dist/preview.js +1 -1
- package/dist/sharing.js +1 -1
- package/dist/sidebar.js +1 -1
- package/dist/uploader.js +1 -1
- package/es/api/Metadata.js +98 -13
- package/es/api/Metadata.js.flow +110 -12
- package/es/api/Metadata.js.map +1 -1
- package/es/elements/common/messages.js +16 -0
- package/es/elements/common/messages.js.flow +25 -0
- package/es/elements/common/messages.js.map +1 -1
- package/es/elements/content-explorer/Content.js +5 -2
- package/es/elements/content-explorer/Content.js.map +1 -1
- package/es/elements/content-explorer/ContentExplorer.js +31 -6
- package/es/elements/content-explorer/ContentExplorer.js.map +1 -1
- package/es/elements/content-explorer/MetadataQueryAPIHelper.js +164 -10
- package/es/elements/content-explorer/MetadataQueryAPIHelper.js.map +1 -1
- package/es/elements/content-explorer/MetadataQueryBuilder.js +115 -0
- package/es/elements/content-explorer/MetadataQueryBuilder.js.map +1 -0
- package/es/elements/content-explorer/MetadataSidePanel.js +40 -14
- package/es/elements/content-explorer/MetadataSidePanel.js.map +1 -1
- package/es/elements/content-explorer/MetadataViewContainer.js +133 -36
- package/es/elements/content-explorer/MetadataViewContainer.js.map +1 -1
- package/es/elements/content-explorer/stories/MetadataView.stories.js +3 -25
- package/es/elements/content-explorer/stories/MetadataView.stories.js.map +1 -1
- package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js +65 -29
- package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js.map +1 -1
- package/es/elements/content-explorer/utils.js +140 -12
- package/es/elements/content-explorer/utils.js.map +1 -1
- package/es/src/elements/common/__mocks__/mockMetadata.d.ts +8 -24
- package/es/src/elements/content-explorer/Content.d.ts +4 -3
- package/es/src/elements/content-explorer/ContentExplorer.d.ts +19 -6
- package/es/src/elements/content-explorer/MetadataQueryAPIHelper.d.ts +22 -3
- package/es/src/elements/content-explorer/MetadataQueryBuilder.d.ts +27 -0
- package/es/src/elements/content-explorer/MetadataSidePanel.d.ts +6 -3
- package/es/src/elements/content-explorer/MetadataViewContainer.d.ts +10 -4
- package/es/src/elements/content-explorer/__tests__/MetadataQueryBuilder.test.d.ts +1 -0
- package/es/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.d.ts +1 -0
- package/es/src/elements/content-explorer/utils.d.ts +9 -3
- package/i18n/bn-IN.js +4 -0
- package/i18n/bn-IN.properties +12 -0
- package/i18n/da-DK.js +4 -0
- package/i18n/da-DK.properties +12 -0
- package/i18n/de-DE.js +5 -1
- package/i18n/de-DE.properties +12 -0
- package/i18n/en-AU.js +4 -0
- package/i18n/en-AU.properties +12 -0
- package/i18n/en-CA.js +4 -0
- package/i18n/en-CA.properties +12 -0
- package/i18n/en-GB.js +4 -0
- package/i18n/en-GB.properties +12 -0
- package/i18n/en-US.js +4 -0
- package/i18n/en-US.properties +8 -0
- package/i18n/en-x-pseudo.js +4 -0
- package/i18n/es-419.js +5 -1
- package/i18n/es-419.properties +12 -0
- package/i18n/es-ES.js +5 -1
- package/i18n/es-ES.properties +12 -0
- package/i18n/fi-FI.js +4 -0
- package/i18n/fi-FI.properties +12 -0
- package/i18n/fr-CA.js +4 -0
- package/i18n/fr-CA.properties +12 -0
- package/i18n/fr-FR.js +4 -0
- package/i18n/fr-FR.properties +12 -0
- package/i18n/hi-IN.js +4 -0
- package/i18n/hi-IN.properties +12 -0
- package/i18n/it-IT.js +4 -0
- package/i18n/it-IT.properties +12 -0
- package/i18n/ja-JP.js +6 -2
- package/i18n/ja-JP.properties +14 -2
- package/i18n/ko-KR.js +4 -0
- package/i18n/ko-KR.properties +12 -0
- package/i18n/nb-NO.js +4 -0
- package/i18n/nb-NO.properties +12 -0
- package/i18n/nl-NL.js +4 -0
- package/i18n/nl-NL.properties +12 -0
- package/i18n/pl-PL.js +4 -0
- package/i18n/pl-PL.properties +12 -0
- package/i18n/pt-BR.js +4 -0
- package/i18n/pt-BR.properties +12 -0
- package/i18n/ru-RU.js +5 -1
- package/i18n/ru-RU.properties +12 -0
- package/i18n/sv-SE.js +4 -0
- package/i18n/sv-SE.properties +12 -0
- package/i18n/tr-TR.js +5 -1
- package/i18n/tr-TR.properties +12 -0
- package/i18n/zh-CN.js +4 -0
- package/i18n/zh-CN.properties +12 -0
- package/i18n/zh-TW.js +4 -0
- package/i18n/zh-TW.properties +12 -0
- package/package.json +3 -3
- package/src/api/Metadata.js +110 -12
- package/src/api/__tests__/Metadata.test.js +120 -0
- package/src/elements/common/__mocks__/mockMetadata.ts +7 -11
- package/src/elements/common/messages.js +25 -0
- package/src/elements/content-explorer/Content.tsx +9 -2
- package/src/elements/content-explorer/ContentExplorer.tsx +71 -17
- package/src/elements/content-explorer/MetadataQueryAPIHelper.ts +199 -8
- package/src/elements/content-explorer/MetadataQueryBuilder.ts +159 -0
- package/src/elements/content-explorer/MetadataSidePanel.tsx +55 -14
- package/src/elements/content-explorer/MetadataViewContainer.tsx +164 -29
- package/src/elements/content-explorer/__tests__/Content.test.tsx +1 -0
- package/src/elements/content-explorer/__tests__/ContentExplorer.test.tsx +38 -7
- package/src/elements/content-explorer/__tests__/MetadataQueryAPIHelper.test.ts +428 -12
- package/src/elements/content-explorer/__tests__/MetadataQueryBuilder.test.ts +419 -0
- package/src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx +145 -3
- package/src/elements/content-explorer/__tests__/MetadataViewContainer.test.tsx +413 -9
- package/src/elements/content-explorer/stories/MetadataView.stories.tsx +3 -21
- package/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx +56 -21
- package/src/elements/content-explorer/utils.ts +150 -13
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mergeQueryParams,
|
|
3
|
+
mergeQueries,
|
|
4
|
+
getStringFilter,
|
|
5
|
+
getRangeFilter,
|
|
6
|
+
getSelectFilter,
|
|
7
|
+
getMimeTypeFilter,
|
|
8
|
+
} from '../MetadataQueryBuilder';
|
|
9
|
+
|
|
10
|
+
describe('elements/content-explorer/MetadataQueryBuilder', () => {
|
|
11
|
+
describe('mergeQueryParams', () => {
|
|
12
|
+
test('should merge two objects', () => {
|
|
13
|
+
const target = { key1: 'value1' };
|
|
14
|
+
const source = { key2: 'value2' };
|
|
15
|
+
const result = mergeQueryParams(target, source);
|
|
16
|
+
expect(result).toEqual({ key1: 'value1', key2: 'value2' });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should override target values with source values', () => {
|
|
20
|
+
const target = { key1: 'value1', key2: 'old' };
|
|
21
|
+
const source = { key2: 'new', key3: 'value3' };
|
|
22
|
+
const result = mergeQueryParams(target, source);
|
|
23
|
+
expect(result).toEqual({ key1: 'value1', key2: 'new', key3: 'value3' });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('should return source when target is empty', () => {
|
|
27
|
+
const target = {};
|
|
28
|
+
const source = { key1: 'value1' };
|
|
29
|
+
const result = mergeQueryParams(target, source);
|
|
30
|
+
expect(result).toEqual({ key1: 'value1' });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('should return target when source is empty', () => {
|
|
34
|
+
const target = { key1: 'value1' };
|
|
35
|
+
const source = {};
|
|
36
|
+
const result = mergeQueryParams(target, source);
|
|
37
|
+
expect(result).toEqual({ key1: 'value1' });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should return empty object when both are empty', () => {
|
|
41
|
+
const result = mergeQueryParams({}, {});
|
|
42
|
+
expect(result).toEqual({});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('mergeQueries', () => {
|
|
47
|
+
test('should merge two arrays', () => {
|
|
48
|
+
const target = ['query1'];
|
|
49
|
+
const source = ['query2'];
|
|
50
|
+
const result = mergeQueries(target, source);
|
|
51
|
+
expect(result).toEqual(['query1', 'query2']);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('should handle empty target array', () => {
|
|
55
|
+
const target: string[] = [];
|
|
56
|
+
const source = ['query1', 'query2'];
|
|
57
|
+
const result = mergeQueries(target, source);
|
|
58
|
+
expect(result).toEqual(['query1', 'query2']);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should handle empty source array', () => {
|
|
62
|
+
const target = ['query1', 'query2'];
|
|
63
|
+
const source: string[] = [];
|
|
64
|
+
const result = mergeQueries(target, source);
|
|
65
|
+
expect(result).toEqual(['query1', 'query2']);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('should handle both empty arrays', () => {
|
|
69
|
+
const result = mergeQueries([], []);
|
|
70
|
+
expect(result).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('getStringFilter', () => {
|
|
75
|
+
test('should generate string filter with ILIKE query', () => {
|
|
76
|
+
const result = getStringFilter('test', 'field_name', 0);
|
|
77
|
+
expect(result).toEqual({
|
|
78
|
+
queryParams: { arg_field_name_1: '%test%' },
|
|
79
|
+
queries: ['(field_name ILIKE :arg_field_name_1)'],
|
|
80
|
+
keysGenerated: 1,
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('should escape special characters in value', () => {
|
|
85
|
+
const result = getStringFilter('test_value%123', 'field_name', 0);
|
|
86
|
+
expect(result).toEqual({
|
|
87
|
+
queryParams: { arg_field_name_1: '%test\\_value\\%123%' },
|
|
88
|
+
queries: ['(field_name ILIKE :arg_field_name_1)'],
|
|
89
|
+
keysGenerated: 1,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('should use correct arg index', () => {
|
|
94
|
+
const result = getStringFilter('test', 'field_name', 5);
|
|
95
|
+
expect(result).toEqual({
|
|
96
|
+
queryParams: { arg_field_name_6: '%test%' },
|
|
97
|
+
queries: ['(field_name ILIKE :arg_field_name_6)'],
|
|
98
|
+
keysGenerated: 1,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('getRangeFilter', () => {
|
|
104
|
+
test('should generate range filter with both gt and lt', () => {
|
|
105
|
+
const filterValue = { range: { gt: 10, lt: 20 } };
|
|
106
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
107
|
+
expect(result).toEqual({
|
|
108
|
+
queryParams: { arg_field_name_1: 10, arg_field_name_2: 20 },
|
|
109
|
+
queries: ['(field_name >= :arg_field_name_1 AND field_name <= :arg_field_name_2)'],
|
|
110
|
+
keysGenerated: 2,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('should generate range filter with only gt', () => {
|
|
115
|
+
const filterValue = { range: { gt: 10, lt: '' } };
|
|
116
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
117
|
+
expect(result).toEqual({
|
|
118
|
+
queryParams: { arg_field_name_1: 10 },
|
|
119
|
+
queries: ['(field_name >= :arg_field_name_1)'],
|
|
120
|
+
keysGenerated: 1,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('should generate range filter with only lt', () => {
|
|
125
|
+
const filterValue = { range: { gt: '', lt: 20 } };
|
|
126
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
127
|
+
expect(result).toEqual({
|
|
128
|
+
queryParams: { arg_field_name_1: 20 },
|
|
129
|
+
queries: ['(field_name <= :arg_field_name_1)'],
|
|
130
|
+
keysGenerated: 1,
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should handle null values in range', () => {
|
|
135
|
+
const filterValue = { range: { gt: null, lt: 20 } };
|
|
136
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
137
|
+
expect(result).toEqual({
|
|
138
|
+
queryParams: { arg_field_name_1: 20 },
|
|
139
|
+
queries: ['(field_name <= :arg_field_name_1)'],
|
|
140
|
+
keysGenerated: 1,
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('should handle undefined values in range', () => {
|
|
145
|
+
const filterValue = { range: { gt: undefined, lt: 20 } };
|
|
146
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
147
|
+
expect(result).toEqual({
|
|
148
|
+
queryParams: { arg_field_name_1: 20 },
|
|
149
|
+
queries: ['(field_name <= :arg_field_name_1)'],
|
|
150
|
+
keysGenerated: 1,
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('should handle zero values in range', () => {
|
|
155
|
+
const filterValue = { range: { gt: 0, lt: 100 } };
|
|
156
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
157
|
+
expect(result).toEqual({
|
|
158
|
+
queryParams: { arg_field_name_1: 0, arg_field_name_2: 100 },
|
|
159
|
+
queries: ['(field_name >= :arg_field_name_1 AND field_name <= :arg_field_name_2)'],
|
|
160
|
+
keysGenerated: 2,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('should return empty result for invalid filter value', () => {
|
|
165
|
+
const result = getRangeFilter(null, 'field_name', 0);
|
|
166
|
+
expect(result).toEqual({
|
|
167
|
+
queryParams: {},
|
|
168
|
+
queries: [],
|
|
169
|
+
keysGenerated: 0,
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('should return empty result for filter value without range', () => {
|
|
174
|
+
const filterValue = { someOtherProperty: 'value' } as unknown as
|
|
175
|
+
| string[]
|
|
176
|
+
| { range: { gt: number | string; lt: number | string } };
|
|
177
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
178
|
+
expect(result).toEqual({
|
|
179
|
+
queryParams: {},
|
|
180
|
+
queries: [],
|
|
181
|
+
keysGenerated: 0,
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('should return empty result for empty range', () => {
|
|
186
|
+
const filterValue = { range: { gt: '', lt: '' } };
|
|
187
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
188
|
+
expect(result).toEqual({
|
|
189
|
+
queryParams: {},
|
|
190
|
+
queries: [],
|
|
191
|
+
keysGenerated: 0,
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('should return empty result for null range', () => {
|
|
196
|
+
const filterValue = { range: null };
|
|
197
|
+
const result = getRangeFilter(filterValue, 'field_name', 0);
|
|
198
|
+
expect(result).toEqual({
|
|
199
|
+
queryParams: {},
|
|
200
|
+
queries: [],
|
|
201
|
+
keysGenerated: 0,
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('should use correct arg index', () => {
|
|
206
|
+
const filterValue = { range: { gt: 10, lt: 20 } };
|
|
207
|
+
const result = getRangeFilter(filterValue, 'field_name', 5);
|
|
208
|
+
expect(result).toEqual({
|
|
209
|
+
queryParams: { arg_field_name_6: 10, arg_field_name_7: 20 },
|
|
210
|
+
queries: ['(field_name >= :arg_field_name_6 AND field_name <= :arg_field_name_7)'],
|
|
211
|
+
keysGenerated: 2,
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('getSelectFilter', () => {
|
|
217
|
+
test('should generate select filter for multiple values', () => {
|
|
218
|
+
const filterValue = ['value1', 'value2', 'value3'];
|
|
219
|
+
const result = getSelectFilter(filterValue, 'field_name', 0);
|
|
220
|
+
expect(result).toEqual({
|
|
221
|
+
queryParams: {
|
|
222
|
+
arg_field_name_1: 'value1',
|
|
223
|
+
arg_field_name_2: 'value2',
|
|
224
|
+
arg_field_name_3: 'value3',
|
|
225
|
+
},
|
|
226
|
+
queries: ['(field_name HASANY (:arg_field_name_1, :arg_field_name_2, :arg_field_name_3))'],
|
|
227
|
+
keysGenerated: 3,
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('should handle mimetype-filter field key specially', () => {
|
|
232
|
+
const filterValue = ['pdf', 'doc'];
|
|
233
|
+
const result = getSelectFilter(filterValue, 'mimetype-filter', 0);
|
|
234
|
+
expect(result).toEqual({
|
|
235
|
+
queryParams: {
|
|
236
|
+
arg_mimetype_filter_1: 'pdf',
|
|
237
|
+
arg_mimetype_filter_2: 'doc',
|
|
238
|
+
},
|
|
239
|
+
queries: ['(item.extension HASANY (:arg_mimetype_filter_1, :arg_mimetype_filter_2))'],
|
|
240
|
+
keysGenerated: 2,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('should handle single value array', () => {
|
|
245
|
+
const filterValue = ['single_value'];
|
|
246
|
+
const result = getSelectFilter(filterValue, 'field_name', 0);
|
|
247
|
+
expect(result).toEqual({
|
|
248
|
+
queryParams: { arg_field_name_1: 'single_value' },
|
|
249
|
+
queries: ['(field_name HASANY (:arg_field_name_1))'],
|
|
250
|
+
keysGenerated: 1,
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('should handle numeric values converted to strings', () => {
|
|
255
|
+
const filterValue = ['123', '456'];
|
|
256
|
+
const result = getSelectFilter(filterValue, 'field_name', 0);
|
|
257
|
+
expect(result).toEqual({
|
|
258
|
+
queryParams: {
|
|
259
|
+
arg_field_name_1: '123',
|
|
260
|
+
arg_field_name_2: '456',
|
|
261
|
+
},
|
|
262
|
+
queries: ['(field_name HASANY (:arg_field_name_1, :arg_field_name_2))'],
|
|
263
|
+
keysGenerated: 2,
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('should return empty result for empty array', () => {
|
|
268
|
+
const result = getSelectFilter([], 'field_name', 0);
|
|
269
|
+
expect(result).toEqual({
|
|
270
|
+
queryParams: {},
|
|
271
|
+
queries: [],
|
|
272
|
+
keysGenerated: 0,
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('should return empty result for null/undefined', () => {
|
|
277
|
+
const result = getSelectFilter(null, 'field_name', 0);
|
|
278
|
+
expect(result).toEqual({
|
|
279
|
+
queryParams: {},
|
|
280
|
+
queries: [],
|
|
281
|
+
keysGenerated: 0,
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test('should use correct arg index', () => {
|
|
286
|
+
const filterValue = ['value1'];
|
|
287
|
+
const result = getSelectFilter(filterValue, 'field_name', 5);
|
|
288
|
+
expect(result).toEqual({
|
|
289
|
+
queryParams: { arg_field_name_6: 'value1' },
|
|
290
|
+
queries: ['(field_name HASANY (:arg_field_name_6))'],
|
|
291
|
+
keysGenerated: 1,
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('should handle field names with special characters', () => {
|
|
296
|
+
const filterValue = ['value1', 'value2'];
|
|
297
|
+
const result = getSelectFilter(filterValue, 'field-name.with/special_chars', 0);
|
|
298
|
+
expect(result).toEqual({
|
|
299
|
+
queryParams: {
|
|
300
|
+
arg_field_name_with_special_chars_1: 'value1',
|
|
301
|
+
arg_field_name_with_special_chars_2: 'value2',
|
|
302
|
+
},
|
|
303
|
+
queries: [
|
|
304
|
+
'(field-name.with/special_chars HASANY (:arg_field_name_with_special_chars_1, :arg_field_name_with_special_chars_2))',
|
|
305
|
+
],
|
|
306
|
+
keysGenerated: 2,
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe('getMimeTypeFilter', () => {
|
|
312
|
+
test('should generate mime type filter and remove "Type" suffix', () => {
|
|
313
|
+
const filterValue = ['pdfType', 'docType', 'txtType'];
|
|
314
|
+
const result = getMimeTypeFilter(filterValue, 'mimetype', 0);
|
|
315
|
+
expect(result).toEqual({
|
|
316
|
+
queryParams: {
|
|
317
|
+
arg_mimetype_1: 'pdf',
|
|
318
|
+
arg_mimetype_2: 'doc',
|
|
319
|
+
arg_mimetype_3: 'txt',
|
|
320
|
+
},
|
|
321
|
+
queries: ['(item.extension IN (:arg_mimetype_1, :arg_mimetype_2, :arg_mimetype_3))'],
|
|
322
|
+
keysGenerated: 3,
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('should handle values without "Type" suffix', () => {
|
|
327
|
+
const filterValue = ['pdf', 'doc'];
|
|
328
|
+
const result = getMimeTypeFilter(filterValue, 'mimetype', 0);
|
|
329
|
+
expect(result).toEqual({
|
|
330
|
+
queryParams: {
|
|
331
|
+
arg_mimetype_1: 'pdf',
|
|
332
|
+
arg_mimetype_2: 'doc',
|
|
333
|
+
},
|
|
334
|
+
queries: ['(item.extension IN (:arg_mimetype_1, :arg_mimetype_2))'],
|
|
335
|
+
keysGenerated: 2,
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('should handle mixed values with and without "Type" suffix', () => {
|
|
340
|
+
const filterValue = ['pdfType', 'doc', 'txtType'];
|
|
341
|
+
const result = getMimeTypeFilter(filterValue, 'mimetype', 0);
|
|
342
|
+
expect(result).toEqual({
|
|
343
|
+
queryParams: {
|
|
344
|
+
arg_mimetype_1: 'pdf',
|
|
345
|
+
arg_mimetype_2: 'doc',
|
|
346
|
+
arg_mimetype_3: 'txt',
|
|
347
|
+
},
|
|
348
|
+
queries: ['(item.extension IN (:arg_mimetype_1, :arg_mimetype_2, :arg_mimetype_3))'],
|
|
349
|
+
keysGenerated: 3,
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test('should handle single value array', () => {
|
|
354
|
+
const filterValue = ['pdfType'];
|
|
355
|
+
const result = getMimeTypeFilter(filterValue, 'mimetype', 0);
|
|
356
|
+
expect(result).toEqual({
|
|
357
|
+
queryParams: { arg_mimetype_1: 'pdf' },
|
|
358
|
+
queries: ['(item.extension IN (:arg_mimetype_1))'],
|
|
359
|
+
keysGenerated: 1,
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('should handle numeric values converted to strings', () => {
|
|
364
|
+
const filterValue = ['123', '456'];
|
|
365
|
+
const result = getMimeTypeFilter(filterValue, 'mimetype', 0);
|
|
366
|
+
expect(result).toEqual({
|
|
367
|
+
queryParams: {
|
|
368
|
+
arg_mimetype_1: '123',
|
|
369
|
+
arg_mimetype_2: '456',
|
|
370
|
+
},
|
|
371
|
+
queries: ['(item.extension IN (:arg_mimetype_1, :arg_mimetype_2))'],
|
|
372
|
+
keysGenerated: 2,
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('should return empty result for empty array', () => {
|
|
377
|
+
const result = getMimeTypeFilter([], 'mimetype', 0);
|
|
378
|
+
expect(result).toEqual({
|
|
379
|
+
queryParams: {},
|
|
380
|
+
queries: [],
|
|
381
|
+
keysGenerated: 0,
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('should return empty result for null/undefined', () => {
|
|
386
|
+
const result = getMimeTypeFilter(null, 'mimetype', 0);
|
|
387
|
+
expect(result).toEqual({
|
|
388
|
+
queryParams: {},
|
|
389
|
+
queries: [],
|
|
390
|
+
keysGenerated: 0,
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test('should use correct arg index', () => {
|
|
395
|
+
const filterValue = ['pdfType'];
|
|
396
|
+
const result = getMimeTypeFilter(filterValue, 'mimetype', 5);
|
|
397
|
+
expect(result).toEqual({
|
|
398
|
+
queryParams: { arg_mimetype_6: 'pdf' },
|
|
399
|
+
queries: ['(item.extension IN (:arg_mimetype_6))'],
|
|
400
|
+
keysGenerated: 1,
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test('should handle field names with special characters', () => {
|
|
405
|
+
const filterValue = ['pdfType', 'docType'];
|
|
406
|
+
const result = getMimeTypeFilter(filterValue, 'mime-type.with/special_chars', 0);
|
|
407
|
+
expect(result).toEqual({
|
|
408
|
+
queryParams: {
|
|
409
|
+
arg_mime_type_with_special_chars_1: 'pdf',
|
|
410
|
+
arg_mime_type_with_special_chars_2: 'doc',
|
|
411
|
+
},
|
|
412
|
+
queries: [
|
|
413
|
+
'(item.extension IN (:arg_mime_type_with_special_chars_1, :arg_mime_type_with_special_chars_2))',
|
|
414
|
+
],
|
|
415
|
+
keysGenerated: 2,
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
|
-
import {
|
|
3
|
+
import { Notification } from '@box/blueprint-web';
|
|
4
|
+
import { render, screen, waitFor } from '../../../test-utils/testing-library';
|
|
4
5
|
import MetadataSidePanel, { type MetadataSidePanelProps } from '../MetadataSidePanel';
|
|
5
6
|
|
|
6
7
|
// Mock scrollTo method
|
|
@@ -65,13 +66,20 @@ const mockOnClose = jest.fn();
|
|
|
65
66
|
describe('elements/content-explorer/MetadataSidePanel', () => {
|
|
66
67
|
const defaultProps: MetadataSidePanelProps = {
|
|
67
68
|
currentCollection: mockCollection,
|
|
68
|
-
onClose: mockOnClose,
|
|
69
69
|
metadataTemplate: mockMetadataTemplate,
|
|
70
|
+
onClose: mockOnClose,
|
|
71
|
+
onUpdate: jest.fn(),
|
|
72
|
+
refreshCollection: jest.fn(),
|
|
70
73
|
selectedItemIds: new Set(['1']),
|
|
71
74
|
};
|
|
72
75
|
|
|
73
76
|
const renderComponent = (props: Partial<MetadataSidePanelProps> = {}) =>
|
|
74
|
-
render(
|
|
77
|
+
render(
|
|
78
|
+
<Notification.Provider>
|
|
79
|
+
<Notification.Viewport />
|
|
80
|
+
<MetadataSidePanel {...defaultProps} {...props} />
|
|
81
|
+
</Notification.Provider>,
|
|
82
|
+
);
|
|
75
83
|
|
|
76
84
|
test('renders the metadata title', () => {
|
|
77
85
|
renderComponent();
|
|
@@ -124,4 +132,138 @@ describe('elements/content-explorer/MetadataSidePanel', () => {
|
|
|
124
132
|
const submitButton = screen.getByRole('button', { name: 'Save' });
|
|
125
133
|
expect(submitButton).toBeInTheDocument();
|
|
126
134
|
});
|
|
135
|
+
|
|
136
|
+
test('switches back to view mode when cancel button is clicked', async () => {
|
|
137
|
+
renderComponent();
|
|
138
|
+
const editTemplateButton = screen.getByLabelText('Edit Mock Template');
|
|
139
|
+
await userEvent.click(editTemplateButton);
|
|
140
|
+
|
|
141
|
+
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
|
|
142
|
+
await userEvent.click(cancelButton);
|
|
143
|
+
|
|
144
|
+
// Should be back in view mode
|
|
145
|
+
expect(screen.getByLabelText('Edit Mock Template')).toBeInTheDocument();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('calls onUpdate when form is submitted for single item', async () => {
|
|
149
|
+
const mockUpdateMetadata = jest.fn().mockResolvedValue(undefined);
|
|
150
|
+
renderComponent({ onUpdate: mockUpdateMetadata });
|
|
151
|
+
|
|
152
|
+
const editTemplateButton = screen.getByLabelText('Edit Mock Template');
|
|
153
|
+
await userEvent.click(editTemplateButton);
|
|
154
|
+
|
|
155
|
+
const submitButton = screen.getByRole('button', { name: 'Save' });
|
|
156
|
+
await userEvent.click(submitButton);
|
|
157
|
+
|
|
158
|
+
expect(mockUpdateMetadata).toHaveBeenCalledWith(
|
|
159
|
+
[mockCollection.items[0]],
|
|
160
|
+
expect.any(Array),
|
|
161
|
+
expect.any(Array),
|
|
162
|
+
expect.any(Array),
|
|
163
|
+
expect.any(Function),
|
|
164
|
+
expect.any(Function),
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('calls onUpdate when multiple items are selected', async () => {
|
|
169
|
+
const mockUpdateMetadata = jest.fn().mockResolvedValue(undefined);
|
|
170
|
+
|
|
171
|
+
renderComponent({
|
|
172
|
+
selectedItemIds: new Set(['1', '2']),
|
|
173
|
+
onUpdate: mockUpdateMetadata,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const editTemplateButton = screen.getByLabelText('Edit Mock Template');
|
|
177
|
+
await userEvent.click(editTemplateButton);
|
|
178
|
+
|
|
179
|
+
const submitButton = screen.getByRole('button', { name: 'Save' });
|
|
180
|
+
await userEvent.click(submitButton);
|
|
181
|
+
|
|
182
|
+
await waitFor(() => {
|
|
183
|
+
expect(mockUpdateMetadata).toHaveBeenCalledTimes(1);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('displays success notification when metadata update succeeds', async () => {
|
|
188
|
+
const mockUpdateMetadata = jest.fn().mockImplementation((_, __, ___, ____, successCallback) => {
|
|
189
|
+
successCallback();
|
|
190
|
+
return Promise.resolve();
|
|
191
|
+
});
|
|
192
|
+
const mockRefreshCollection = jest.fn();
|
|
193
|
+
|
|
194
|
+
renderComponent({
|
|
195
|
+
onUpdate: mockUpdateMetadata,
|
|
196
|
+
refreshCollection: mockRefreshCollection,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const editTemplateButton = screen.getByLabelText('Edit Mock Template');
|
|
200
|
+
await userEvent.click(editTemplateButton);
|
|
201
|
+
|
|
202
|
+
const submitButton = screen.getByRole('button', { name: 'Save' });
|
|
203
|
+
await userEvent.click(submitButton);
|
|
204
|
+
|
|
205
|
+
expect(screen.getByText('1 document updated')).toBeInTheDocument();
|
|
206
|
+
expect(mockRefreshCollection).toHaveBeenCalledTimes(1);
|
|
207
|
+
expect(screen.getByLabelText('Edit Mock Template')).toBeInTheDocument(); // Back to view mode
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('displays error notification when metadata update fails', async () => {
|
|
211
|
+
const mockUpdateMetadata = jest.fn().mockImplementation((_, __, ___, ____, _____, errorCallback) => {
|
|
212
|
+
errorCallback();
|
|
213
|
+
return Promise.resolve();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
renderComponent({ onUpdate: mockUpdateMetadata });
|
|
217
|
+
|
|
218
|
+
const editTemplateButton = screen.getByLabelText('Edit Mock Template');
|
|
219
|
+
await userEvent.click(editTemplateButton);
|
|
220
|
+
|
|
221
|
+
const submitButton = screen.getByRole('button', { name: 'Save' });
|
|
222
|
+
await userEvent.click(submitButton);
|
|
223
|
+
|
|
224
|
+
await waitFor(() => {
|
|
225
|
+
expect(screen.getByText('Unable to save changes. Please try again.')).toBeInTheDocument();
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('handles "all" selection correctly', () => {
|
|
230
|
+
renderComponent({ selectedItemIds: 'all' });
|
|
231
|
+
const subtitle = screen.getByText('2 files selected');
|
|
232
|
+
expect(subtitle).toBeInTheDocument();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('displays "Multiple Values" for items with different field values', () => {
|
|
236
|
+
const collectionWithDifferentValues = {
|
|
237
|
+
...mockCollection,
|
|
238
|
+
items: [
|
|
239
|
+
{
|
|
240
|
+
...mockCollection.items[0],
|
|
241
|
+
metadata: {
|
|
242
|
+
enterprise_123: {
|
|
243
|
+
mockTemplate: {
|
|
244
|
+
alias: 'value-1',
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
...mockCollection.items[1],
|
|
251
|
+
metadata: {
|
|
252
|
+
enterprise_123: {
|
|
253
|
+
mockTemplate: {
|
|
254
|
+
alias: 'value-2',
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
renderComponent({
|
|
263
|
+
currentCollection: collectionWithDifferentValues,
|
|
264
|
+
selectedItemIds: new Set(['1', '2']),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(screen.getByText('Multiple Values')).toBeInTheDocument();
|
|
268
|
+
});
|
|
127
269
|
});
|