orc-shared 1.6.0-dev.8 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/actions/applications.js +1 -1
  2. package/dist/actions/authentication.js +1 -1
  3. package/dist/actions/countries.js +1 -1
  4. package/dist/actions/locale.js +1 -1
  5. package/dist/actions/makeApiAction.js +3 -3
  6. package/dist/actions/makeOrcApiAction.js +2 -2
  7. package/dist/actions/metadata.js +3 -3
  8. package/dist/actions/navigation.js +2 -2
  9. package/dist/actions/scopes.js +1 -1
  10. package/dist/actions/tasks.js +1 -1
  11. package/dist/actions/timezones.js +1 -1
  12. package/dist/actions/versionInfo.js +1 -1
  13. package/dist/buildStore.js +2 -2
  14. package/dist/components/AppFrame/About.js +3 -3
  15. package/dist/components/AppFrame/AppFrame.js +1 -1
  16. package/dist/components/AppFrame/Preferences.js +3 -3
  17. package/dist/components/ApplicationModuleLoader.js +1 -1
  18. package/dist/components/CategoryList.js +1 -1
  19. package/dist/components/DropMenu/index.js +1 -1
  20. package/dist/components/Form/FieldList.js +2 -2
  21. package/dist/components/Form/InputField.js +1 -1
  22. package/dist/components/Form/Inputs/Translation.js +3 -3
  23. package/dist/components/List/List.js +1 -1
  24. package/dist/components/List/enhanceColumnDefs.js +2 -2
  25. package/dist/components/Loader.js +1 -1
  26. package/dist/components/MaterialUI/DataDisplay/CollapsableList.js +1 -1
  27. package/dist/components/MaterialUI/DataDisplay/Notification.js +3 -3
  28. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/GlobalErrorMessages.js +33 -5
  29. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/LookupDisplayValue.js +12 -7
  30. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/StepperModal.js +1 -1
  31. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/Translations.js +2 -2
  32. package/dist/components/MaterialUI/DataDisplay/SelectionList.js +1 -1
  33. package/dist/components/MaterialUI/DataDisplay/Table.js +1 -1
  34. package/dist/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.js +1 -1
  35. package/dist/components/MaterialUI/DataDisplay/TransferList.js +1 -1
  36. package/dist/components/MaterialUI/DataDisplay/tableHelpers.js +1 -1
  37. package/dist/components/MaterialUI/DataDisplay/useTableSelection.js +3 -3
  38. package/dist/components/MaterialUI/Feedback/NotificationContext.js +1 -1
  39. package/dist/components/MaterialUI/Feedback/loadingScreen.js +1 -1
  40. package/dist/components/MaterialUI/Inputs/Autocomplete.js +2 -2
  41. package/dist/components/MaterialUI/Inputs/InputBase.js +97 -15
  42. package/dist/components/MaterialUI/Inputs/InputBaseProps.js +3 -1
  43. package/dist/components/MaterialUI/Inputs/Select.js +3 -3
  44. package/dist/components/MaterialUI/Inputs/Switch.js +2 -2
  45. package/dist/components/MaterialUI/Inputs/TimePicker.js +1 -1
  46. package/dist/components/MaterialUI/Inputs/createInput.js +2 -2
  47. package/dist/components/MaterialUI/Navigation/DropDownMenu.js +1 -1
  48. package/dist/components/MaterialUI/Navigation/TabBar.js +1 -1
  49. package/dist/components/MaterialUI/Navigation/TabLabel.js +1 -1
  50. package/dist/components/MaterialUI/ScopeSelector/ScopeTreeView.js +1 -1
  51. package/dist/components/MaterialUI/Surfaces/ExpansionPanel.js +1 -1
  52. package/dist/components/MaterialUI/Surfaces/SectionExpansionPanel.js +1 -1
  53. package/dist/components/MaterialUI/hocs/withDeferredPopper.js +1 -1
  54. package/dist/components/MaterialUI/hocs/withDeferredTooltip.js +1 -1
  55. package/dist/components/MaterialUI/muiThemes.js +2 -2
  56. package/dist/components/Modal/index.js +1 -1
  57. package/dist/components/Modules.js +1 -1
  58. package/dist/components/MultiSelector.js +1 -1
  59. package/dist/components/Navigation/Bar.js +3 -3
  60. package/dist/components/Navigation/Tab.js +1 -1
  61. package/dist/components/Navigation/useNavigationState.js +3 -3
  62. package/dist/components/Routing/Page.js +1 -1
  63. package/dist/components/Routing/SegmentPage.js +1 -1
  64. package/dist/components/Routing/withWaypointing.js +2 -2
  65. package/dist/components/Scope/ScopeNode.js +1 -1
  66. package/dist/components/Scope/index.js +3 -3
  67. package/dist/components/Scope/useScopeConfirmationModalState.js +2 -2
  68. package/dist/components/Selector.js +1 -1
  69. package/dist/components/Text.js +1 -1
  70. package/dist/components/Treeview/Node.js +2 -2
  71. package/dist/components/Treeview/index.js +3 -3
  72. package/dist/getThemeOverrides.js +2 -2
  73. package/dist/hocs/withNavigationLink.js +1 -1
  74. package/dist/hocs/withScopeData.js +1 -1
  75. package/dist/hocs/withToggle.js +1 -1
  76. package/dist/hocs/withUpdateHandler.js +2 -2
  77. package/dist/hocs/withViewState.js +1 -1
  78. package/dist/hooks/useEditState.js +2 -2
  79. package/dist/hooks/useEntityLoader.js +3 -3
  80. package/dist/hooks/useFullEntityEditState.js +3 -3
  81. package/dist/hooks/useInfiniteScroll.js +1 -1
  82. package/dist/hooks/useLabelMessage.js +2 -2
  83. package/dist/hooks/useMultipleFieldEditState.js +2 -2
  84. package/dist/hooks/useNotificationRequestState.js +2 -2
  85. package/dist/hooks/useToggle.js +1 -1
  86. package/dist/reducers/authentication.js +1 -1
  87. package/dist/reducers/request.js +2 -1
  88. package/dist/selectors/metadata.js +2 -2
  89. package/dist/selectors/modules.js +2 -2
  90. package/dist/utils/buildUrl.js +1 -1
  91. package/dist/utils/flatten.js +3 -3
  92. package/dist/utils/testUtils.js +1 -1
  93. package/dist/utils/timezoneHelper.js +1 -1
  94. package/package.json +4 -3
  95. package/src/components/MaterialUI/DataDisplay/PredefinedElements/GlobalErrorMessages.js +16 -1
  96. package/src/components/MaterialUI/DataDisplay/PredefinedElements/GlobalErrorMessages.test.js +6 -10
  97. package/src/components/MaterialUI/DataDisplay/PredefinedElements/LookupDisplayValue.js +13 -2
  98. package/src/components/MaterialUI/DataDisplay/PredefinedElements/LookupDisplayValue.test.js +26 -0
  99. package/src/components/MaterialUI/Inputs/InputBase.js +96 -14
  100. package/src/components/MaterialUI/Inputs/InputBase.test.js +337 -1
  101. package/src/components/MaterialUI/Inputs/InputBaseProps.js +2 -0
  102. package/src/components/MaterialUI/Inputs/InputBaseProps.test.js +2 -0
  103. package/src/reducers/request.js +2 -1
  104. package/src/reducers/request.test.js +23 -0
@@ -1,8 +1,9 @@
1
- import React from "react";
1
+ import React, { useRef } from "react";
2
2
  import { makeStyles } from "@material-ui/core/styles";
3
3
  import InputBaseMUI from "@material-ui/core/InputBase";
4
4
  import InputBaseProps, { isInputProps } from "./InputBaseProps";
5
5
  import classNames from "classnames";
6
+ import { NumericFormat, numericFormatter } from "react-number-format";
6
7
 
7
8
  export const useStyles = makeStyles(theme => ({
8
9
  container: {
@@ -79,6 +80,27 @@ export const useStyles = makeStyles(theme => ({
79
80
  },
80
81
  }));
81
82
 
83
+ export const AdvancedNumericInput = props => {
84
+ const { inputRef, onChange, ...other } = props;
85
+
86
+ // https://github.com/s-yadav/react-number-format
87
+
88
+ return (
89
+ <NumericFormat
90
+ {...other}
91
+ getInputRef={inputRef}
92
+ onValueChange={values => {
93
+ onChange({
94
+ target: {
95
+ name: props.name,
96
+ value: values.value,
97
+ },
98
+ });
99
+ }}
100
+ />
101
+ );
102
+ };
103
+
82
104
  const InputBase = ({ inputProps }) => {
83
105
  if (isInputProps(inputProps) === false) {
84
106
  throw new TypeError("inputProps property is not of type InputBaseProps");
@@ -101,6 +123,19 @@ const InputBase = ({ inputProps }) => {
101
123
  const autoComplete = inputProps?.get(InputBaseProps.propNames.autoComplete);
102
124
  const timeoutDelay = inputProps?.get(InputBaseProps.propNames.timeoutDelay) || 100;
103
125
  const rowsProps = inputProps?.get(InputBaseProps.propNames.rows);
126
+ const numericInputProps = inputProps?.get(InputBaseProps.propNames.numericInputProps) || null;
127
+
128
+ const isAdvancedNumericInput = type.toLowerCase() === "advancednumericinput";
129
+ const inputComponent = isAdvancedNumericInput ? AdvancedNumericInput : undefined;
130
+ const inputControlType = isAdvancedNumericInput ? "text" : type;
131
+
132
+ if (isAdvancedNumericInput && numericInputProps) {
133
+ Object.keys(numericInputProps).forEach(key => {
134
+ if (key !== "blurFormattingSkipFixedDecimalScale") {
135
+ inputAttributes[key] = numericInputProps[key];
136
+ }
137
+ });
138
+ }
104
139
 
105
140
  const defaultRows = 4;
106
141
  let rows = rowsProps;
@@ -116,34 +151,80 @@ const InputBase = ({ inputProps }) => {
116
151
  const classes = useStyles({ label, errorPosition });
117
152
 
118
153
  const onChangeHandler = event => {
119
- event.persist();
154
+ if (event.persist) {
155
+ event.persist();
156
+ }
120
157
 
121
158
  if (!event.target.value || window.bypassDebounce === true) {
122
159
  update(event.target.value, metadata);
123
160
  }
124
-
125
161
  setInputText(event.target.value);
126
162
  };
127
163
 
164
+ const timerId = useRef(null);
165
+
166
+ React.useEffect(() => {
167
+ return () => {
168
+ clearTimeout(timerId.current);
169
+ timerId.current = null;
170
+ };
171
+ }, []);
172
+
173
+ const onBlurInternal = e => {
174
+ let updateValue = (inputText ?? value).toString(); // value cannot be null as defined above
175
+
176
+ if (timerId.current) {
177
+ clearTimeout(timerId.current);
178
+ timerId.current = null;
179
+ }
180
+
181
+ if (isAdvancedNumericInput) {
182
+ const formattingProps = { ...numericInputProps };
183
+ delete formattingProps.blurFormattingSkipFixedDecimalScale;
184
+
185
+ if (numericInputProps?.blurFormattingSkipFixedDecimalScale !== true) {
186
+ formattingProps.fixedDecimalScale = true;
187
+ }
188
+
189
+ updateValue = numericFormatter(updateValue, formattingProps);
190
+ }
191
+
192
+ update(updateValue, metadata);
193
+ setInputText(null);
194
+
195
+ if (onBlur) {
196
+ onBlur(e);
197
+ }
198
+ };
199
+
128
200
  const inputBaseInputStyle = inputProps?.getStyle(InputBaseProps.ruleNames.input);
129
201
  const errorTextStyle = inputProps?.getStyle(InputBaseProps.ruleNames.errorText);
130
-
131
202
  const [inputText, setInputText] = React.useState(null);
132
-
133
203
  const textToDisplay = inputText ?? value;
134
204
 
135
205
  React.useEffect(() => {
136
- if (inputText !== value && inputText != null && window.bypassDebounce !== true) {
137
- const timeOutId = setTimeout(() => {
138
- const updateValue = update(inputText, metadata);
139
- setInputText(null);
206
+ if (inputText === null || window.bypassDebounce === true) {
207
+ return;
208
+ }
140
209
 
141
- return updateValue;
210
+ if (inputText !== value) {
211
+ clearTimeout(timerId.current);
212
+ timerId.current = setTimeout(() => {
213
+ update(inputText, metadata);
214
+ setInputText(null);
142
215
  }, timeoutDelay);
143
- return () => clearTimeout(timeOutId);
216
+
217
+ return () => {
218
+ clearTimeout(timerId.current);
219
+ timerId.current = null;
220
+ };
221
+ } else {
222
+ setInputText(null);
223
+ clearTimeout(timerId.current);
144
224
  }
225
+
145
226
  // eslint-disable-next-line react-hooks/exhaustive-deps
146
- }, [inputText, value]);
227
+ }, [inputText, metadata, timeoutDelay, update, value]);
147
228
 
148
229
  return (
149
230
  <div className={classes.container}>
@@ -157,15 +238,16 @@ const InputBase = ({ inputProps }) => {
157
238
  multiline: classes.multiline,
158
239
  inputMultiline: classes.inputMultiline,
159
240
  }}
160
- onBlur={onBlur}
241
+ onBlur={onBlurInternal}
161
242
  onClick={onClick}
162
- type={type}
243
+ type={inputControlType}
163
244
  placeholder={placeholder}
164
245
  value={textToDisplay}
165
246
  fullWidth={true}
166
247
  onChange={event => onChangeHandler(event)}
167
248
  error={!!error}
168
249
  inputProps={inputAttributes}
250
+ inputComponent={inputComponent}
169
251
  disabled={disabled}
170
252
  multiline={multiline}
171
253
  startAdornment={startAdornment}
@@ -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;
@@ -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"];
@@ -24,7 +24,8 @@ const requestReducer = (state = initialState, action) => {
24
24
  }
25
25
  if (action.type.endsWith("_FAILURE")) {
26
26
  const requestName = action.type.replace(/_FAILURE$/, "");
27
- if (safeGet(action, "payload", "status") === 403) {
27
+ const status = safeGet(action, "payload", "status");
28
+ if (status === 403 || status === 401) {
28
29
  return state.deleteIn(["actives", requestName]).set("logout", true);
29
30
  } else {
30
31
  return state.deleteIn(["actives", requestName]).set("error", Immutable.fromJS(action));
@@ -125,4 +125,27 @@ describe("Request reducer", () => {
125
125
  }),
126
126
  );
127
127
  });
128
+
129
+ it("clears activity flag and sets login flag when a request fails with status 401", () => {
130
+ const oldState = Immutable.fromJS({
131
+ actives: {
132
+ SOME_FLAG: true,
133
+ TEST_THIS: true,
134
+ },
135
+ });
136
+ const action = {
137
+ type: "TEST_THIS_FAILURE",
138
+ payload: { status: 401, message: "UnauthorizedAccessException" },
139
+ };
140
+ const newState = reducer(oldState, action);
141
+ expect(newState, "not to be", oldState).and(
142
+ "to equal",
143
+ Immutable.fromJS({
144
+ actives: {
145
+ SOME_FLAG: true,
146
+ },
147
+ [LOGOUT]: true,
148
+ }),
149
+ );
150
+ });
128
151
  });