orc-shared 1.6.0-dev.1 → 1.6.0-dev.10
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/actions/globalErrorMessages.js +67 -0
- package/dist/actions/makeApiAction.js +1 -1
- package/dist/buildStore.js +3 -1
- package/dist/components/MaterialUI/DataDisplay/PredefinedElements/GlobalErrorMessages.js +134 -0
- package/dist/components/MaterialUI/DataDisplay/PredefinedElements/LookupDisplayValue.js +84 -0
- package/dist/components/MaterialUI/DataDisplay/PredefinedElements/StepperModal.js +2 -1
- package/dist/components/MaterialUI/DataDisplay/Table.js +2 -2
- package/dist/components/MaterialUI/Inputs/InputBase.js +97 -15
- package/dist/components/MaterialUI/Inputs/InputBaseProps.js +3 -1
- package/dist/components/MaterialUI/hocs/withDeferredPopper.js +1 -1
- package/dist/components/Routing/Page.js +4 -1
- package/dist/components/Routing/SegmentPage.js +4 -1
- package/dist/components/Routing/SubPage.js +11 -13
- package/dist/components/Scope/useScopeConfirmationModalState.js +1 -1
- package/dist/hooks/useDispatchWithErrorHandling.js +106 -0
- package/dist/hooks/useNotificationRequestState.js +2 -2
- package/dist/reducers/globalErrorMessages.js +79 -0
- package/dist/reducers/request.js +2 -1
- package/dist/reducers/scopes.js +3 -0
- package/dist/selectors/authentication.js +2 -2
- package/dist/selectors/globalErrorMessages.js +58 -0
- package/dist/selectors/metadata.js +2 -2
- package/dist/utils/buildUrl.js +1 -1
- package/dist/utils/responseProcessingHelper.js +87 -0
- package/package.json +7 -4
- package/src/actions/globalErrorMessages.js +12 -0
- package/src/actions/globalErrorMessages.test.js +21 -0
- package/src/buildStore.js +2 -0
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/GlobalErrorMessages.js +93 -0
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/GlobalErrorMessages.test.js +472 -0
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/LookupDisplayValue.js +18 -0
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/LookupDisplayValue.test.js +121 -0
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/StepperModal.js +2 -1
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/StepperModal.test.js +20 -0
- package/src/components/MaterialUI/Inputs/InputBase.js +97 -15
- package/src/components/MaterialUI/Inputs/InputBase.test.js +339 -3
- package/src/components/MaterialUI/Inputs/InputBaseProps.js +2 -0
- package/src/components/MaterialUI/Inputs/InputBaseProps.test.js +2 -0
- package/src/components/Routing/Page.js +12 -1
- package/src/components/Routing/SegmentPage.js +12 -1
- package/src/components/Routing/SubPage.js +4 -8
- package/src/components/Routing/SubPage.test.js +46 -0
- package/src/hooks/useDispatchWithErrorHandling.js +57 -0
- package/src/hooks/useDispatchWithErrorHandling.test.js +230 -0
- package/src/reducers/globalErrorMessages.js +25 -0
- package/src/reducers/globalErrorMessages.test.js +66 -0
- package/src/reducers/request.js +2 -1
- package/src/reducers/request.test.js +23 -0
- package/src/reducers/scopes.js +3 -0
- package/src/reducers/scopes.test.js +47 -0
- package/src/selectors/globalErrorMessages.js +11 -0
- package/src/selectors/globalErrorMessages.test.js +25 -0
- package/src/selectors/metadata.js +1 -1
- package/src/selectors/metadata.test.js +12 -0
- package/src/utils/buildUrl.js +1 -1
- package/src/utils/responseProcessingHelper.js +42 -0
- package/src/utils/responseProcessingHelper.test.js +218 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { mount } from "enzyme";
|
|
3
|
-
import InputBase from "./InputBase";
|
|
3
|
+
import InputBase, { AdvancedNumericInput } from "./InputBase";
|
|
4
4
|
import InputBaseMUI from "@material-ui/core/InputBase";
|
|
5
5
|
import sinon from "sinon";
|
|
6
6
|
import { ignoreConsoleError } from "../../../utils/testUtils";
|
|
7
7
|
import InputBaseProps from "./InputBaseProps";
|
|
8
8
|
import { act } from "unexpected-reaction";
|
|
9
|
+
import { fireEvent, render, getByDisplayValue } from "@testing-library/react";
|
|
9
10
|
|
|
10
11
|
describe("InputBase Component", () => {
|
|
11
12
|
let update, container;
|
|
@@ -71,7 +72,7 @@ describe("InputBase Component", () => {
|
|
|
71
72
|
const component = <InputBase inputProps={inputProps} />;
|
|
72
73
|
|
|
73
74
|
const mountedComponent = mount(component);
|
|
74
|
-
const expected = <InputBaseMUI value={aValue} title={aValue}
|
|
75
|
+
const expected = <InputBaseMUI value={aValue} title={aValue} minRows={defaultRows} />;
|
|
75
76
|
|
|
76
77
|
expect(mountedComponent.containsMatchingElement(expected), "to be truthy");
|
|
77
78
|
});
|
|
@@ -91,7 +92,7 @@ describe("InputBase Component", () => {
|
|
|
91
92
|
const component = <InputBase inputProps={inputProps} />;
|
|
92
93
|
|
|
93
94
|
const mountedComponent = mount(component);
|
|
94
|
-
const expected = <InputBaseMUI value={aValue} title={aValue}
|
|
95
|
+
const expected = <InputBaseMUI value={aValue} title={aValue} minRows={desiredNumberOfRows} />;
|
|
95
96
|
|
|
96
97
|
expect(mountedComponent.containsMatchingElement(expected), "to be truthy");
|
|
97
98
|
});
|
|
@@ -285,6 +286,35 @@ describe("InputBase Component", () => {
|
|
|
285
286
|
|
|
286
287
|
expect(mountedComponent.containsMatchingElement(expected), "to be truthy");
|
|
287
288
|
});
|
|
289
|
+
|
|
290
|
+
it("Change value on blur with pending timer", () => {
|
|
291
|
+
window.bypassDebounce = false;
|
|
292
|
+
const inputProps = new InputBaseProps();
|
|
293
|
+
const aLabel = "aLabel";
|
|
294
|
+
const aValue = "12.2";
|
|
295
|
+
const metadata = {
|
|
296
|
+
test: "value",
|
|
297
|
+
};
|
|
298
|
+
const onBlur = sinon.spy().named("blur");
|
|
299
|
+
|
|
300
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
301
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
302
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
303
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
304
|
+
inputProps.set(InputBaseProps.propNames.onBlur, onBlur);
|
|
305
|
+
inputProps.set(InputBaseProps.propNames.timeoutDelay, 1000);
|
|
306
|
+
|
|
307
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
308
|
+
const mountedComponent = mount(component);
|
|
309
|
+
const input = mountedComponent.find("input");
|
|
310
|
+
|
|
311
|
+
input.simulate("change", { target: { value: "13", focus: () => {} } });
|
|
312
|
+
input.simulate("blur", {});
|
|
313
|
+
|
|
314
|
+
expect(onBlur, "was called once");
|
|
315
|
+
expect(update, "not to have calls satisfying", [{ args: [aValue, metadata] }]);
|
|
316
|
+
expect(update, "to have calls satisfying", [{ args: ["13", metadata] }]);
|
|
317
|
+
});
|
|
288
318
|
});
|
|
289
319
|
|
|
290
320
|
describe("InputBase component debouce", () => {
|
|
@@ -350,3 +380,309 @@ describe("InputBase component debouce", () => {
|
|
|
350
380
|
expect(input.get(0).props.value, "to equal", "");
|
|
351
381
|
});
|
|
352
382
|
});
|
|
383
|
+
|
|
384
|
+
describe("AdvancedNumericInput", () => {
|
|
385
|
+
const noop = function () {};
|
|
386
|
+
let update, container;
|
|
387
|
+
beforeEach(() => {
|
|
388
|
+
window.bypassDebounce = true;
|
|
389
|
+
jest.useFakeTimers();
|
|
390
|
+
|
|
391
|
+
container = document.createElement("div");
|
|
392
|
+
document.body.appendChild(container);
|
|
393
|
+
update = sinon.spy().named("update");
|
|
394
|
+
});
|
|
395
|
+
afterEach(() => {
|
|
396
|
+
jest.useRealTimers();
|
|
397
|
+
delete window.bypassDebounce;
|
|
398
|
+
document.body.removeChild(container);
|
|
399
|
+
container = null;
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("Renders InputBase component as advanced numeric input", () => {
|
|
403
|
+
const inputProps = new InputBaseProps();
|
|
404
|
+
const aLabel = "aLabel";
|
|
405
|
+
const aValue = "value";
|
|
406
|
+
|
|
407
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
408
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
409
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
410
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
411
|
+
|
|
412
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
413
|
+
|
|
414
|
+
const mountedComponent = mount(component);
|
|
415
|
+
const expected = <InputBaseMUI value={aValue} title="" inputComponent={AdvancedNumericInput} />;
|
|
416
|
+
|
|
417
|
+
expect(mountedComponent.containsMatchingElement(expected), "to be truthy");
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("Renders InputBase component as advanced numeric input with custom numeric props", () => {
|
|
421
|
+
const inputProps = new InputBaseProps();
|
|
422
|
+
const aLabel = "aLabel";
|
|
423
|
+
const aValue = "value";
|
|
424
|
+
|
|
425
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
426
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
427
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
428
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
429
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
430
|
+
|
|
431
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
432
|
+
|
|
433
|
+
const mountedComponent = mount(component);
|
|
434
|
+
|
|
435
|
+
const advInput = mountedComponent.find("AdvancedNumericInput");
|
|
436
|
+
expect(advInput.props().decimalScale, "to be", 2);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("Change advanced numeric input value", () => {
|
|
440
|
+
const inputProps = new InputBaseProps();
|
|
441
|
+
const aLabel = "aLabel";
|
|
442
|
+
const aValue = "12.2";
|
|
443
|
+
const metadata = {
|
|
444
|
+
test: "value",
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
448
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
449
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
450
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
451
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
452
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
453
|
+
|
|
454
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
455
|
+
const mountedComponent = mount(component);
|
|
456
|
+
const input = mountedComponent.find("input");
|
|
457
|
+
input.simulate("change", { target: { value: "13", focus: noop } });
|
|
458
|
+
|
|
459
|
+
expect(update, "to have calls satisfying", [{ args: ["13", metadata] }]);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it("Change advanced numeric input on blur without custom blur method", () => {
|
|
463
|
+
const inputProps = new InputBaseProps();
|
|
464
|
+
const aLabel = "aLabel";
|
|
465
|
+
const aValue = "12.2";
|
|
466
|
+
const metadata = {
|
|
467
|
+
test: "value",
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
471
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
472
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
473
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
474
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
475
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
476
|
+
|
|
477
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
478
|
+
const mountedComponent = mount(component);
|
|
479
|
+
const input = mountedComponent.find("input");
|
|
480
|
+
|
|
481
|
+
input.simulate("blur", {});
|
|
482
|
+
|
|
483
|
+
expect(update, "to have calls satisfying", [{ args: ["12.20", metadata] }]);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("Onblur send a formatted string instead of a number", () => {
|
|
487
|
+
const inputProps = new InputBaseProps();
|
|
488
|
+
const aLabel = "aLabel";
|
|
489
|
+
const aValue = 12.2;
|
|
490
|
+
const metadata = {
|
|
491
|
+
test: "value",
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
495
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
496
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
497
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
498
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
499
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
500
|
+
|
|
501
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
502
|
+
const mountedComponent = mount(component);
|
|
503
|
+
const input = mountedComponent.find("input");
|
|
504
|
+
|
|
505
|
+
input.simulate("blur", {});
|
|
506
|
+
|
|
507
|
+
expect(update, "to have calls satisfying", [{ args: ["12.20", metadata] }]);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it("Onblur send a formatted string with default numeric input props", () => {
|
|
511
|
+
const inputProps = new InputBaseProps();
|
|
512
|
+
const aLabel = "aLabel";
|
|
513
|
+
const aValue = 12.2;
|
|
514
|
+
const metadata = {
|
|
515
|
+
test: "value",
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
519
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
520
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
521
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
522
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
523
|
+
|
|
524
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
525
|
+
const mountedComponent = mount(component);
|
|
526
|
+
const input = mountedComponent.find("input");
|
|
527
|
+
|
|
528
|
+
input.simulate("blur", {});
|
|
529
|
+
|
|
530
|
+
expect(update, "to have calls satisfying", [{ args: ["12.2", metadata] }]);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
it("Onblur send an unformatted string when blurFormattingSkipFixedDecimalScale is true", () => {
|
|
534
|
+
const inputProps = new InputBaseProps();
|
|
535
|
+
const aLabel = "aLabel";
|
|
536
|
+
const aValue = 12.2;
|
|
537
|
+
const metadata = {
|
|
538
|
+
test: "value",
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
542
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
543
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
544
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
545
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, {
|
|
546
|
+
decimalScale: 2,
|
|
547
|
+
blurFormattingSkipFixedDecimalScale: true,
|
|
548
|
+
});
|
|
549
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
550
|
+
|
|
551
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
552
|
+
const mountedComponent = mount(component);
|
|
553
|
+
const input = mountedComponent.find("input");
|
|
554
|
+
|
|
555
|
+
input.simulate("blur", {});
|
|
556
|
+
|
|
557
|
+
expect(update, "to have calls satisfying", [{ args: ["12.2", metadata] }]);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it("Onblur send an empty string when the initial value is empty", () => {
|
|
561
|
+
const inputProps = new InputBaseProps();
|
|
562
|
+
const aLabel = "aLabel";
|
|
563
|
+
const aValue = null;
|
|
564
|
+
const metadata = {
|
|
565
|
+
test: "value",
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
569
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
570
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
571
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
572
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
573
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
574
|
+
|
|
575
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
576
|
+
const mountedComponent = mount(component);
|
|
577
|
+
const input = mountedComponent.find("input");
|
|
578
|
+
|
|
579
|
+
input.simulate("blur", {});
|
|
580
|
+
|
|
581
|
+
expect(update, "to have calls satisfying", [{ args: ["", metadata] }]);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it("Change advanced numeric input on blur with no pending timer", () => {
|
|
585
|
+
const inputProps = new InputBaseProps();
|
|
586
|
+
const aLabel = "aLabel";
|
|
587
|
+
const aValue = "12.2";
|
|
588
|
+
const metadata = {
|
|
589
|
+
test: "value",
|
|
590
|
+
};
|
|
591
|
+
const onBlur = sinon.spy().named("blur");
|
|
592
|
+
|
|
593
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
594
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
595
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
596
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
597
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
598
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
599
|
+
inputProps.set(InputBaseProps.propNames.onBlur, onBlur);
|
|
600
|
+
|
|
601
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
602
|
+
const mountedComponent = mount(component);
|
|
603
|
+
const input = mountedComponent.find("input");
|
|
604
|
+
|
|
605
|
+
input.simulate("blur", {});
|
|
606
|
+
|
|
607
|
+
expect(onBlur, "was called once");
|
|
608
|
+
expect(update, "to have calls satisfying", [{ args: ["12.20", metadata] }]);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("Change advanced numeric input on blur with pending timer", () => {
|
|
612
|
+
window.bypassDebounce = false;
|
|
613
|
+
const inputProps = new InputBaseProps();
|
|
614
|
+
const aLabel = "aLabel";
|
|
615
|
+
const aValue = "12.2";
|
|
616
|
+
const metadata = {
|
|
617
|
+
test: "value",
|
|
618
|
+
};
|
|
619
|
+
const onBlur = sinon.spy().named("blur");
|
|
620
|
+
|
|
621
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
622
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
623
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
624
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
625
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
626
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
627
|
+
inputProps.set(InputBaseProps.propNames.onBlur, onBlur);
|
|
628
|
+
inputProps.set(InputBaseProps.propNames.timeoutDelay, 1000);
|
|
629
|
+
|
|
630
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
631
|
+
const mountedComponent = mount(component);
|
|
632
|
+
const input = mountedComponent.find("input");
|
|
633
|
+
|
|
634
|
+
input.simulate("change", { target: { value: "13", focus: noop } });
|
|
635
|
+
input.simulate("blur", {});
|
|
636
|
+
|
|
637
|
+
expect(onBlur, "was called once");
|
|
638
|
+
expect(update, "not to have calls satisfying", [{ args: [aValue, metadata] }]);
|
|
639
|
+
expect(update, "to have calls satisfying", [{ args: ["13.00", metadata] }]);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("Local state is reset when the value props changes for the same value as inputText", () => {
|
|
643
|
+
window.bypassDebounce = false;
|
|
644
|
+
const inputProps = new InputBaseProps();
|
|
645
|
+
const aLabel = "aLabel";
|
|
646
|
+
const aValue = "12.2";
|
|
647
|
+
const metadata = {
|
|
648
|
+
test: "value",
|
|
649
|
+
};
|
|
650
|
+
const onBlur = sinon.spy().named("blur");
|
|
651
|
+
|
|
652
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
653
|
+
inputProps.set(InputBaseProps.propNames.value, aValue);
|
|
654
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
655
|
+
inputProps.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
656
|
+
inputProps.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
657
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
658
|
+
inputProps.set(InputBaseProps.propNames.onBlur, onBlur);
|
|
659
|
+
inputProps.set(InputBaseProps.propNames.timeoutDelay, 1000);
|
|
660
|
+
|
|
661
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
662
|
+
const { container, rerender } = render(component);
|
|
663
|
+
|
|
664
|
+
const input = getByDisplayValue(container, "12.2");
|
|
665
|
+
|
|
666
|
+
fireEvent.change(input, {
|
|
667
|
+
target: {
|
|
668
|
+
value: "13",
|
|
669
|
+
},
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
const inputProps2 = new InputBaseProps();
|
|
673
|
+
inputProps2.set(InputBaseProps.propNames.update, update);
|
|
674
|
+
inputProps2.set(InputBaseProps.propNames.value, "13");
|
|
675
|
+
inputProps2.set(InputBaseProps.propNames.label, aLabel);
|
|
676
|
+
inputProps2.set(InputBaseProps.propNames.type, "AdvancedNumericInput");
|
|
677
|
+
inputProps2.set(InputBaseProps.propNames.numericInputProps, { decimalScale: 2 });
|
|
678
|
+
inputProps2.set(InputBaseProps.propNames.metadata, metadata);
|
|
679
|
+
inputProps2.set(InputBaseProps.propNames.onBlur, onBlur);
|
|
680
|
+
inputProps2.set(InputBaseProps.propNames.timeoutDelay, 1000);
|
|
681
|
+
|
|
682
|
+
act(() => {
|
|
683
|
+
rerender(<InputBase inputProps={inputProps2} />);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
// no idea what to assert here, this test is mostly for code coverage
|
|
687
|
+
});
|
|
688
|
+
});
|
|
@@ -19,6 +19,7 @@ class InputBaseProps extends ComponentProps {
|
|
|
19
19
|
autoComplete: "autoComplete",
|
|
20
20
|
timeoutDelay: "timeoutDelay",
|
|
21
21
|
rows: "rows",
|
|
22
|
+
numericInputProps: "numericInputProps",
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
static ruleNames = {
|
|
@@ -45,6 +46,7 @@ class InputBaseProps extends ComponentProps {
|
|
|
45
46
|
this.componentProps.set(this.constructor.propNames.autoComplete, null);
|
|
46
47
|
this.componentProps.set(this.constructor.propNames.timeoutDelay, null);
|
|
47
48
|
this.componentProps.set(this.constructor.propNames.rows, null);
|
|
49
|
+
this.componentProps.set(this.constructor.propNames.numericInputProps, null);
|
|
48
50
|
|
|
49
51
|
this.componentClasses.set(this.constructor.ruleNames.input, null);
|
|
50
52
|
this.componentClasses.set(this.constructor.ruleNames.errorText, null);
|
|
@@ -20,6 +20,7 @@ describe("InputBase Props", () => {
|
|
|
20
20
|
"autoComplete",
|
|
21
21
|
"timeoutDelay",
|
|
22
22
|
"rows",
|
|
23
|
+
"numericInputProps",
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
expect(InputBaseProps.propNames, "to have keys", propNames);
|
|
@@ -44,6 +45,7 @@ describe("InputBase Props", () => {
|
|
|
44
45
|
"autoComplete",
|
|
45
46
|
"timeoutDelay",
|
|
46
47
|
"rows",
|
|
48
|
+
"numericInputProps",
|
|
47
49
|
];
|
|
48
50
|
|
|
49
51
|
const ruleNames = ["input", "errorText"];
|
|
@@ -4,9 +4,12 @@ import withErrorBoundary from "../../hocs/withErrorBoundary";
|
|
|
4
4
|
import FullPage from "./FullPage";
|
|
5
5
|
import SubPage from "./SubPage";
|
|
6
6
|
import withWaypointing from "./withWaypointing";
|
|
7
|
+
import UrlPattern from "url-pattern";
|
|
7
8
|
|
|
8
9
|
const Page = ({ component: View, path, pages = {}, subpages = {}, modulePrependPath, isVisible = true }) => {
|
|
9
10
|
const WrappedView = useMemo(() => withErrorBoundary(path)(withWaypointing(View, isVisible)), [path, View, isVisible]);
|
|
11
|
+
const parentUrlPattern = new UrlPattern(path);
|
|
12
|
+
|
|
10
13
|
return (
|
|
11
14
|
<React.Fragment>
|
|
12
15
|
<Switch>
|
|
@@ -38,7 +41,15 @@ const Page = ({ component: View, path, pages = {}, subpages = {}, modulePrependP
|
|
|
38
41
|
<Route
|
|
39
42
|
key={subpath}
|
|
40
43
|
path={path + subpath}
|
|
41
|
-
render={route =>
|
|
44
|
+
render={route => (
|
|
45
|
+
<SubPage
|
|
46
|
+
root={path}
|
|
47
|
+
config={config}
|
|
48
|
+
parentUrlPattern={parentUrlPattern}
|
|
49
|
+
{...route}
|
|
50
|
+
modulePrependPath={modulePrependPath}
|
|
51
|
+
/>
|
|
52
|
+
)}
|
|
42
53
|
/>
|
|
43
54
|
))}
|
|
44
55
|
</Switch>
|
|
@@ -197,6 +197,9 @@ const SegmentPage = ({
|
|
|
197
197
|
);
|
|
198
198
|
}
|
|
199
199
|
if (config.subpages) {
|
|
200
|
+
const parentUrl = path + segpath;
|
|
201
|
+
const parentUrlPattern = new UrlPattern(parentUrl);
|
|
202
|
+
|
|
200
203
|
subpages.push(
|
|
201
204
|
...Object.entries(config.subpages).map(([subpath, config]) => {
|
|
202
205
|
const pagePath = segpath + subpath;
|
|
@@ -204,7 +207,15 @@ const SegmentPage = ({
|
|
|
204
207
|
<Route
|
|
205
208
|
key={pagePath}
|
|
206
209
|
path={path + pagePath}
|
|
207
|
-
render={route =>
|
|
210
|
+
render={route => (
|
|
211
|
+
<SubPage
|
|
212
|
+
root={path}
|
|
213
|
+
config={config}
|
|
214
|
+
parentUrlPattern={parentUrlPattern}
|
|
215
|
+
{...route}
|
|
216
|
+
modulePrependPath={modulePrependPath}
|
|
217
|
+
/>
|
|
218
|
+
)}
|
|
208
219
|
/>
|
|
209
220
|
);
|
|
210
221
|
}),
|
|
@@ -20,22 +20,19 @@ const useStyles = makeStyles(theme => ({
|
|
|
20
20
|
},
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
|
-
export const SubPage = ({ config, match, location, history, root, modulePrependPath }) => {
|
|
23
|
+
export const SubPage = ({ config, match, location, history, root, modulePrependPath, parentUrlPattern }) => {
|
|
24
24
|
const classes = useStyles();
|
|
25
25
|
const dispatch = useDispatch();
|
|
26
26
|
let { component: View, ...props } = config;
|
|
27
27
|
const pattern = new UrlPattern(root);
|
|
28
28
|
const baseHref = pattern.stringify(match.params);
|
|
29
|
-
|
|
30
29
|
const path = location.pathname;
|
|
31
30
|
|
|
32
|
-
const basePathArr = path.split("/");
|
|
33
|
-
basePathArr.pop();
|
|
34
|
-
const basePath = basePathArr.join("/");
|
|
35
31
|
const WrappedView = useMemo(() => withErrorBoundary(path)(withWaypointing(View)), [path, View]);
|
|
36
32
|
const closeSubPage = () => {
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
const parentHref = parentUrlPattern.stringify(match.params);
|
|
34
|
+
history.push(parentHref);
|
|
35
|
+
dispatch(mapHref(parentHref, parentHref));
|
|
39
36
|
};
|
|
40
37
|
|
|
41
38
|
const message = (
|
|
@@ -59,7 +56,6 @@ export const SubPage = ({ config, match, location, history, root, modulePrependP
|
|
|
59
56
|
modalProps.set(ModalProps.propNames.title, titleComponent);
|
|
60
57
|
modalProps.set(ModalProps.propNames.open, true);
|
|
61
58
|
modalProps.set(ModalProps.propNames.type, "fullwidth");
|
|
62
|
-
modalProps.set(ModalProps.propNames.backdropClickCallback, closeSubPage);
|
|
63
59
|
|
|
64
60
|
let actionPanel = (
|
|
65
61
|
<div className={classes.actionPanel}>
|
|
@@ -13,6 +13,7 @@ import Button from "@material-ui/core/Button";
|
|
|
13
13
|
import translations from "~/translations/en-US.json";
|
|
14
14
|
import { TestWrapper, createMuiTheme } from "../../utils/testUtils";
|
|
15
15
|
import sharedMessages from "../../sharedMessages";
|
|
16
|
+
import UrlPattern from "url-pattern";
|
|
16
17
|
|
|
17
18
|
const InnerView = ({ theme, pathname, search, mapFrom, match, location, routeIsAligned, set }) => (
|
|
18
19
|
<PropStruct
|
|
@@ -92,6 +93,7 @@ describe("SubPage", () => {
|
|
|
92
93
|
config={{ component: InnerView, set: true, title: "Item Details" }}
|
|
93
94
|
root="/foo"
|
|
94
95
|
path="/foo/bar"
|
|
96
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
95
97
|
{...route}
|
|
96
98
|
/>
|
|
97
99
|
)}
|
|
@@ -140,6 +142,7 @@ describe("SubPage", () => {
|
|
|
140
142
|
config={{ component: InnerView, set: true, title: sharedMessages.confirmation }}
|
|
141
143
|
root="/foo"
|
|
142
144
|
path="/foo/bar"
|
|
145
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
143
146
|
{...route}
|
|
144
147
|
/>
|
|
145
148
|
)}
|
|
@@ -164,6 +167,7 @@ describe("SubPage", () => {
|
|
|
164
167
|
config={{ component: InnerView, set: true, title: "Item Details" }}
|
|
165
168
|
root="/foo"
|
|
166
169
|
path="/foo/bar"
|
|
170
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
167
171
|
{...route}
|
|
168
172
|
/>
|
|
169
173
|
)}
|
|
@@ -180,6 +184,42 @@ describe("SubPage", () => {
|
|
|
180
184
|
expect(history.push, "to have calls satisfying", [{ args: ["/foo"] }]);
|
|
181
185
|
expect(dispatch, "to have calls satisfying", [{ args: [mapHref("/foo", "/foo")] }]);
|
|
182
186
|
});
|
|
187
|
+
it("closing the dialog navigate to the parentUrlPattern with parameters", () => {
|
|
188
|
+
history = createMemoryHistory({ initialEntries: ["/foo/bar/123/456"] });
|
|
189
|
+
sinon.spy(history, "push");
|
|
190
|
+
history.push.named("history.push");
|
|
191
|
+
dispatch.resetHistory();
|
|
192
|
+
|
|
193
|
+
const component = (
|
|
194
|
+
<TestWrapper provider={{ store }} intlProvider={intlProvider} stylesProvider muiThemeProvider={{ theme }}>
|
|
195
|
+
<div>
|
|
196
|
+
<div id="outer" />
|
|
197
|
+
<Router history={history}>
|
|
198
|
+
<Route
|
|
199
|
+
path="/foo/bar/:parentId/:id"
|
|
200
|
+
render={route => (
|
|
201
|
+
<SubPage
|
|
202
|
+
config={{ component: InnerView, set: true, title: "Item Details" }}
|
|
203
|
+
root="/foo/bar/:parentId/:id"
|
|
204
|
+
path="/foo/bar/123/456"
|
|
205
|
+
parentUrlPattern={new UrlPattern("/foo/bar/:parentId")}
|
|
206
|
+
{...route}
|
|
207
|
+
/>
|
|
208
|
+
)}
|
|
209
|
+
/>
|
|
210
|
+
</Router>
|
|
211
|
+
</div>
|
|
212
|
+
</TestWrapper>
|
|
213
|
+
);
|
|
214
|
+
const mountedComponent = mount(component);
|
|
215
|
+
|
|
216
|
+
const closeButton = mountedComponent.find("button").at(0);
|
|
217
|
+
|
|
218
|
+
closeButton.invoke("onClick")();
|
|
219
|
+
expect(history.push, "to have calls satisfying", [{ args: ["/foo/bar/123"] }]);
|
|
220
|
+
expect(dispatch, "to have a call satisfying", { args: [mapHref("/foo/bar/123", "/foo/bar/123")] });
|
|
221
|
+
});
|
|
222
|
+
|
|
183
223
|
it("renders action panel passed from props", () => {
|
|
184
224
|
const actions = () => [{ label: sharedMessages.cancel }, { label: sharedMessages.applyChanges }];
|
|
185
225
|
|
|
@@ -200,6 +240,7 @@ describe("SubPage", () => {
|
|
|
200
240
|
}}
|
|
201
241
|
root="/foo"
|
|
202
242
|
path="/foo/bar"
|
|
243
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
203
244
|
{...route}
|
|
204
245
|
/>
|
|
205
246
|
)}
|
|
@@ -242,6 +283,7 @@ describe("SubPage", () => {
|
|
|
242
283
|
}}
|
|
243
284
|
root="/foo"
|
|
244
285
|
path="/foo/bar"
|
|
286
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
245
287
|
{...route}
|
|
246
288
|
/>
|
|
247
289
|
)}
|
|
@@ -286,6 +328,7 @@ describe("SubPage", () => {
|
|
|
286
328
|
}}
|
|
287
329
|
root="/foo"
|
|
288
330
|
path="/foo/bar"
|
|
331
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
289
332
|
{...route}
|
|
290
333
|
/>
|
|
291
334
|
)}
|
|
@@ -327,6 +370,7 @@ describe("SubPage", () => {
|
|
|
327
370
|
}}
|
|
328
371
|
root="/foo"
|
|
329
372
|
path="/foo/bar"
|
|
373
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
330
374
|
{...route}
|
|
331
375
|
/>
|
|
332
376
|
)}
|
|
@@ -368,6 +412,7 @@ describe("SubPage", () => {
|
|
|
368
412
|
}}
|
|
369
413
|
root="/foo"
|
|
370
414
|
path="/foo/bar"
|
|
415
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
371
416
|
{...route}
|
|
372
417
|
/>
|
|
373
418
|
)}
|
|
@@ -411,6 +456,7 @@ describe("SubPage", () => {
|
|
|
411
456
|
}}
|
|
412
457
|
root="/foo"
|
|
413
458
|
path="/foo/bar"
|
|
459
|
+
parentUrlPattern={new UrlPattern("/foo")}
|
|
414
460
|
{...route}
|
|
415
461
|
/>
|
|
416
462
|
)}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useDispatch } from "react-redux";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { extractStandardErrorMessagesFromResponse } from "../utils/responseProcessingHelper";
|
|
4
|
+
import { pushGlobalErrorMessage } from "../actions/globalErrorMessages";
|
|
5
|
+
|
|
6
|
+
export const executeDispatchWithErrorHandling = ({
|
|
7
|
+
dispatch,
|
|
8
|
+
action,
|
|
9
|
+
errorTitle,
|
|
10
|
+
errorDescription,
|
|
11
|
+
validationLookupModule,
|
|
12
|
+
validationLookupName,
|
|
13
|
+
}) => {
|
|
14
|
+
return dispatch(action).then(data => {
|
|
15
|
+
const extractedMessages = extractStandardErrorMessagesFromResponse(
|
|
16
|
+
data,
|
|
17
|
+
validationLookupModule,
|
|
18
|
+
validationLookupName,
|
|
19
|
+
);
|
|
20
|
+
if (extractedMessages.hasErrors) {
|
|
21
|
+
const newMsg = {
|
|
22
|
+
messages: extractedMessages.messages,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (extractedMessages.messages.length > 0) {
|
|
26
|
+
newMsg.title = errorDescription;
|
|
27
|
+
} else {
|
|
28
|
+
newMsg.title = errorTitle;
|
|
29
|
+
newMsg.description = errorDescription;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
dispatch(pushGlobalErrorMessage(newMsg));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return data;
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const useDispatchWithErrorHandling = () => {
|
|
40
|
+
const dispatch = useDispatch();
|
|
41
|
+
|
|
42
|
+
return useCallback(
|
|
43
|
+
({ action, errorTitle, errorDescription, validationLookupModule, validationLookupName }) => {
|
|
44
|
+
return executeDispatchWithErrorHandling({
|
|
45
|
+
dispatch,
|
|
46
|
+
action,
|
|
47
|
+
errorTitle,
|
|
48
|
+
errorDescription,
|
|
49
|
+
validationLookupModule,
|
|
50
|
+
validationLookupName,
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
[dispatch],
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default useDispatchWithErrorHandling;
|