orc-shared 5.10.0-dev.2 → 5.10.0-dev.21
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/metadata.js +30 -11
- package/dist/actions/requestsApi.js +10 -1
- package/dist/components/AppFrame/About.js +136 -100
- package/dist/components/AppFrame/Anchor.js +45 -21
- package/dist/components/AppFrame/AppFrame.js +53 -31
- package/dist/components/AppFrame/Help.js +35 -15
- package/dist/components/AppFrame/MenuItem.js +148 -114
- package/dist/components/AppFrame/Preferences.js +136 -97
- package/dist/components/AppFrame/Sidebar.js +51 -28
- package/dist/components/AppFrame/Topbar.js +61 -36
- package/dist/components/ColumnWrapper.js +28 -5
- package/dist/components/Culture.js +33 -14
- package/dist/components/DropMenu/Menu.js +79 -45
- package/dist/components/DropMenu/index.js +34 -29
- package/dist/components/Form/Combination.js +45 -16
- package/dist/components/Form/Field.js +57 -38
- package/dist/components/Form/FieldElements.js +0 -11
- package/dist/components/Form/Fieldset.js +47 -19
- package/dist/components/Form/Form.js +22 -9
- package/dist/components/Form/FormElement.js +40 -7
- package/dist/components/Form/Inputs/Button.js +63 -18
- package/dist/components/Form/Inputs/ReadOnly.js +50 -27
- package/dist/components/{AppFrame/ApplicationSelector/Header.js → Form/Inputs/Selector.js} +30 -31
- package/dist/components/Form/Inputs/Text.js +20 -37
- package/dist/components/Form/Inputs/Toggles.js +39 -40
- package/dist/components/Form/Inputs/index.js +2 -13
- package/dist/components/MaterialUI/DataDisplay/PredefinedElements/Placeholder.js +31 -11
- package/dist/components/MaterialUI/DataDisplay/PredefinedElements/SectionToolbar.js +89 -0
- package/dist/components/MaterialUI/DataDisplay/Table.js +109 -18
- package/dist/components/MaterialUI/DataDisplay/TableProps.js +5 -1
- package/dist/components/MaterialUI/DataDisplay/TableWithInMemoryPaging.js +198 -0
- package/dist/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.js +4 -1
- package/dist/components/MaterialUI/Inputs/DatePicker.js +14 -14
- package/dist/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +1 -0
- package/dist/components/MaterialUI/Inputs/Select.js +2 -0
- package/dist/components/MaterialUI/Inputs/SelectProps.js +2 -0
- package/dist/components/MaterialUI/Inputs/Switch.js +17 -1
- package/dist/components/MaterialUI/Inputs/SwitchProps.js +2 -0
- package/dist/components/MaterialUI/Inputs/TimePicker.js +14 -21
- package/dist/components/MaterialUI/hocs/withDeferredTooltip.js +3 -1
- package/dist/components/MaterialUI/muiThemes.js +2 -1
- package/dist/components/Provision.js +1 -1
- package/dist/constants.js +2 -1
- package/dist/content/iconsSheet.svg +740 -116
- package/dist/hocs/withScrollBox.js +27 -12
- package/dist/hooks/useDaysAndMonthsLocalization.js +77 -0
- package/dist/hooks/useInMemoryPaging.js +135 -0
- package/dist/hooks/useMultipleFieldEditState.js +12 -3
- package/dist/reducers/metadata.js +6 -0
- package/dist/schemas/metadata.js +9 -1
- package/dist/selectors/locale.js +1 -0
- package/dist/selectors/metadata.js +14 -11
- package/dist/sharedMessages.js +184 -0
- package/dist/utils/ListHelper.js +271 -0
- package/dist/utils/comparisonHelper.js +185 -0
- package/dist/utils/propertyBagHelper.js +3 -1
- package/dist/utils/timezoneHelper.js +18 -31
- package/package.json +4 -3
- package/src/actions/metadata.js +11 -0
- package/src/actions/metadata.test.js +27 -0
- package/src/actions/requestsApi.js +6 -0
- package/src/components/AppFrame/About.js +97 -117
- package/src/components/AppFrame/About.test.js +128 -90
- package/src/components/AppFrame/Anchor.js +34 -36
- package/src/components/AppFrame/Anchor.test.js +5 -68
- package/src/components/AppFrame/AppFrame.js +31 -40
- package/src/components/AppFrame/AppFrame.test.js +424 -445
- package/src/components/AppFrame/Help.js +23 -20
- package/src/components/AppFrame/Help.test.js +3 -3
- package/src/components/AppFrame/MenuItem.js +106 -126
- package/src/components/AppFrame/MenuItem.test.js +78 -169
- package/src/components/AppFrame/Preferences.js +110 -98
- package/src/components/AppFrame/Preferences.test.js +115 -219
- package/src/components/AppFrame/Sidebar.js +39 -41
- package/src/components/AppFrame/Sidebar.test.js +88 -168
- package/src/components/AppFrame/Topbar.js +59 -52
- package/src/components/AppFrame/Topbar.test.js +31 -39
- package/src/components/ColumnWrapper.js +18 -9
- package/src/components/Culture.js +20 -10
- package/src/components/Culture.test.js +27 -16
- package/src/components/DropMenu/DropMenu.test.js +185 -224
- package/src/components/DropMenu/Menu.js +73 -80
- package/src/components/DropMenu/Menu.test.js +35 -86
- package/src/components/DropMenu/index.js +19 -15
- package/src/components/Form/Combination.js +35 -28
- package/src/components/Form/Combination.test.js +6 -19
- package/src/components/Form/Field.js +53 -66
- package/src/components/Form/Field.test.js +29 -51
- package/src/components/Form/FieldElements.js +0 -14
- package/src/components/Form/FieldElements.test.js +104 -111
- package/src/components/Form/Fieldset.js +42 -37
- package/src/components/Form/Fieldset.test.js +14 -7
- package/src/components/Form/Form.js +11 -7
- package/src/components/Form/Form.test.js +75 -56
- package/src/components/Form/FormElement.js +24 -16
- package/src/components/Form/InputField.test.js +24 -30
- package/src/components/Form/Inputs/Button.js +58 -14
- package/src/components/Form/Inputs/Button.test.js +32 -7
- package/src/components/Form/Inputs/Inputs.test.js +0 -7
- package/src/components/Form/Inputs/ReadOnly.js +34 -28
- package/src/components/Form/Inputs/ReadOnly.test.js +45 -7
- package/src/components/Form/Inputs/Selector.js +22 -0
- package/src/components/Form/Inputs/Selector.test.js +105 -0
- package/src/components/Form/Inputs/Text.js +15 -44
- package/src/components/Form/Inputs/Text.test.js +20 -29
- package/src/components/Form/Inputs/Toggles.js +27 -26
- package/src/components/Form/Inputs/Toggles.test.js +22 -28
- package/src/components/Form/Inputs/index.js +4 -15
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/InformationItem.test.js +1 -4
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/Placeholder.js +32 -6
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/Placeholder.test.js +3 -1
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/SectionToolbar.js +39 -0
- package/src/components/MaterialUI/DataDisplay/Table.js +190 -114
- package/src/components/MaterialUI/DataDisplay/Table.test.js +246 -1
- package/src/components/MaterialUI/DataDisplay/TableProps.js +4 -0
- package/src/components/MaterialUI/DataDisplay/TableProps.test.js +2 -0
- package/src/components/MaterialUI/DataDisplay/TableWithInMemoryPaging.js +145 -0
- package/src/components/MaterialUI/DataDisplay/TableWithInMemoryPaging.test.js +457 -0
- package/src/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.js +5 -1
- package/src/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.test.js +7 -1
- package/src/components/MaterialUI/Inputs/DatePicker.js +19 -20
- package/src/components/MaterialUI/Inputs/DatePicker.test.js +11 -6
- package/src/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +1 -0
- package/src/components/MaterialUI/Inputs/Select.js +2 -0
- package/src/components/MaterialUI/Inputs/SelectProps.js +2 -0
- package/src/components/MaterialUI/Inputs/SelectProps.test.js +2 -0
- package/src/components/MaterialUI/Inputs/Switch.js +22 -1
- package/src/components/MaterialUI/Inputs/Switch.test.js +23 -0
- package/src/components/MaterialUI/Inputs/SwitchProps.js +2 -0
- package/src/components/MaterialUI/Inputs/SwitchProps.test.js +2 -0
- package/src/components/MaterialUI/Inputs/TimePicker.js +10 -19
- package/src/components/MaterialUI/Inputs/TimePicker.test.js +278 -117
- package/src/components/MaterialUI/hocs/withDeferredTooltip.js +4 -1
- package/src/components/MaterialUI/hocs/withDeferredTooltip.test.js +27 -0
- package/src/components/MaterialUI/muiThemes.js +1 -0
- package/src/components/Navigation/Bar.test.js +92 -87
- package/src/components/Provision.js +1 -1
- package/src/components/TaskDetailsModal.test.js +1 -3
- package/src/constants.js +1 -0
- package/src/content/iconsSheet.svg +740 -116
- package/src/hocs/withScrollBox.js +32 -19
- package/src/hocs/withScrollBox.test.js +15 -3
- package/src/hooks/useDaysAndMonthsLocalization.js +79 -0
- package/src/hooks/useDaysAndMonthsLocalization.test.js +107 -0
- package/src/hooks/useInMemoryPaging.js +78 -0
- package/src/hooks/useInMemoryPaging.test.js +515 -0
- package/src/hooks/useMultipleFieldEditState.js +11 -4
- package/src/hooks/useMultipleFieldEditState.test.js +49 -1
- package/src/reducers/metadata.js +6 -1
- package/src/reducers/metadata.test.js +31 -0
- package/src/requests +1 -0
- package/src/schemas/metadata.js +3 -0
- package/src/selectors/locale.js +1 -1
- package/src/selectors/metadata.js +12 -9
- package/src/selectors/metadata.test.js +92 -11
- package/src/sharedMessages.js +184 -0
- package/src/timezones.json +883 -0
- package/src/translations/en-US.json +46 -0
- package/src/translations/fr-CA.json +46 -0
- package/src/utils/ListHelper.js +203 -0
- package/src/utils/ListHelper.test.js +710 -0
- package/src/utils/comparisonHelper.js +135 -0
- package/src/utils/comparisonHelper.test.js +334 -0
- package/src/utils/propertyBagHelper.js +2 -0
- package/src/utils/propertyBagHelper.test.js +6 -0
- package/src/utils/timezoneHelper.js +10 -135
- package/src/utils/timezoneHelper.test.js +7 -7
- package/dist/components/Form/FieldList.js +0 -270
- package/dist/components/Form/Inputs/FieldButtons.js +0 -66
- package/dist/components/Form/Inputs/Number.js +0 -117
- package/dist/components/Form/Inputs/SmallButton.js +0 -91
- package/dist/components/Form/Inputs/Time.js +0 -86
- package/dist/components/Form/Inputs/Translation.js +0 -169
- package/src/components/AppFrame/ApplicationSelector/Header.js +0 -34
- package/src/components/AppFrame/ApplicationSelector/Header.test.js +0 -23
- package/src/components/Form/FieldList.js +0 -210
- package/src/components/Form/FieldList.test.js +0 -558
- package/src/components/Form/Inputs/FieldButtons.js +0 -90
- package/src/components/Form/Inputs/Number.js +0 -60
- package/src/components/Form/Inputs/Number.test.js +0 -435
- package/src/components/Form/Inputs/SmallButton.js +0 -37
- package/src/components/Form/Inputs/SmallButton.test.js +0 -65
- package/src/components/Form/Inputs/Time.js +0 -32
- package/src/components/Form/Inputs/Time.test.js +0 -41
- package/src/components/Form/Inputs/Translation.js +0 -93
- package/src/components/Form/Inputs/Translation.test.js +0 -204
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState, useImperativeHandle } from "react";
|
|
2
2
|
import TableMui from "@material-ui/core/Table";
|
|
3
3
|
import TableContainer from "@material-ui/core/TableContainer";
|
|
4
4
|
import TableHead from "@material-ui/core/TableHead";
|
|
@@ -10,6 +10,7 @@ import TableProps, { isTableProps } from "./TableProps";
|
|
|
10
10
|
import classNames from "classnames";
|
|
11
11
|
import ResizeDetector from "react-resize-detector";
|
|
12
12
|
import { isEqual } from "lodash";
|
|
13
|
+
import useViewState from "../../../hooks/useViewState";
|
|
13
14
|
|
|
14
15
|
export const useStyles = makeStyles(theme => ({
|
|
15
16
|
container: {
|
|
@@ -303,6 +304,14 @@ const buildTableRows = (
|
|
|
303
304
|
};
|
|
304
305
|
|
|
305
306
|
const FullTable = React.forwardRef((props, ref) => {
|
|
307
|
+
return props.saveScrollbarPosition ? (
|
|
308
|
+
<FullTableWithSavedScrollbar {...props} ref={ref} />
|
|
309
|
+
) : (
|
|
310
|
+
<DefaultFullTable {...props} ref={ref} />
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const DefaultFullTable = React.forwardRef((props, ref) => {
|
|
306
315
|
const scrollEvent = evt => {
|
|
307
316
|
if (
|
|
308
317
|
evt.target.scrollHeight - (evt.target.scrollTop + evt.target.offsetHeight) < 100 &&
|
|
@@ -310,6 +319,8 @@ const FullTable = React.forwardRef((props, ref) => {
|
|
|
310
319
|
) {
|
|
311
320
|
props.scrollLoader(props.latestPage + 1);
|
|
312
321
|
}
|
|
322
|
+
|
|
323
|
+
props.saveScrollBarPosition?.(evt);
|
|
313
324
|
};
|
|
314
325
|
|
|
315
326
|
return (
|
|
@@ -318,6 +329,7 @@ const FullTable = React.forwardRef((props, ref) => {
|
|
|
318
329
|
className={classNames(props.classes.tableContainer, props.customClasses.tableContainer)}
|
|
319
330
|
ref={ref}
|
|
320
331
|
onScroll={scrollEvent}
|
|
332
|
+
data-qa="scrollable-table-div"
|
|
321
333
|
>
|
|
322
334
|
<ResizeDetector onResize={props.onResize} />
|
|
323
335
|
<TableMui
|
|
@@ -345,132 +357,196 @@ const FullTable = React.forwardRef((props, ref) => {
|
|
|
345
357
|
);
|
|
346
358
|
});
|
|
347
359
|
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
rows,
|
|
352
|
-
scrollLoader,
|
|
353
|
-
latestPage,
|
|
354
|
-
pageLength,
|
|
355
|
-
placeholder,
|
|
356
|
-
tableProps,
|
|
357
|
-
context,
|
|
358
|
-
}) => {
|
|
359
|
-
if (isTableProps(tableProps) === false) {
|
|
360
|
-
throw new TypeError("tableProps property is not of type TableProps");
|
|
360
|
+
const FullTableWithSavedScrollbar = React.forwardRef((props, ref) => {
|
|
361
|
+
if (props.saveScrollbarPosition && !props.tableName) {
|
|
362
|
+
throw new Error("prop 'tableName' is required if 'saveScrollbarPosition' is set to true.");
|
|
361
363
|
}
|
|
362
364
|
|
|
363
|
-
const
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
customClasses["headerCell"] = tableProps?.getStyle(TableProps.ruleNames.headerCell) || null;
|
|
378
|
-
customClasses["tableContainer"] = tableProps?.getStyle(TableProps.ruleNames.tableContainer) || null;
|
|
379
|
-
customClasses["container"] = tableProps?.getStyle(TableProps.ruleNames.container) || null;
|
|
380
|
-
customClasses["table"] = tableProps?.getStyle(TableProps.ruleNames.table) || null;
|
|
381
|
-
|
|
382
|
-
if ((selectedRows && !selectedRowsChanged) || (!selectedRows && selectedRowsChanged))
|
|
383
|
-
throw new Error("Both 'selectedRows' and 'selectedRowsChanged' need to be defined if one of them is.");
|
|
384
|
-
|
|
385
|
-
const refScrolled = useRef();
|
|
386
|
-
|
|
387
|
-
const [scrolled, setScrolled] = useState(0);
|
|
388
|
-
const [tableSize, setTableSize] = useState({ width: 0, height: 0 });
|
|
389
|
-
|
|
390
|
-
const [selectedNumber, tableSelectionStatus, selectionMethods] = useTableSelection(
|
|
391
|
-
rows,
|
|
392
|
-
selectedRows,
|
|
393
|
-
selectedRowsChanged,
|
|
365
|
+
const [scrollbarViewState, updateScrollbarViewState] = useViewState(props.tableName + "ScrollbarPosition");
|
|
366
|
+
const [scrollbar, setScrollbarPosition] = useState(scrollbarViewState.scrollBarPosition);
|
|
367
|
+
|
|
368
|
+
useEffect(
|
|
369
|
+
() => {
|
|
370
|
+
const handler = setTimeout(() => {
|
|
371
|
+
updateScrollbarViewState("scrollBarPosition", scrollbar);
|
|
372
|
+
}, 500);
|
|
373
|
+
return () => {
|
|
374
|
+
clearTimeout(handler);
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
378
|
+
[scrollbar],
|
|
394
379
|
);
|
|
395
380
|
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
stickyHeader,
|
|
400
|
-
scrolled,
|
|
401
|
-
onRowClick,
|
|
402
|
-
});
|
|
381
|
+
const saveScrollBarPosition = evt => {
|
|
382
|
+
setScrollbarPosition(evt.target.scrollTop);
|
|
383
|
+
};
|
|
403
384
|
|
|
404
385
|
useEffect(() => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
386
|
+
if (scrollbarViewState.scrollBarPosition > 0) {
|
|
387
|
+
// The Table component usually display the provided list ASAP however it can happen that the Table is not fully rendered with its items when we attempt to change the scrollTop value.
|
|
388
|
+
// The timer should ensure that the list is rendered before attempting to change the scrollTop if the first attempt was not able to set the scroll position
|
|
389
|
+
// This edge case was found using these steps:
|
|
390
|
+
// * Create an organization with 50+ customers
|
|
391
|
+
// * Go to the customer section of the organization and scroll down a bit
|
|
392
|
+
// * Wait 1 second for the scroll position to be stored
|
|
393
|
+
// * Go to the organization list (click on the tab)
|
|
394
|
+
// * Go back to the organization (its tab should still be visible)
|
|
395
|
+
// * We expect the customer list to remember its scroll position but it wasn't the case
|
|
396
|
+
//
|
|
397
|
+
// In addition to the timer, the tableName was added as a dependency to force the effect to execute again when it changes.
|
|
398
|
+
// The tableName value in the organization's customers page is composed with 2 parts: "OrganizationCustomers_" + organizationId. This allows 2 organizations to use different scroll position for their respective customers pages.
|
|
399
|
+
// When the page first load, it is possible for organizationId to not yet be defined (strange but it's routing related) and because of that this useEffect was executed but with an unknown scrollBarPosition.
|
|
400
|
+
// The organizationId will be set on the next render and this useEffect needs be executed again to have the list scrolled to the correct position.
|
|
401
|
+
|
|
402
|
+
const previousScrollTop = ref.current.scrollTop;
|
|
403
|
+
ref.current.scrollTop = scrollbarViewState.scrollBarPosition;
|
|
404
|
+
|
|
405
|
+
// Note AD20250714: I was not able to create a test for the code below because it needs to run in a real browser otherwise the behavior with scrollTop does not match reality
|
|
406
|
+
/* istanbul ignore if */
|
|
407
|
+
if (ref.current.scrollTop === previousScrollTop) {
|
|
408
|
+
setTimeout(() => {
|
|
409
|
+
// using a timer to give a chance to the UI to populate the list
|
|
410
|
+
ref.current.scrollTop = scrollbarViewState.scrollBarPosition;
|
|
411
|
+
}, 250);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
415
|
+
}, [props.tableName]); // forcing this effect to run again when the tableName changes
|
|
410
416
|
|
|
411
|
-
|
|
417
|
+
return <DefaultFullTable {...props} ref={ref} saveScrollBarPosition={saveScrollBarPosition} />;
|
|
418
|
+
});
|
|
412
419
|
|
|
413
|
-
|
|
420
|
+
const Table = React.forwardRef(
|
|
421
|
+
({ tableInfo, headers, rows, scrollLoader, latestPage, pageLength, placeholder, tableProps, context }, ref) => {
|
|
422
|
+
if (isTableProps(tableProps) === false) {
|
|
423
|
+
throw new TypeError("tableProps property is not of type TableProps");
|
|
424
|
+
}
|
|
414
425
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
426
|
+
const customClasses = tableProps?.get(TableProps.propNames.classes) || {};
|
|
427
|
+
const selectMode = tableProps?.get(TableProps.propNames.selectMode) || false;
|
|
428
|
+
const stickyHeader = tableProps?.get(TableProps.propNames.stickyHeader) || false;
|
|
429
|
+
const withoutTopBorder = tableProps?.get(TableProps.propNames.withoutTopBorder) || false;
|
|
430
|
+
const onRowClick = tableProps?.get(TableProps.propNames.onRowClick) || null;
|
|
431
|
+
const deepPropsComparation = tableProps?.get(TableProps.propNames.deepPropsComparation) || false;
|
|
432
|
+
const isEditingMode = tableProps?.get(TableProps.propNames.isEditingMode) || false;
|
|
433
|
+
const selectedRows = tableProps?.get(TableProps.propNames.selectedRows) || null;
|
|
434
|
+
const selectedRowsChanged = tableProps?.get(TableProps.propNames.selectedRowsChanged) || null;
|
|
435
|
+
const constrained = tableProps?.get(TableProps.propNames.constrained) || false;
|
|
436
|
+
const tableName = tableProps?.get(TableProps.propNames.tableName) || null;
|
|
437
|
+
const saveScrollbarPosition = tableProps?.get(TableProps.propNames.saveScrollbarPosition) || false;
|
|
438
|
+
|
|
439
|
+
customClasses["tableHeader"] = tableProps?.getStyle(TableProps.ruleNames.tableHeader) || null;
|
|
440
|
+
customClasses["tableRow"] = tableProps?.getStyle(TableProps.ruleNames.tableRow) || null;
|
|
441
|
+
customClasses["tableCell"] = tableProps?.getStyle(TableProps.ruleNames.tableCell) || null;
|
|
442
|
+
customClasses["headerCell"] = tableProps?.getStyle(TableProps.ruleNames.headerCell) || null;
|
|
443
|
+
customClasses["tableContainer"] = tableProps?.getStyle(TableProps.ruleNames.tableContainer) || null;
|
|
444
|
+
customClasses["container"] = tableProps?.getStyle(TableProps.ruleNames.container) || null;
|
|
445
|
+
customClasses["table"] = tableProps?.getStyle(TableProps.ruleNames.table) || null;
|
|
446
|
+
|
|
447
|
+
if ((selectedRows && !selectedRowsChanged) || (!selectedRows && selectedRowsChanged))
|
|
448
|
+
throw new Error("Both 'selectedRows' and 'selectedRowsChanged' need to be defined if one of them is.");
|
|
449
|
+
|
|
450
|
+
const refScrolled = useRef();
|
|
451
|
+
|
|
452
|
+
useImperativeHandle(ref, () => ({
|
|
453
|
+
scrollToTop: () => {
|
|
454
|
+
if (refScrolled.current.scrollTop > 0) {
|
|
455
|
+
refScrolled.current.scrollTop = 0;
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
}));
|
|
459
|
+
|
|
460
|
+
const [scrolled, setScrolled] = useState(0);
|
|
461
|
+
const [tableSize, setTableSize] = useState({ width: 0, height: 0 });
|
|
462
|
+
|
|
463
|
+
const [selectedNumber, tableSelectionStatus, selectionMethods] = useTableSelection(
|
|
464
|
+
rows,
|
|
465
|
+
selectedRows,
|
|
466
|
+
selectedRowsChanged,
|
|
467
|
+
);
|
|
427
468
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
469
|
+
const classes = useStyles({
|
|
470
|
+
withoutTopBorder,
|
|
471
|
+
selectMode,
|
|
472
|
+
stickyHeader,
|
|
473
|
+
scrolled,
|
|
474
|
+
onRowClick,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
useEffect(() => {
|
|
478
|
+
const handleResize = () => {
|
|
479
|
+
if (refScrolled.current.offsetHeight < refScrolled.current.scrollHeight)
|
|
480
|
+
setScrolled(refScrolled.current.offsetWidth - refScrolled.current.clientWidth);
|
|
481
|
+
else setScrolled(0);
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
handleResize();
|
|
485
|
+
|
|
486
|
+
window.addEventListener("resize", handleResize);
|
|
487
|
+
|
|
488
|
+
/* istanbul ignore next */
|
|
489
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
490
|
+
}, [refScrolled, tableSize.width, tableSize.height]);
|
|
491
|
+
|
|
492
|
+
const tableHeaders = buildTableHeaders(
|
|
493
|
+
headers,
|
|
494
|
+
classes,
|
|
495
|
+
customClasses,
|
|
496
|
+
selectMode,
|
|
497
|
+
tableSelectionStatus,
|
|
498
|
+
selectionMethods,
|
|
499
|
+
);
|
|
439
500
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
501
|
+
const tableRows = buildTableRows(
|
|
502
|
+
rows,
|
|
503
|
+
classes,
|
|
504
|
+
customClasses,
|
|
505
|
+
selectMode,
|
|
506
|
+
onRowClick,
|
|
507
|
+
selectionMethods,
|
|
508
|
+
deepPropsComparation,
|
|
509
|
+
context,
|
|
510
|
+
isEditingMode,
|
|
511
|
+
);
|
|
444
512
|
|
|
445
|
-
|
|
446
|
-
|
|
513
|
+
const stickerTableHeader =
|
|
514
|
+
stickyHeader === true ? (
|
|
515
|
+
<StickerTableHeader selectMode={selectMode} classes={classes} tableHeaders={tableHeaders} />
|
|
516
|
+
) : null;
|
|
447
517
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
<
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
518
|
+
/* istanbul ignore next */
|
|
519
|
+
const onResize = useCallback((width, height) => setTableSize({ width: width, height: height }), [setTableSize]);
|
|
520
|
+
|
|
521
|
+
return (
|
|
522
|
+
<TableContainer className={classNames(classes.container, customClasses.container)}>
|
|
523
|
+
{tableInfo}
|
|
524
|
+
{stickerTableHeader}
|
|
525
|
+
<FullTable
|
|
526
|
+
ref={refScrolled}
|
|
527
|
+
classes={classes}
|
|
528
|
+
customClasses={customClasses}
|
|
529
|
+
constrained={constrained}
|
|
530
|
+
onResize={onResize}
|
|
531
|
+
selectedNumber={selectedNumber}
|
|
532
|
+
scrollLoader={scrollLoader}
|
|
533
|
+
tableHeaders={tableHeaders}
|
|
534
|
+
dataRows={rows}
|
|
535
|
+
tableRows={tableRows}
|
|
536
|
+
stickyHeader={stickyHeader}
|
|
537
|
+
latestPage={latestPage}
|
|
538
|
+
pageLength={pageLength}
|
|
539
|
+
placeholder={placeholder}
|
|
540
|
+
deepPropsComparation={deepPropsComparation}
|
|
541
|
+
isEditingMode={isEditingMode}
|
|
542
|
+
context={context}
|
|
543
|
+
tableName={tableName}
|
|
544
|
+
saveScrollbarPosition={saveScrollbarPosition}
|
|
545
|
+
/>
|
|
546
|
+
</TableContainer>
|
|
547
|
+
);
|
|
548
|
+
},
|
|
549
|
+
);
|
|
474
550
|
|
|
475
551
|
export default React.memo(
|
|
476
552
|
Table,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useRef } from "react";
|
|
2
2
|
import ReactDOM from "react-dom";
|
|
3
3
|
import { act } from "react-dom/test-utils";
|
|
4
4
|
import sinon from "sinon";
|
|
@@ -19,6 +19,8 @@ import ResizeDetector from "react-resize-detector";
|
|
|
19
19
|
import CheckboxMui from "../Inputs/Checkbox";
|
|
20
20
|
import { cloneDeep } from "lodash";
|
|
21
21
|
import TooltippedTypography from "./TooltippedElements/TooltippedTypography";
|
|
22
|
+
import Immutable from "immutable";
|
|
23
|
+
import { TestWrapper } from "../../../utils/testUtils";
|
|
22
24
|
|
|
23
25
|
const TestComp = ({ classToTest, styleProps }) => {
|
|
24
26
|
const classes = useStyles({ ...styleProps });
|
|
@@ -507,6 +509,22 @@ describe("Table", () => {
|
|
|
507
509
|
{ fieldName: "a2", label: headerLabels.column2 },
|
|
508
510
|
];
|
|
509
511
|
|
|
512
|
+
let store, state;
|
|
513
|
+
|
|
514
|
+
beforeEach(() => {
|
|
515
|
+
state = Immutable.fromJS({
|
|
516
|
+
modules: {
|
|
517
|
+
tree: {},
|
|
518
|
+
},
|
|
519
|
+
view: {},
|
|
520
|
+
});
|
|
521
|
+
store = {
|
|
522
|
+
subscribe: () => {},
|
|
523
|
+
getState: () => state,
|
|
524
|
+
dispatch: sinon.spy().named("dispatch"),
|
|
525
|
+
};
|
|
526
|
+
});
|
|
527
|
+
|
|
510
528
|
it("Fails if tableProps has wrong type", () => {
|
|
511
529
|
ignoreConsoleError(() => {
|
|
512
530
|
const component = <Table rows={[]} headers={[]} tableProps="Wrong type" />;
|
|
@@ -1113,4 +1131,231 @@ describe("Table", () => {
|
|
|
1113
1131
|
|
|
1114
1132
|
expect(style.customClass, "to equal", "specialClass");
|
|
1115
1133
|
});
|
|
1134
|
+
|
|
1135
|
+
it("Table throws if saveScrollbar is true but tableName is falsy", () => {
|
|
1136
|
+
const { headers, rows } = buildHeaderAndRowFromConfig(config, elements);
|
|
1137
|
+
|
|
1138
|
+
const tableProps = new TableProps();
|
|
1139
|
+
|
|
1140
|
+
tableProps.set(TableProps.propNames.tableName, null);
|
|
1141
|
+
tableProps.set(TableProps.propNames.saveScrollbarPosition, true);
|
|
1142
|
+
|
|
1143
|
+
ignoreConsoleError(() => {
|
|
1144
|
+
const component = <Table rows={rows} headers={headers} tableProps={tableProps} />;
|
|
1145
|
+
expect(() => mount(component), "to throw a", Error).then(error => {
|
|
1146
|
+
expect(error, "to have message", "prop 'tableName' is required if 'saveScrollbarPosition' is set to true.");
|
|
1147
|
+
});
|
|
1148
|
+
});
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
it("handle scrolling event with save ", () => {
|
|
1152
|
+
const { headers, rows } = buildHeaderAndRowFromConfig(config, elements);
|
|
1153
|
+
|
|
1154
|
+
const tableProps = new TableProps();
|
|
1155
|
+
|
|
1156
|
+
tableProps.set(TableProps.propNames.selectMode, true);
|
|
1157
|
+
tableProps.set(TableProps.propNames.tableName, "test");
|
|
1158
|
+
tableProps.set(TableProps.propNames.saveScrollbarPosition, true);
|
|
1159
|
+
|
|
1160
|
+
const scrollLoader = sinon.spy().named("scrollLoader");
|
|
1161
|
+
|
|
1162
|
+
const component = (
|
|
1163
|
+
<TestWrapper provider={{ store }}>
|
|
1164
|
+
<Table
|
|
1165
|
+
rows={rows}
|
|
1166
|
+
headers={headers}
|
|
1167
|
+
pageLength={2}
|
|
1168
|
+
latestPage={1}
|
|
1169
|
+
tableProps={tableProps}
|
|
1170
|
+
scrollLoader={scrollLoader}
|
|
1171
|
+
/>
|
|
1172
|
+
</TestWrapper>
|
|
1173
|
+
);
|
|
1174
|
+
|
|
1175
|
+
const mountedComponent = mount(component);
|
|
1176
|
+
|
|
1177
|
+
const scrollEvent = document.createEvent("MouseEvents");
|
|
1178
|
+
scrollEvent.initEvent("scroll", true, false);
|
|
1179
|
+
|
|
1180
|
+
const table = mountedComponent.find(TableMui);
|
|
1181
|
+
|
|
1182
|
+
table.simulate("scroll", {
|
|
1183
|
+
target: { scrollHeight: 1000, scrollTop: 40, offsetHeight: 100 },
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
expect(scrollLoader, "was not called");
|
|
1187
|
+
|
|
1188
|
+
table.simulate("scroll", {
|
|
1189
|
+
target: { scrollHeight: 1000, scrollTop: 850, offsetHeight: 100 },
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
return expect(scrollLoader, "to have calls satisfying", [{ args: [2] }])
|
|
1193
|
+
.then(() => new Promise(r => setTimeout(r, 600)))
|
|
1194
|
+
.then(() =>
|
|
1195
|
+
expect(store.dispatch, "to have calls satisfying", [
|
|
1196
|
+
{
|
|
1197
|
+
args: [
|
|
1198
|
+
{
|
|
1199
|
+
type: "VIEW_STATE_SET_FIELD",
|
|
1200
|
+
payload: {
|
|
1201
|
+
name: "testScrollbarPosition",
|
|
1202
|
+
field: "scrollBarPosition",
|
|
1203
|
+
value: 850,
|
|
1204
|
+
},
|
|
1205
|
+
},
|
|
1206
|
+
],
|
|
1207
|
+
},
|
|
1208
|
+
]),
|
|
1209
|
+
);
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
it("should initialize scrollbar to saved state", () => {
|
|
1213
|
+
const { headers, rows } = buildHeaderAndRowFromConfig(config, elements);
|
|
1214
|
+
|
|
1215
|
+
state = state.setIn(
|
|
1216
|
+
["view", "testScrollbarPosition"],
|
|
1217
|
+
Immutable.fromJS({
|
|
1218
|
+
scrollBarPosition: 800,
|
|
1219
|
+
}),
|
|
1220
|
+
);
|
|
1221
|
+
|
|
1222
|
+
const tableProps = new TableProps();
|
|
1223
|
+
|
|
1224
|
+
tableProps.set(TableProps.propNames.selectMode, true);
|
|
1225
|
+
tableProps.set(TableProps.propNames.tableName, "test");
|
|
1226
|
+
tableProps.set(TableProps.propNames.saveScrollbarPosition, true);
|
|
1227
|
+
|
|
1228
|
+
const scrollLoader = sinon.spy().named("scrollLoader");
|
|
1229
|
+
|
|
1230
|
+
const component = (
|
|
1231
|
+
<TestWrapper provider={{ store }}>
|
|
1232
|
+
<Table
|
|
1233
|
+
rows={rows}
|
|
1234
|
+
headers={headers}
|
|
1235
|
+
pageLength={2}
|
|
1236
|
+
latestPage={1}
|
|
1237
|
+
tableProps={tableProps}
|
|
1238
|
+
scrollLoader={scrollLoader}
|
|
1239
|
+
/>
|
|
1240
|
+
</TestWrapper>
|
|
1241
|
+
);
|
|
1242
|
+
|
|
1243
|
+
const mountedComponent = mount(component);
|
|
1244
|
+
const scrollableDiv = mountedComponent.find({ "data-qa": "scrollable-table-div" });
|
|
1245
|
+
expect(scrollableDiv?.getElement()?.ref?.current?.scrollTop, "to equal", 800);
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
it("should scroll to top", () => {
|
|
1249
|
+
const { headers, rows } = buildHeaderAndRowFromConfig(config, elements);
|
|
1250
|
+
|
|
1251
|
+
state = state.setIn(
|
|
1252
|
+
["view", "testScrollbarPosition"],
|
|
1253
|
+
Immutable.fromJS({
|
|
1254
|
+
scrollBarPosition: 800,
|
|
1255
|
+
}),
|
|
1256
|
+
);
|
|
1257
|
+
|
|
1258
|
+
const tableProps = new TableProps();
|
|
1259
|
+
|
|
1260
|
+
tableProps.set(TableProps.propNames.selectMode, true);
|
|
1261
|
+
tableProps.set(TableProps.propNames.tableName, "test");
|
|
1262
|
+
tableProps.set(TableProps.propNames.saveScrollbarPosition, true);
|
|
1263
|
+
|
|
1264
|
+
const scrollLoader = sinon.spy().named("scrollLoader");
|
|
1265
|
+
|
|
1266
|
+
const CustomTable = () => {
|
|
1267
|
+
const ref = useRef(null);
|
|
1268
|
+
|
|
1269
|
+
const clickToTop = () => {
|
|
1270
|
+
ref.current && ref.current.scrollToTop();
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
return (
|
|
1274
|
+
<>
|
|
1275
|
+
<input type="button" onClick={clickToTop} data-qa="scroll-btn" />
|
|
1276
|
+
<Table
|
|
1277
|
+
ref={ref}
|
|
1278
|
+
rows={rows}
|
|
1279
|
+
headers={headers}
|
|
1280
|
+
pageLength={2}
|
|
1281
|
+
latestPage={1}
|
|
1282
|
+
tableProps={tableProps}
|
|
1283
|
+
scrollLoader={scrollLoader}
|
|
1284
|
+
/>
|
|
1285
|
+
</>
|
|
1286
|
+
);
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
const component = (
|
|
1290
|
+
<TestWrapper provider={{ store }}>
|
|
1291
|
+
<CustomTable />
|
|
1292
|
+
</TestWrapper>
|
|
1293
|
+
);
|
|
1294
|
+
|
|
1295
|
+
const mountedComponent = mount(component);
|
|
1296
|
+
const scrollableDiv = mountedComponent.find({ "data-qa": "scrollable-table-div" });
|
|
1297
|
+
expect(scrollableDiv?.getElement()?.ref?.current?.scrollTop, "to equal", 800);
|
|
1298
|
+
|
|
1299
|
+
const scrollBtn = mountedComponent.find({ "data-qa": "scroll-btn" });
|
|
1300
|
+
scrollBtn.simulate("click");
|
|
1301
|
+
|
|
1302
|
+
expect(scrollableDiv?.getElement()?.ref?.current?.scrollTop, "to equal", 0);
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
it("should not scroll to top because already at the top", () => {
|
|
1306
|
+
const { headers, rows } = buildHeaderAndRowFromConfig(config, elements);
|
|
1307
|
+
|
|
1308
|
+
state = state.setIn(
|
|
1309
|
+
["view", "testScrollbarPosition"],
|
|
1310
|
+
Immutable.fromJS({
|
|
1311
|
+
scrollBarPosition: 0,
|
|
1312
|
+
}),
|
|
1313
|
+
);
|
|
1314
|
+
|
|
1315
|
+
const tableProps = new TableProps();
|
|
1316
|
+
|
|
1317
|
+
tableProps.set(TableProps.propNames.selectMode, true);
|
|
1318
|
+
tableProps.set(TableProps.propNames.tableName, "test");
|
|
1319
|
+
tableProps.set(TableProps.propNames.saveScrollbarPosition, true);
|
|
1320
|
+
|
|
1321
|
+
const scrollLoader = sinon.spy().named("scrollLoader");
|
|
1322
|
+
|
|
1323
|
+
const CustomTable = () => {
|
|
1324
|
+
const ref = useRef(null);
|
|
1325
|
+
|
|
1326
|
+
const clickToTop = () => {
|
|
1327
|
+
ref.current && ref.current.scrollToTop();
|
|
1328
|
+
};
|
|
1329
|
+
|
|
1330
|
+
return (
|
|
1331
|
+
<>
|
|
1332
|
+
<input type="button" onClick={clickToTop} data-qa="scroll-btn" />
|
|
1333
|
+
<Table
|
|
1334
|
+
ref={ref}
|
|
1335
|
+
rows={rows}
|
|
1336
|
+
headers={headers}
|
|
1337
|
+
pageLength={2}
|
|
1338
|
+
latestPage={1}
|
|
1339
|
+
tableProps={tableProps}
|
|
1340
|
+
scrollLoader={scrollLoader}
|
|
1341
|
+
/>
|
|
1342
|
+
</>
|
|
1343
|
+
);
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1346
|
+
const component = (
|
|
1347
|
+
<TestWrapper provider={{ store }}>
|
|
1348
|
+
<CustomTable />
|
|
1349
|
+
</TestWrapper>
|
|
1350
|
+
);
|
|
1351
|
+
|
|
1352
|
+
const mountedComponent = mount(component);
|
|
1353
|
+
const scrollableDiv = mountedComponent.find({ "data-qa": "scrollable-table-div" });
|
|
1354
|
+
expect(scrollableDiv?.getElement()?.ref?.current?.scrollTop, "to equal", 0);
|
|
1355
|
+
|
|
1356
|
+
const scrollBtn = mountedComponent.find({ "data-qa": "scroll-btn" });
|
|
1357
|
+
scrollBtn.simulate("click");
|
|
1358
|
+
|
|
1359
|
+
expect(scrollableDiv?.getElement()?.ref?.current?.scrollTop, "to equal", 0);
|
|
1360
|
+
});
|
|
1116
1361
|
});
|
|
@@ -12,6 +12,8 @@ class TableProps extends ComponentProps {
|
|
|
12
12
|
selectedRows: "selectedRows",
|
|
13
13
|
selectedRowsChanged: "selectedRowsChanged",
|
|
14
14
|
constrained: "constrained",
|
|
15
|
+
tableName: "tableName",
|
|
16
|
+
saveScrollbarPosition: "saveScrollbarPosition",
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
static ruleNames = {
|
|
@@ -37,6 +39,8 @@ class TableProps extends ComponentProps {
|
|
|
37
39
|
this.componentProps.set(this.constructor.propNames.selectedRows, null);
|
|
38
40
|
this.componentProps.set(this.constructor.propNames.selectedRowsChanged, null);
|
|
39
41
|
this.componentProps.set(this.constructor.propNames.constrained, false);
|
|
42
|
+
this.componentProps.set(this.constructor.propNames.tableName, null);
|
|
43
|
+
this.componentProps.set(this.constructor.propNames.saveScrollbarPosition, null);
|
|
40
44
|
|
|
41
45
|
this.componentClasses.set(this.constructor.ruleNames.tableHeader, null);
|
|
42
46
|
this.componentClasses.set(this.constructor.ruleNames.tableRow, null);
|
|
@@ -30,6 +30,8 @@ describe("TableProps Props", () => {
|
|
|
30
30
|
"selectedRows",
|
|
31
31
|
"selectedRowsChanged",
|
|
32
32
|
"constrained",
|
|
33
|
+
"tableName",
|
|
34
|
+
"saveScrollbarPosition",
|
|
33
35
|
];
|
|
34
36
|
|
|
35
37
|
const ruleNames = ["tableHeader", "tableRow", "tableCell", "headerCell", "tableContainer", "container", "table"];
|