@configuratorware/configurator-frontendgui 1.57.13 → 1.57.15
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/App/Modules/Designer/Components/DesignerSelectionToolbox/DesignerSelectionToolbox.js +5 -1
- package/App/Modules/Designer/Containers/DesignerSelectionToolbox.js +10 -2
- package/App/Reducers/Configurator/Reducer.js +35 -14
- package/App/Reducers/DesignArea/Selectors.js +39 -7
- package/App/Reducers/Fonts/Selectors.js +35 -10
- package/App/Services/DesignerService.js +23 -5
- package/App/Shared/Components/AmountInput/index.js +65 -44
- package/App/Shared/Components/ProductVariant/index.js +1 -1
- package/package.json +4 -4
- package/src/App/Modules/Designer/Components/DesignerSelectionToolbox/DesignerSelectionToolbox.js +4 -0
- package/src/App/Modules/Designer/Containers/DesignerSelectionToolbox.js +5 -1
- package/src/App/Reducers/Configurator/Reducer.js +67 -17
- package/src/App/Reducers/Configurator/__tests__/Reducer.test.js +230 -30
- package/src/App/Reducers/DesignArea/Selectors.js +26 -0
- package/src/App/Reducers/DesignArea/__tests__/Selectors.test.js +19 -0
- package/src/App/Reducers/Fonts/Selectors.js +25 -5
- package/src/App/Reducers/Fonts/__tests__/Selectors.test.js +14 -1
- package/src/App/Services/DesignerService.js +28 -9
- package/src/App/Services/__tests__/DesignerService.test.js +111 -0
- package/src/App/Shared/Components/AmountInput/__tests__/AmountInput.blur.test.js +249 -0
- package/src/App/Shared/Components/AmountInput/__tests__/AmountInput.retyp.test.js +57 -0
- package/src/App/Shared/Components/AmountInput/__tests__/index.test.js +16 -0
- package/src/App/Shared/Components/AmountInput/index.js +42 -38
- package/src/App/Shared/Components/ProductVariant/index.js +1 -1
|
@@ -9,7 +9,6 @@ import isArray from 'lodash/isArray';
|
|
|
9
9
|
import find from 'lodash/find';
|
|
10
10
|
import findIndex from 'lodash/findIndex';
|
|
11
11
|
import omit from 'lodash/omit';
|
|
12
|
-
import forEach from 'lodash/forEach';
|
|
13
12
|
import reduce from 'lodash/reduce';
|
|
14
13
|
import immutableSet from 'Utils/Immutable/set';
|
|
15
14
|
import { designDataHandler } from '../DesignData/Reducer';
|
|
@@ -421,13 +420,21 @@ export function baseHandler(state, action) {
|
|
|
421
420
|
case actionTypes.SET_SELECTED_AMOUNT: {
|
|
422
421
|
const selectedAmountsPath = 'customdata.selectedAmounts';
|
|
423
422
|
const selectedAmounts = get(state.configuration, selectedAmountsPath, {});
|
|
424
|
-
|
|
423
|
+
let configuration = immutableSet(
|
|
425
424
|
state.configuration,
|
|
426
425
|
selectedAmountsPath,
|
|
427
426
|
action.amount === 0
|
|
428
427
|
? omit(selectedAmounts, action.identifier)
|
|
429
428
|
: { ...selectedAmounts, [action.identifier]: action.amount }
|
|
430
429
|
);
|
|
430
|
+
if (action.amount === 0) {
|
|
431
|
+
const bulkNames = get(configuration, 'customdata.bulkNames', {});
|
|
432
|
+
configuration = immutableSet(
|
|
433
|
+
configuration,
|
|
434
|
+
'customdata.bulkNames',
|
|
435
|
+
omit(bulkNames, action.identifier)
|
|
436
|
+
);
|
|
437
|
+
}
|
|
431
438
|
return {
|
|
432
439
|
...state,
|
|
433
440
|
selectedAmountsUpdated: !!action.isUpdatedByUser,
|
|
@@ -437,16 +444,33 @@ export function baseHandler(state, action) {
|
|
|
437
444
|
|
|
438
445
|
case actionTypes.SWITCH_SELECTED_AMOUNT: {
|
|
439
446
|
const { srcIdentifier, dstIdentifier } = action;
|
|
447
|
+
if (srcIdentifier === dstIdentifier) {
|
|
448
|
+
return state;
|
|
449
|
+
}
|
|
440
450
|
const selectedAmountsPath = 'customdata.selectedAmounts';
|
|
441
451
|
const selectedAmounts = get(state.configuration, selectedAmountsPath, {});
|
|
442
452
|
if (!(srcIdentifier in selectedAmounts)) {
|
|
443
453
|
return state;
|
|
444
454
|
}
|
|
445
|
-
|
|
455
|
+
let configuration = immutableSet(
|
|
446
456
|
state.configuration,
|
|
447
457
|
selectedAmountsPath,
|
|
448
458
|
omit({ ...selectedAmounts, [dstIdentifier]: selectedAmounts[srcIdentifier] }, srcIdentifier)
|
|
449
459
|
);
|
|
460
|
+
const bulkNames = get(configuration, 'customdata.bulkNames', {});
|
|
461
|
+
if (srcIdentifier in bulkNames) {
|
|
462
|
+
configuration = immutableSet(
|
|
463
|
+
configuration,
|
|
464
|
+
'customdata.bulkNames',
|
|
465
|
+
omit({ ...bulkNames, [dstIdentifier]: bulkNames[srcIdentifier] }, srcIdentifier)
|
|
466
|
+
);
|
|
467
|
+
} else if (dstIdentifier in bulkNames) {
|
|
468
|
+
configuration = immutableSet(
|
|
469
|
+
configuration,
|
|
470
|
+
'customdata.bulkNames',
|
|
471
|
+
omit(bulkNames, dstIdentifier)
|
|
472
|
+
);
|
|
473
|
+
}
|
|
450
474
|
return {
|
|
451
475
|
...state,
|
|
452
476
|
configuration,
|
|
@@ -455,9 +479,21 @@ export function baseHandler(state, action) {
|
|
|
455
479
|
|
|
456
480
|
case actionTypes.SET_SELECTED_AMOUNTS: {
|
|
457
481
|
const selectedAmountsPath = 'configuration.customdata.selectedAmounts';
|
|
482
|
+
const bulkNamesPath = 'configuration.customdata.bulkNames';
|
|
458
483
|
const selectedAmounts = get(state, selectedAmountsPath, {});
|
|
459
484
|
const nextAmounts = { ...selectedAmounts, ...action.sizes };
|
|
460
|
-
const
|
|
485
|
+
const removedIdentifiers = filter(
|
|
486
|
+
Object.keys(action.sizes || {}),
|
|
487
|
+
identifier => action.sizes[identifier] === 0
|
|
488
|
+
);
|
|
489
|
+
let newState = { ...state, selectedAmountsUpdated: !!action.isUpdatedByUser };
|
|
490
|
+
if (removedIdentifiers.length > 0) {
|
|
491
|
+
newState = immutableSet(
|
|
492
|
+
newState,
|
|
493
|
+
bulkNamesPath,
|
|
494
|
+
omit(get(state, bulkNamesPath, {}), removedIdentifiers)
|
|
495
|
+
);
|
|
496
|
+
}
|
|
461
497
|
return immutableSet(newState, selectedAmountsPath, {
|
|
462
498
|
...reduce(
|
|
463
499
|
nextAmounts,
|
|
@@ -474,14 +510,22 @@ export function baseHandler(state, action) {
|
|
|
474
510
|
}
|
|
475
511
|
case actionTypes.DELETE_SELECTED_AMOUNTS: {
|
|
476
512
|
const selectedAmountsPath = 'configuration.customdata.selectedAmounts';
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
513
|
+
const bulkNamesPath = 'configuration.customdata.bulkNames';
|
|
514
|
+
const sizeIdentifiers = reduce(
|
|
515
|
+
get(action, 'variant.sizes', []),
|
|
516
|
+
(identifiers, size) => (size?.identifier ? [...identifiers, size.identifier] : identifiers),
|
|
517
|
+
[]
|
|
518
|
+
);
|
|
519
|
+
const nextState = immutableSet(
|
|
520
|
+
state,
|
|
521
|
+
selectedAmountsPath,
|
|
522
|
+
omit(get(state, selectedAmountsPath, {}), sizeIdentifiers)
|
|
523
|
+
);
|
|
524
|
+
return immutableSet(
|
|
525
|
+
nextState,
|
|
526
|
+
bulkNamesPath,
|
|
527
|
+
omit(get(state, bulkNamesPath, {}), sizeIdentifiers)
|
|
528
|
+
);
|
|
485
529
|
}
|
|
486
530
|
default:
|
|
487
531
|
return state;
|
|
@@ -530,11 +574,17 @@ export function configurationModifierHandler(state, action) {
|
|
|
530
574
|
}
|
|
531
575
|
case actionTypes.DELETE_SELECTED_AMOUNT: {
|
|
532
576
|
const selectedAmountsPath = 'configuration.customdata.selectedAmounts';
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
577
|
+
const bulkNamesPath = 'configuration.customdata.bulkNames';
|
|
578
|
+
const nextState = immutableSet(
|
|
579
|
+
state,
|
|
580
|
+
selectedAmountsPath,
|
|
581
|
+
omit(get(state, selectedAmountsPath, {}), action.identifier)
|
|
582
|
+
);
|
|
583
|
+
return immutableSet(
|
|
584
|
+
nextState,
|
|
585
|
+
bulkNamesPath,
|
|
586
|
+
omit(get(state, bulkNamesPath, {}), action.identifier)
|
|
587
|
+
);
|
|
538
588
|
}
|
|
539
589
|
case actionTypes.SET_SELECTED_CALCULATION: {
|
|
540
590
|
return immutableSet(
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CLEAR_BULKNAMES,
|
|
8
8
|
SAVE_BULKNAMES,
|
|
9
9
|
SET_SELECTED_AMOUNTS,
|
|
10
|
+
DELETE_SELECTED_AMOUNT,
|
|
10
11
|
DELETE_SELECTED_AMOUNTS,
|
|
11
12
|
skipNextAutoResolve,
|
|
12
13
|
RECEIVE_CALCULATION,
|
|
@@ -145,37 +146,236 @@ describe('Reducers/Configurator/Reducer', () => {
|
|
|
145
146
|
hoodie_blue_s: 2,
|
|
146
147
|
});
|
|
147
148
|
});
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
149
|
+
describe('bulkNames stay in sync with selectedAmounts', () => {
|
|
150
|
+
test('SET_SELECTED_AMOUNT with amount 0 removes bulkNames for that identifier', () => {
|
|
151
|
+
let state = configuratorReducer(initialState, {
|
|
152
|
+
type: SET_SELECTED_AMOUNT,
|
|
153
|
+
identifier: 'hoodie_blue_s',
|
|
154
|
+
amount: 2,
|
|
155
|
+
});
|
|
156
|
+
state = configuratorReducer(state, {
|
|
157
|
+
type: SAVE_BULKNAMES,
|
|
158
|
+
identifier: 'hoodie_blue_s',
|
|
159
|
+
bulkNamesData: ['Alex', 'Leon'],
|
|
160
|
+
});
|
|
161
|
+
state = configuratorReducer(state, {
|
|
162
|
+
type: SET_SELECTED_AMOUNT,
|
|
163
|
+
identifier: 'hoodie_blue_s',
|
|
164
|
+
amount: 0,
|
|
165
|
+
});
|
|
166
|
+
expect(get(state, 'configuration.customdata.selectedAmounts')).toEqual({});
|
|
167
|
+
expect(get(state, 'configuration.customdata.bulkNames')).toEqual({});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('DELETE_SELECTED_AMOUNT removes bulkNames for that identifier', () => {
|
|
171
|
+
let state = configuratorReducer(initialState, {
|
|
172
|
+
type: SET_SELECTED_AMOUNT,
|
|
173
|
+
identifier: 'hoodie_blue_s',
|
|
174
|
+
amount: 2,
|
|
175
|
+
});
|
|
176
|
+
state = configuratorReducer(state, {
|
|
177
|
+
type: SAVE_BULKNAMES,
|
|
178
|
+
identifier: 'hoodie_blue_s',
|
|
179
|
+
bulkNamesData: ['Alex', 'Leon'],
|
|
180
|
+
});
|
|
181
|
+
state = configuratorReducer(state, {
|
|
182
|
+
type: SAVE_BULKNAMES,
|
|
183
|
+
identifier: 'hoodie_green_m',
|
|
184
|
+
bulkNamesData: ['Lea', 'Martin'],
|
|
185
|
+
});
|
|
186
|
+
state = configuratorReducer(state, {
|
|
187
|
+
type: DELETE_SELECTED_AMOUNT,
|
|
188
|
+
identifier: 'hoodie_blue_s',
|
|
189
|
+
});
|
|
190
|
+
expect(get(state, 'configuration.customdata.selectedAmounts')).toEqual({});
|
|
191
|
+
expect(get(state, 'configuration.customdata.bulkNames')).toEqual({
|
|
192
|
+
hoodie_green_m: ['Lea', 'Martin'],
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('DELETE_SELECTED_AMOUNTS clears bulkNames for all variant sizes', () => {
|
|
197
|
+
const variant = {
|
|
198
|
+
identifier: 'hoodie_blue_s',
|
|
199
|
+
title: 'blau',
|
|
200
|
+
url: 'http://localhost:10030/images/item/thumb/hoodie_blue_s.jpg',
|
|
201
|
+
sizes: [
|
|
202
|
+
{
|
|
203
|
+
identifier: 'hoodie_blue_s',
|
|
204
|
+
amount: 1,
|
|
205
|
+
size: 'Größe S',
|
|
206
|
+
key: 'hoodie_blue_s',
|
|
207
|
+
label: 'Größe S',
|
|
208
|
+
url: 'http://localhost:10030/images/item/thumb/hoodie_blue_s.jpg',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
identifier: 'hoodie_green_m',
|
|
212
|
+
amount: 1,
|
|
213
|
+
size: 'Größe M',
|
|
214
|
+
key: 'hoodie_green_m',
|
|
215
|
+
label: 'Größe M',
|
|
216
|
+
url: 'http://localhost:10030/images/item/thumb/hoodie_green_m.jpg',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
const sizes = { hoodie_blue_s: 0 };
|
|
221
|
+
let state = configuratorReducer(stateWithSelectedAmounts, {
|
|
222
|
+
type: SAVE_BULKNAMES,
|
|
223
|
+
identifier: 'hoodie_blue_s',
|
|
224
|
+
bulkNamesData: ['Alex', 'Leon'],
|
|
225
|
+
});
|
|
226
|
+
state = configuratorReducer(state, {
|
|
227
|
+
type: SAVE_BULKNAMES,
|
|
228
|
+
identifier: 'hoodie_green_m',
|
|
229
|
+
bulkNamesData: ['Lea', 'Martin'],
|
|
230
|
+
});
|
|
231
|
+
state = configuratorReducer(state, {
|
|
232
|
+
type: DELETE_SELECTED_AMOUNTS,
|
|
233
|
+
variant: variant,
|
|
234
|
+
sizes: sizes,
|
|
235
|
+
});
|
|
236
|
+
expect(get(state, 'configuration.customdata.selectedAmounts')).toEqual({});
|
|
237
|
+
expect(get(state, 'configuration.customdata.bulkNames')).toEqual({});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('SET_SELECTED_AMOUNTS removes bulkNames for sizes set to 0', () => {
|
|
241
|
+
const variant = {
|
|
242
|
+
identifier: 'hoodie_blue_s',
|
|
243
|
+
sizes: [{ identifier: 'hoodie_blue_s' }, { identifier: 'hoodie_green_m' }],
|
|
244
|
+
};
|
|
245
|
+
let state = configuratorReducer(initialState, {
|
|
246
|
+
type: SET_SELECTED_AMOUNTS,
|
|
247
|
+
variant,
|
|
248
|
+
sizes: { hoodie_green_m: 5, hoodie_blue_s: 2 },
|
|
249
|
+
});
|
|
250
|
+
state = configuratorReducer(state, {
|
|
251
|
+
type: SAVE_BULKNAMES,
|
|
252
|
+
identifier: 'hoodie_blue_s',
|
|
253
|
+
bulkNamesData: ['Alex', 'Leon'],
|
|
254
|
+
});
|
|
255
|
+
state = configuratorReducer(state, {
|
|
256
|
+
type: SAVE_BULKNAMES,
|
|
257
|
+
identifier: 'hoodie_green_m',
|
|
258
|
+
bulkNamesData: ['Lea', 'Martin'],
|
|
259
|
+
});
|
|
260
|
+
state = configuratorReducer(state, {
|
|
261
|
+
type: SET_SELECTED_AMOUNTS,
|
|
262
|
+
variant,
|
|
263
|
+
sizes: { hoodie_blue_s: 0 },
|
|
264
|
+
});
|
|
265
|
+
expect(get(state, 'configuration.customdata.selectedAmounts')).toEqual({ hoodie_green_m: 5 });
|
|
266
|
+
expect(get(state, 'configuration.customdata.bulkNames')).toEqual({
|
|
267
|
+
hoodie_green_m: ['Lea', 'Martin'],
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('SWITCH_SELECTED_AMOUNT moves bulkNames from src to dst identifier', () => {
|
|
272
|
+
let state = configuratorReducer(initialState, {
|
|
273
|
+
type: SET_SELECTED_AMOUNT,
|
|
274
|
+
identifier: 'shirt_red_m',
|
|
275
|
+
amount: 2,
|
|
276
|
+
});
|
|
277
|
+
state = configuratorReducer(state, {
|
|
278
|
+
type: SAVE_BULKNAMES,
|
|
279
|
+
identifier: 'shirt_red_m',
|
|
280
|
+
bulkNamesData: ['Alex', 'Leon'],
|
|
281
|
+
});
|
|
282
|
+
state = configuratorReducer(state, {
|
|
283
|
+
type: SWITCH_SELECTED_AMOUNT,
|
|
284
|
+
srcIdentifier: 'shirt_red_m',
|
|
285
|
+
dstIdentifier: 'shirt_blue_m',
|
|
286
|
+
});
|
|
287
|
+
expect(get(state, 'configuration.customdata.selectedAmounts')).toEqual({ shirt_blue_m: 2 });
|
|
288
|
+
expect(get(state, 'configuration.customdata.bulkNames')).toEqual({
|
|
289
|
+
shirt_blue_m: ['Alex', 'Leon'],
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test('SWITCH_SELECTED_AMOUNT is a no-op when src is not in selectedAmounts', () => {
|
|
294
|
+
const state = configuratorReducer(initialState, {
|
|
295
|
+
type: SAVE_BULKNAMES,
|
|
296
|
+
identifier: 'shirt_red_m',
|
|
297
|
+
bulkNamesData: ['Alex'],
|
|
298
|
+
});
|
|
299
|
+
const next = configuratorReducer(state, {
|
|
300
|
+
type: SWITCH_SELECTED_AMOUNT,
|
|
301
|
+
srcIdentifier: 'shirt_red_m',
|
|
302
|
+
dstIdentifier: 'shirt_blue_m',
|
|
303
|
+
});
|
|
304
|
+
expect(next).toBe(state);
|
|
305
|
+
expect(get(next, 'configuration.customdata.bulkNames')).toEqual({ shirt_red_m: ['Alex'] });
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('SWITCH_SELECTED_AMOUNT is a no-op when src and dst are the same identifier', () => {
|
|
309
|
+
let state = configuratorReducer(initialState, {
|
|
310
|
+
type: SET_SELECTED_AMOUNT,
|
|
311
|
+
identifier: 'shirt_red_m',
|
|
312
|
+
amount: 2,
|
|
313
|
+
});
|
|
314
|
+
state = configuratorReducer(state, {
|
|
315
|
+
type: SAVE_BULKNAMES,
|
|
316
|
+
identifier: 'shirt_red_m',
|
|
317
|
+
bulkNamesData: ['Alex', 'Leon'],
|
|
318
|
+
});
|
|
319
|
+
const next = configuratorReducer(state, {
|
|
320
|
+
type: SWITCH_SELECTED_AMOUNT,
|
|
321
|
+
srcIdentifier: 'shirt_red_m',
|
|
322
|
+
dstIdentifier: 'shirt_red_m',
|
|
323
|
+
});
|
|
324
|
+
expect(next).toBe(state);
|
|
325
|
+
expect(get(next, 'configuration.customdata.selectedAmounts')).toEqual({ shirt_red_m: 2 });
|
|
326
|
+
expect(get(next, 'configuration.customdata.bulkNames')).toEqual({
|
|
327
|
+
shirt_red_m: ['Alex', 'Leon'],
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('SWITCH_SELECTED_AMOUNT moves amount and clears dst bulkNames when src has no names', () => {
|
|
332
|
+
let state = configuratorReducer(initialState, {
|
|
333
|
+
type: SET_SELECTED_AMOUNT,
|
|
334
|
+
identifier: 'shirt_red_m',
|
|
335
|
+
amount: 2,
|
|
336
|
+
});
|
|
337
|
+
state = configuratorReducer(state, {
|
|
338
|
+
type: SAVE_BULKNAMES,
|
|
339
|
+
identifier: 'shirt_blue_m',
|
|
340
|
+
bulkNamesData: ['Lea'],
|
|
341
|
+
});
|
|
342
|
+
state = configuratorReducer(state, {
|
|
343
|
+
type: SWITCH_SELECTED_AMOUNT,
|
|
344
|
+
srcIdentifier: 'shirt_red_m',
|
|
345
|
+
dstIdentifier: 'shirt_blue_m',
|
|
346
|
+
});
|
|
347
|
+
expect(get(state, 'configuration.customdata.selectedAmounts')).toEqual({ shirt_blue_m: 2 });
|
|
348
|
+
expect(get(state, 'configuration.customdata.bulkNames')).toEqual({});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('SET_SELECTED_AMOUNTS removes all bulkNames when every size is set to 0', () => {
|
|
352
|
+
const variant = {
|
|
353
|
+
identifier: 'hoodie_blue_s',
|
|
354
|
+
sizes: [{ identifier: 'hoodie_blue_s' }, { identifier: 'hoodie_green_m' }],
|
|
355
|
+
};
|
|
356
|
+
let state = configuratorReducer(initialState, {
|
|
357
|
+
type: SET_SELECTED_AMOUNTS,
|
|
358
|
+
variant,
|
|
359
|
+
sizes: { hoodie_green_m: 5, hoodie_blue_s: 2 },
|
|
360
|
+
});
|
|
361
|
+
state = configuratorReducer(state, {
|
|
362
|
+
type: SAVE_BULKNAMES,
|
|
363
|
+
identifier: 'hoodie_blue_s',
|
|
364
|
+
bulkNamesData: ['Alex'],
|
|
365
|
+
});
|
|
366
|
+
state = configuratorReducer(state, {
|
|
367
|
+
type: SAVE_BULKNAMES,
|
|
368
|
+
identifier: 'hoodie_green_m',
|
|
369
|
+
bulkNamesData: ['Lea'],
|
|
370
|
+
});
|
|
371
|
+
state = configuratorReducer(state, {
|
|
372
|
+
type: SET_SELECTED_AMOUNTS,
|
|
373
|
+
variant,
|
|
374
|
+
sizes: { hoodie_blue_s: 0, hoodie_green_m: 0 },
|
|
375
|
+
});
|
|
376
|
+
expect(get(state, 'configuration.customdata.selectedAmounts')).toEqual({});
|
|
377
|
+
expect(get(state, 'configuration.customdata.bulkNames')).toEqual({});
|
|
177
378
|
});
|
|
178
|
-
expect(get(stateWithSelectedAmounts, 'configuration.customdata.selectedAmounts')).toEqual({});
|
|
179
379
|
});
|
|
180
380
|
|
|
181
381
|
test('returns the correct state for SWITCH_SELECTED_AMOUNT action', () => {
|
|
@@ -646,6 +646,32 @@ export const parseColorData = jsonStr => {
|
|
|
646
646
|
};
|
|
647
647
|
};
|
|
648
648
|
|
|
649
|
+
/**
|
|
650
|
+
* Normalizes canvas text color entries (style.colors or text editor) for HTML generation.
|
|
651
|
+
* @param {{ identifier: string|object, value: string }|null|undefined} color
|
|
652
|
+
* @returns {{ identifier: string, value: string }|null}
|
|
653
|
+
*/
|
|
654
|
+
export const normalizeTextColorData = color => {
|
|
655
|
+
if (!color?.identifier || !color?.value) {
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
const identifier =
|
|
659
|
+
typeof color.identifier === 'string' ? color.identifier : JSON.stringify(color.identifier);
|
|
660
|
+
return { identifier, value: color.value.toLowerCase() };
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Color from canvas object style.colors (parsed from content HTML).
|
|
665
|
+
* @param {{ styleColors?: Array }} params
|
|
666
|
+
* @returns {{ identifier: string, value: string }|null}
|
|
667
|
+
*/
|
|
668
|
+
export const getColorDataForTextContent = ({ styleColors }) => {
|
|
669
|
+
if (styleColors?.length > 0) {
|
|
670
|
+
return normalizeTextColorData(styleColors[0]);
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
};
|
|
674
|
+
|
|
649
675
|
export const getCameraViewListForDesignAreaList = (filteredDesignAreas, designedAreas) =>
|
|
650
676
|
['default'].concat(
|
|
651
677
|
filteredDesignAreas
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
getColorsFromCanvasDataWithPalette,
|
|
21
21
|
createColorData,
|
|
22
22
|
parseColorData,
|
|
23
|
+
normalizeTextColorData,
|
|
24
|
+
getColorDataForTextContent,
|
|
23
25
|
isActiveBulkName,
|
|
24
26
|
getDefaultDesignAreaIdentifierFromProductionMethod,
|
|
25
27
|
getDefaultColorsFromDesignProductionMethod,
|
|
@@ -1128,6 +1130,23 @@ describe('Reducers/DesignArea/Selectors', () => {
|
|
|
1128
1130
|
});
|
|
1129
1131
|
});
|
|
1130
1132
|
|
|
1133
|
+
test('getColorDataForTextContent returns color from styleColors', () => {
|
|
1134
|
+
const styleColors = [
|
|
1135
|
+
{
|
|
1136
|
+
identifier: '{"color":"blue","palette":"default"}',
|
|
1137
|
+
value: '#0078b8',
|
|
1138
|
+
},
|
|
1139
|
+
];
|
|
1140
|
+
expect(getColorDataForTextContent({ styleColors })).toEqual(styleColors[0]);
|
|
1141
|
+
expect(getColorDataForTextContent({ styleColors: [] })).toBeNull();
|
|
1142
|
+
expect(
|
|
1143
|
+
normalizeTextColorData({ identifier: { color: 'red', palette: 'default' }, value: '#F73429' })
|
|
1144
|
+
).toEqual({
|
|
1145
|
+
identifier: '{"color":"red","palette":"default"}',
|
|
1146
|
+
value: '#f73429',
|
|
1147
|
+
});
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1131
1150
|
test('get default design area from the flag isDefault', () => {
|
|
1132
1151
|
const state = {
|
|
1133
1152
|
designArea: {
|
|
@@ -11,6 +11,30 @@ export function getDefaultFont(state) {
|
|
|
11
11
|
return find(fonts, { isDefault: true }) || first(fonts);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
const stringifyFontParams = fontParams =>
|
|
15
|
+
Object.keys(fontParams)
|
|
16
|
+
.map(prop => `${prop}: ${fontParams[prop]};`)
|
|
17
|
+
.join(' ');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Inline font CSS from the text toolbar selection (font family + bold/italic).
|
|
21
|
+
* @param {{ fontItem?: { family?: string }, isBold?: boolean, isItalic?: boolean }} params
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
export const getTextContentFontParamsFromSelection = ({ fontItem, isBold, isItalic }) => {
|
|
25
|
+
if (!fontItem?.family) {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
const fontParams = {
|
|
29
|
+
'font-family': `"${escapeQuotes(fontItem.family)}"`,
|
|
30
|
+
'font-weight': isBold ? 'bold' : 'normal',
|
|
31
|
+
'font-style': isItalic ? 'italic' : 'normal',
|
|
32
|
+
};
|
|
33
|
+
return Object.keys(fontParams)
|
|
34
|
+
.map(prop => `${prop}: ${fontParams[prop]};`)
|
|
35
|
+
.join(' ');
|
|
36
|
+
};
|
|
37
|
+
|
|
14
38
|
export const getDefaultFontParams = (state, stringify = true) => {
|
|
15
39
|
const defaultFont = getDefaultFont(state);
|
|
16
40
|
const fontParams = {
|
|
@@ -18,11 +42,7 @@ export const getDefaultFontParams = (state, stringify = true) => {
|
|
|
18
42
|
'font-weight': 'normal',
|
|
19
43
|
'font-style': 'normal',
|
|
20
44
|
};
|
|
21
|
-
return stringify
|
|
22
|
-
? Object.keys(fontParams)
|
|
23
|
-
.map(prop => `${prop}: ${fontParams[prop]};`)
|
|
24
|
-
.join(' ')
|
|
25
|
-
: fontParams;
|
|
45
|
+
return stringify ? stringifyFontParams(fontParams) : fontParams;
|
|
26
46
|
};
|
|
27
47
|
|
|
28
48
|
export const getFontMapping = createSelector(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import { getDefaultFont } from '../Selectors';
|
|
3
|
+
import { getDefaultFont, getTextContentFontParamsFromSelection } from '../Selectors';
|
|
4
4
|
|
|
5
5
|
describe('Reducers/Fonts/Selectors', () => {
|
|
6
6
|
test('getDefaultFont returns the correct font object (isDefault is true)', () => {
|
|
@@ -56,4 +56,17 @@ describe('Reducers/Fonts/Selectors', () => {
|
|
|
56
56
|
isDefault: false,
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
|
+
|
|
60
|
+
it('getTextContentFontParamsFromSelection builds inline font CSS', () => {
|
|
61
|
+
expect(
|
|
62
|
+
getTextContentFontParamsFromSelection({
|
|
63
|
+
fontItem: { family: 'Open Sans' },
|
|
64
|
+
isBold: true,
|
|
65
|
+
isItalic: false,
|
|
66
|
+
})
|
|
67
|
+
).toBe('font-family: "Open Sans"; font-weight: bold; font-style: normal;');
|
|
68
|
+
expect(
|
|
69
|
+
getTextContentFontParamsFromSelection({ fontItem: null, isBold: false, isItalic: false })
|
|
70
|
+
).toBe('');
|
|
71
|
+
});
|
|
59
72
|
});
|
|
@@ -29,11 +29,12 @@ import {
|
|
|
29
29
|
getFontSizeMin,
|
|
30
30
|
hasPlaceHolderImagesForActiveDesignArea,
|
|
31
31
|
getVectorizedImageColor,
|
|
32
|
+
getColorDataForTextContent,
|
|
32
33
|
getElementLockState,
|
|
33
34
|
getSelectedDesignProductionMethod,
|
|
34
35
|
getDesignDataForSelectedDesignArea,
|
|
35
36
|
} from '../Reducers/DesignArea/Selectors';
|
|
36
|
-
import { getDefaultFontParams } from '../Reducers/Fonts/Selectors';
|
|
37
|
+
import { getDefaultFontParams, getTextContentFontParamsFromSelection } from '../Reducers/Fonts/Selectors';
|
|
37
38
|
import { createTextPlaceholder } from '../Reducers/ImageGallery/Modifiers';
|
|
38
39
|
import { updateLinkedImagesWithImageData } from '../Reducers/DesignData/Actions';
|
|
39
40
|
import { checkDesignElementRestrictions } from '../Reducers/DesignArea/DesignElementSelectors';
|
|
@@ -334,21 +335,39 @@ export default class DesignerService {
|
|
|
334
335
|
* @returns {string}
|
|
335
336
|
*/
|
|
336
337
|
createColorizedTextContent(str, colorCount = 1, options = {}) {
|
|
337
|
-
const {
|
|
338
|
+
const {
|
|
339
|
+
withPlaceholder = true,
|
|
340
|
+
styleColors = null,
|
|
341
|
+
fontItem = null,
|
|
342
|
+
isBold = false,
|
|
343
|
+
isItalic = false,
|
|
344
|
+
} = options;
|
|
338
345
|
|
|
339
346
|
const state = Services.store.state;
|
|
340
|
-
const
|
|
347
|
+
const selectionFontParams = getTextContentFontParamsFromSelection({
|
|
348
|
+
fontItem,
|
|
349
|
+
isBold,
|
|
350
|
+
isItalic,
|
|
351
|
+
});
|
|
352
|
+
const fontParams = selectionFontParams || getDefaultFontParams(state);
|
|
341
353
|
const chunkLength = Math.ceil(str.length / colorCount);
|
|
342
354
|
|
|
343
355
|
const content = str
|
|
344
356
|
.match(new RegExp(`.{1,${chunkLength}}`, 'g'))
|
|
345
357
|
.map((chunk, idx) => {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
358
|
+
let colorData = getColorDataForTextContent({ styleColors });
|
|
359
|
+
if (!colorData) {
|
|
360
|
+
const color = getDefaultTextColor(state, colorCount > 1 ? idx : null);
|
|
361
|
+
const lastImageColor = getVectorizedImageColor(state);
|
|
362
|
+
colorData = isEmpty(lastImageColor)
|
|
363
|
+
? createColorData(
|
|
364
|
+
color.identifier,
|
|
365
|
+
color.paletteIdentifier,
|
|
366
|
+
color.custom,
|
|
367
|
+
color.value
|
|
368
|
+
)
|
|
369
|
+
: lastImageColor;
|
|
370
|
+
}
|
|
352
371
|
|
|
353
372
|
return `<span style='color: ${colorData.value}; ${fontParams}' data-color='${colorData.identifier}'>${chunk}</span>`;
|
|
354
373
|
})
|