@onewelcome/react-lib-components 1.3.0 → 1.5.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 (118) hide show
  1. package/README.md +7 -0
  2. package/dist/Breadcrumbs/Breadcrumbs.d.ts +1 -1
  3. package/dist/Button/IconButton.d.ts +2 -1
  4. package/dist/ContextMenu/ContextMenu.d.ts +4 -1
  5. package/dist/DataGrid/DataGrid.d.ts +1 -0
  6. package/dist/DataGrid/DataGridActions/DataGridActions.d.ts +2 -1
  7. package/dist/DataGrid/DataGridActions/DataGridColumnsToggle.d.ts +1 -1
  8. package/dist/DataGrid/datagrid.interfaces.d.ts +4 -4
  9. package/dist/Form/Form.d.ts +3 -3
  10. package/dist/Form/FormControl/FormControl.d.ts +1 -1
  11. package/dist/Form/Input/Input.d.ts +1 -1
  12. package/dist/Form/Select/Select.d.ts +1 -1
  13. package/dist/Form/Select/Select.interfaces.d.ts +1 -1
  14. package/dist/Icon/Icon.d.ts +1 -1
  15. package/dist/Link/Link.d.ts +1 -1
  16. package/dist/Notifications/SlideInModal/SlideInModal.d.ts +1 -1
  17. package/dist/Notifications/Snackbar/interfaces.d.ts +2 -2
  18. package/dist/Pagination/Pagination.d.ts +3 -3
  19. package/dist/Popover/Popover.d.ts +3 -1
  20. package/dist/Tabs/Tab.d.ts +5 -9
  21. package/dist/Tabs/TabButton.d.ts +3 -6
  22. package/dist/Tabs/Tabs.d.ts +1 -2
  23. package/dist/Typography/Typography.d.ts +2 -2
  24. package/dist/Wizard/Wizard.d.ts +1 -1
  25. package/dist/Wizard/wizardStateReducer.d.ts +2 -2
  26. package/dist/_BaseStyling_/BaseStyling.d.ts +2 -0
  27. package/dist/hooks/usePosition.d.ts +6 -5
  28. package/dist/hooks/useSpacing.d.ts +3 -3
  29. package/dist/interfaces.d.ts +1 -1
  30. package/dist/react-lib-components.cjs.development.js +507 -440
  31. package/dist/react-lib-components.cjs.development.js.map +1 -1
  32. package/dist/react-lib-components.cjs.production.min.js +1 -1
  33. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  34. package/dist/react-lib-components.esm.js +508 -441
  35. package/dist/react-lib-components.esm.js.map +1 -1
  36. package/dist/util/helper.d.ts +1 -1
  37. package/package.json +36 -35
  38. package/src/Breadcrumbs/Breadcrumbs.tsx +46 -38
  39. package/src/Button/BaseButton.tsx +23 -20
  40. package/src/Button/Button.tsx +40 -40
  41. package/src/Button/IconButton.tsx +28 -28
  42. package/src/ContextMenu/ContextMenu.tsx +180 -168
  43. package/src/ContextMenu/ContextMenuItem.tsx +55 -49
  44. package/src/DataGrid/DataGrid.tsx +1 -0
  45. package/src/DataGrid/DataGridActions/DataGridActions.module.scss +1 -1
  46. package/src/DataGrid/DataGridActions/DataGridActions.test.tsx +4 -2
  47. package/src/DataGrid/DataGridActions/DataGridActions.tsx +95 -87
  48. package/src/DataGrid/DataGridActions/DataGridColumnsToggle.tsx +64 -64
  49. package/src/DataGrid/DataGridBody/DataGridCell.tsx +42 -44
  50. package/src/DataGrid/DataGridBody/DataGridRow.tsx +29 -29
  51. package/src/DataGrid/DataGridHeader/DataGridHeader.tsx +78 -78
  52. package/src/DataGrid/DataGridHeader/DataGridHeaderCell.tsx +48 -48
  53. package/src/Form/Checkbox/Checkbox.tsx +134 -130
  54. package/src/Form/Fieldset/Fieldset.tsx +81 -78
  55. package/src/Form/Form.tsx +9 -4
  56. package/src/Form/FormControl/FormControl.module.scss +1 -20
  57. package/src/Form/FormControl/FormControl.tsx +36 -35
  58. package/src/Form/FormGroup/FormGroup.tsx +62 -62
  59. package/src/Form/FormHelperText/FormHelperText.tsx +19 -18
  60. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +58 -53
  61. package/src/Form/Input/Input.test.tsx +12 -0
  62. package/src/Form/Input/Input.tsx +91 -87
  63. package/src/Form/Label/Label.tsx +17 -16
  64. package/src/Form/Radio/Radio.tsx +91 -91
  65. package/src/Form/Select/Option.tsx +66 -60
  66. package/src/Form/Select/Select.tsx +207 -209
  67. package/src/Form/Textarea/Textarea.tsx +51 -53
  68. package/src/Form/Toggle/Toggle.tsx +26 -23
  69. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +51 -43
  70. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +112 -106
  71. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +67 -62
  72. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +42 -37
  73. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +94 -94
  74. package/src/Form/Wrapper/Wrapper/Wrapper.tsx +73 -73
  75. package/src/Icon/Icon.tsx +19 -16
  76. package/src/Link/Link.tsx +68 -63
  77. package/src/Notifications/BaseModal/BaseModal.tsx +68 -68
  78. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.tsx +13 -10
  79. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.tsx +33 -25
  80. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.tsx +20 -17
  81. package/src/Notifications/Dialog/Dialog.tsx +83 -83
  82. package/src/Notifications/Dialog/DialogActions/DialogActions.tsx +17 -14
  83. package/src/Notifications/Dialog/DialogTitle/DialogTitle.tsx +15 -12
  84. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +40 -40
  85. package/src/Notifications/SlideInModal/SlideInModal.module.scss +5 -5
  86. package/src/Notifications/SlideInModal/SlideInModal.test.tsx +7 -2
  87. package/src/Notifications/SlideInModal/SlideInModal.tsx +47 -27
  88. package/src/Pagination/Pagination.tsx +169 -169
  89. package/src/Popover/Popover.module.scss +1 -0
  90. package/src/Popover/Popover.test.tsx +4 -1
  91. package/src/Popover/Popover.tsx +74 -32
  92. package/src/ProgressBar/ProgressBar.tsx +17 -14
  93. package/src/Skeleton/Skeleton.tsx +23 -20
  94. package/src/StatusIndicator/StatusIndicator.tsx +18 -15
  95. package/src/Tabs/{TabPanel.module.scss → Tab.module.scss} +1 -1
  96. package/src/Tabs/Tab.test.tsx +1 -39
  97. package/src/Tabs/Tab.tsx +16 -10
  98. package/src/Tabs/TabButton.module.scss +0 -4
  99. package/src/Tabs/TabButton.test.tsx +3 -31
  100. package/src/Tabs/TabButton.tsx +35 -49
  101. package/src/Tabs/Tabs.test.tsx +40 -33
  102. package/src/Tabs/Tabs.tsx +107 -101
  103. package/src/TextEllipsis/TextEllipsis.tsx +50 -41
  104. package/src/Tiles/Tile.tsx +58 -56
  105. package/src/Tiles/Tiles.tsx +44 -41
  106. package/src/Tooltip/Tooltip.test.tsx +5 -5
  107. package/src/Tooltip/Tooltip.tsx +101 -100
  108. package/src/Typography/Typography.tsx +47 -44
  109. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +55 -52
  110. package/src/Wizard/WizardSteps/WizardSteps.tsx +25 -22
  111. package/src/_BaseStyling_/BaseStyling.tsx +4 -0
  112. package/src/hooks/usePosition.test.tsx +85 -85
  113. package/src/hooks/usePosition.ts +6 -3
  114. package/src/mixins.module.scss +7 -7
  115. package/src/util/helper.test.tsx +0 -28
  116. package/dist/Tabs/TabPanel.d.ts +0 -8
  117. package/src/Tabs/TabPanel.test.tsx +0 -92
  118. package/src/Tabs/TabPanel.tsx +0 -43
@@ -14,17 +14,17 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- @import "../../mixins.module.scss";
17
+ @use "../../mixins.module.scss";
18
18
 
19
19
  .slide-in-modal {
20
20
  justify-content: flex-end;
21
21
 
22
22
  visibility: visible;
23
23
 
24
- @include transition(transform, 0.5s, ease-in-out);
24
+ @include mixins.transition(transform, 0.5s, ease-in-out);
25
25
  transform: translate(120%);
26
26
 
27
- &.hidden {
27
+ &.hide {
28
28
  visibility: hidden;
29
29
  }
30
30
 
@@ -32,13 +32,13 @@
32
32
  visibility: visible;
33
33
  transform: translate(0%);
34
34
 
35
- .backdrop-slide {
35
+ #backdrop-slide {
36
36
  background-color: transparent;
37
37
  }
38
38
  }
39
39
  }
40
40
 
41
- .backdrop-slide {
41
+ #backdrop-slide {
42
42
  background-color: transparent;
43
43
  }
44
44
 
@@ -49,12 +49,17 @@ describe("SlideInModal should render", () => {
49
49
  it("makes modal content's container visible after opening transition ends", () => {
50
50
  const { slideInModal, rerender } = createSlideInModal();
51
51
 
52
- expect(slideInModal).toHaveClass("hidden");
52
+ expect(slideInModal).toHaveClass("hide");
53
53
 
54
54
  rerender(<SlideInModal {...defaultParams} open />);
55
55
  fireEvent.transitionEnd(slideInModal);
56
56
 
57
- expect(slideInModal).not.toHaveClass("hidden");
57
+ expect(slideInModal).not.toHaveClass("hide");
58
+
59
+ rerender(<SlideInModal {...defaultParams} open={false} />);
60
+ fireEvent.transitionEnd(slideInModal);
61
+
62
+ expect(slideInModal).toHaveClass("hide");
58
63
  });
59
64
  });
60
65
 
@@ -14,39 +14,59 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import React, { TransitionEventHandler, useState, useRef } from "react";
17
+ import React, {
18
+ ForwardRefRenderFunction,
19
+ TransitionEventHandler,
20
+ useState,
21
+ useRef,
22
+ useCallback,
23
+ useEffect
24
+ } from "react";
18
25
  import { Props as ModalProps, Modal } from "../Modal/Modal";
19
26
  import classes from "./SlideInModal.module.scss";
20
27
 
21
- export const SlideInModal = React.forwardRef<HTMLDivElement, ModalProps>(
22
- ({ children, id, open, ...rest }: ModalProps, ref) => {
23
- const [classHideOnTransition, setClassHideOnTransition] = useState<string>("hidden");
24
- const containerRef = useRef(null);
28
+ const SlideInModalComponent: ForwardRefRenderFunction<HTMLDivElement, ModalProps> = (
29
+ { children, id, open, ...rest }: ModalProps,
30
+ ref
31
+ ) => {
32
+ const [classHideOnTransition, setClassHideOnTransition] = useState<string>("hide");
33
+ const [controlledOpen, setControlledOpen] = useState(false);
34
+ const containerRef = useRef(null);
25
35
 
26
- const onTransitionEnd: TransitionEventHandler<HTMLDivElement> = e => {
36
+ const onTransitionEnd: TransitionEventHandler<HTMLDivElement> = useCallback(
37
+ e => {
27
38
  if (e.target === containerRef.current) {
28
- setClassHideOnTransition(prev => (prev ? "" : "hidden"));
39
+ setClassHideOnTransition(prev => (prev ? "" : "hide"));
40
+ if (!open && controlledOpen) {
41
+ setControlledOpen(false);
42
+ }
29
43
  }
30
- };
44
+ },
45
+ [open]
46
+ );
31
47
 
32
- return (
33
- <Modal
34
- {...rest}
35
- id={id}
36
- open={open}
37
- className={`${classes["slide-in-modal"]} ${open ? classes["visible"] : ""} ${
38
- !open ? classes[classHideOnTransition] : ""
39
- }`}
40
- containerProps={{ className: classes["container"] }}
41
- backdropProps={{ className: classes["backdrop-slide"] }}
42
- forceContainerOpen
43
- onTransitionEnd={onTransitionEnd}
44
- ref={ref || containerRef}
45
- >
46
- {children}
47
- </Modal>
48
- );
49
- }
50
- );
48
+ useEffect(() => {
49
+ open && setControlledOpen(open);
50
+ }, [open]);
51
+
52
+ return (
53
+ <Modal
54
+ {...rest}
55
+ id={id}
56
+ open={controlledOpen}
57
+ className={`${classes["slide-in-modal"]} ${open ? classes["visible"] : ""} ${
58
+ !open ? classes[classHideOnTransition] : ""
59
+ }`}
60
+ containerProps={{ className: classes["container"] }}
61
+ backdropProps={{ id: classes["backdrop-slide"] }}
62
+ onTransitionEnd={onTransitionEnd}
63
+ ref={ref || containerRef}
64
+ >
65
+ {children}
66
+ </Modal>
67
+ );
68
+ };
51
69
 
52
70
  export { Props } from "../Modal/Modal";
71
+
72
+ export const SlideInModal = React.forwardRef(SlideInModalComponent);
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import React, { ComponentPropsWithRef, Fragment, useState } from "react";
17
+ import React, { ForwardRefRenderFunction, ComponentPropsWithRef, Fragment, useState } from "react";
18
18
  import classes from "./Pagination.module.scss";
19
19
  import readyclasses from "../readyclasses.module.scss";
20
20
  import { IconButton } from "../Button/IconButton";
@@ -53,183 +53,183 @@ export interface Props extends Omit<ComponentPropsWithRef<"div">, "translate"> {
53
53
  onPageSizeChange: (pageSize: PageSize) => void;
54
54
  }
55
55
 
56
- export const Pagination = React.forwardRef<HTMLDivElement, Props>(
57
- (
58
- {
59
- totalElements,
60
- pageSize = 10,
61
- translate = DefaultTranslations,
62
- currentPage,
63
- className,
64
- onPageChange,
65
- onPageSizeChange,
66
- ...rest
67
- }: Props,
68
- ref
69
- ) => {
70
- /** We use an internal state variable, because we don't want to fire onCurrentPageChange whenever onChange fires on the input. Rather, only when the Enter key is pressed. */
71
- const [internalCurrentPage, setInternalCurrentPage] = useState(currentPage?.toString() || "1");
72
- const calculateAmountOfPages = () => {
73
- if (!totalElements) return 1;
74
-
75
- if (Math.ceil(totalElements / pageSize) < 1) {
76
- return 1;
77
- }
78
-
79
- return Math.ceil(totalElements / pageSize);
80
- };
81
-
82
- const onEnterListener = (event: React.KeyboardEvent<HTMLInputElement>) => {
83
- if (event.code === "Enter") {
84
- onPageChange(Number(internalCurrentPage));
85
- }
86
- };
87
-
88
- const renderCurrentPageTranslation = () => {
89
- const amountOfPages = calculateAmountOfPages();
90
-
91
- if (amountOfPages) {
92
- const splitCurrentPageTranslation = translate.currentPage.split(" ");
93
-
94
- return splitCurrentPageTranslation.map(string => {
95
- if (string.includes("%1")) {
96
- return (
97
- <Fragment key={string}>
98
- <Label
99
- id="current-value-input-label"
100
- htmlFor="current-value-input"
101
- className={`${readyclasses["sr-only"]} ${classes["current-value-input-label"]}`}
102
- >
103
- {translate.currentPageLabel}
104
- </Label>
105
- <Input
106
- aria-labelledby="current-value-input-label"
107
- key="input"
108
- id="current-value-input"
109
- type="text"
110
- size={currentPage?.toString().length}
111
- max={calculateAmountOfPages()}
112
- wrapperProps={{ className: classes["current-value-input"] }}
113
- onKeyUp={onEnterListener}
114
- onBlur={(event: React.ChangeEvent<HTMLInputElement>) =>
115
- onPageChange(Number(event.target.value))
116
- }
117
- onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
118
- setInternalCurrentPage(e.target.value)
119
- }
120
- name="current-value-input"
121
- value={internalCurrentPage}
122
- className={`${classes["form-element"]} ${classes["current-page-input"]}`}
123
- />
124
- </Fragment>
125
- );
126
- }
127
-
128
- if (string.includes("%2")) {
129
- return (
130
- <div key={string}>
131
- <strong>{string.replace("%2", amountOfPages.toString())}</strong>&nbsp;
132
- </div>
133
- );
134
- }
135
-
136
- return <div key={string}>{string}&nbsp;</div>;
137
- });
138
- }
139
-
140
- return null;
141
- };
142
-
143
- const onPageSizeChangeHandler = (event: React.ChangeEvent<HTMLSelectElement>) => {
144
- const pageSizeNumber = Number(event.target.value) as PageSize;
145
- onPageSizeChange(pageSizeNumber);
146
- };
147
-
148
- const onPageChangeHandler = (pageToGoTo: number) => {
149
- onPageChange(pageToGoTo);
150
- };
151
-
152
- return (
153
- <div
154
- {...rest}
155
- ref={ref}
156
- className={`${classes["pagination-wrapper"]} ${className ? className : ""}`}
157
- >
158
- {totalElements && (
159
- <div className={classes["total"]}>
160
- <span tabIndex={0}>
161
- {translate.totalItems}: <span>{totalElements}</span>
162
- </span>
163
- </div>
164
- )}
165
- <div className={classes["pagination"]}>
166
- {pageSize && (
167
- <div className={classes["per-page"]}>
168
- <Label id="page-size-select-label">{translate.itemsPerPage}</Label>
169
- <Select
170
- labeledBy="page-size-select-label"
171
- className={`${classes["form-element"]} ${classes["page-size-select"]}`}
172
- value={pageSize.toString()}
173
- onChange={onPageSizeChangeHandler}
56
+ const PaginationComponent: ForwardRefRenderFunction<HTMLDivElement, Props> = (
57
+ {
58
+ totalElements,
59
+ pageSize = 10,
60
+ translate = DefaultTranslations,
61
+ currentPage,
62
+ className,
63
+ onPageChange,
64
+ onPageSizeChange,
65
+ ...rest
66
+ }: Props,
67
+ ref
68
+ ) => {
69
+ /** We use an internal state variable, because we don't want to fire onCurrentPageChange whenever onChange fires on the input. Rather, only when the Enter key is pressed. */
70
+ const [internalCurrentPage, setInternalCurrentPage] = useState(currentPage?.toString() || "1");
71
+ const calculateAmountOfPages = () => {
72
+ if (!totalElements) return 1;
73
+
74
+ if (Math.ceil(totalElements / pageSize) < 1) {
75
+ return 1;
76
+ }
77
+
78
+ return Math.ceil(totalElements / pageSize);
79
+ };
80
+
81
+ const onEnterListener = (event: React.KeyboardEvent<HTMLInputElement>) => {
82
+ if (event.code === "Enter") {
83
+ onPageChange(Number(internalCurrentPage));
84
+ }
85
+ };
86
+
87
+ const renderCurrentPageTranslation = () => {
88
+ const amountOfPages = calculateAmountOfPages();
89
+
90
+ if (amountOfPages) {
91
+ const splitCurrentPageTranslation = translate.currentPage.split(" ");
92
+
93
+ return splitCurrentPageTranslation.map(string => {
94
+ if (string.includes("%1")) {
95
+ return (
96
+ <Fragment key={string}>
97
+ <Label
98
+ id="current-value-input-label"
99
+ htmlFor="current-value-input"
100
+ className={`${readyclasses["sr-only"]} ${classes["current-value-input-label"]}`}
174
101
  >
175
- <Option value="10">10</Option>
176
- <Option value="25">25</Option>
177
- <Option value="50">50</Option>
178
- </Select>
102
+ {translate.currentPageLabel}
103
+ </Label>
104
+ <Input
105
+ aria-labelledby="current-value-input-label"
106
+ key="input"
107
+ id="current-value-input"
108
+ type="text"
109
+ size={currentPage?.toString().length}
110
+ max={calculateAmountOfPages()}
111
+ wrapperProps={{ className: classes["current-value-input"] }}
112
+ onKeyUp={onEnterListener}
113
+ onBlur={(event: React.ChangeEvent<HTMLInputElement>) =>
114
+ onPageChange(Number(event.target.value))
115
+ }
116
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
117
+ setInternalCurrentPage(e.target.value)
118
+ }
119
+ name="current-value-input"
120
+ value={internalCurrentPage}
121
+ className={`${classes["form-element"]} ${classes["current-page-input"]}`}
122
+ />
123
+ </Fragment>
124
+ );
125
+ }
126
+
127
+ if (string.includes("%2")) {
128
+ return (
129
+ <div key={string}>
130
+ <strong>{string.replace("%2", amountOfPages.toString())}</strong>&nbsp;
179
131
  </div>
180
- )}
181
- <Fragment>
182
- {!!((currentPage && currentPage > 2) || (currentPage && currentPage > 1)) && (
183
- <div className={classes["previous"]}>
184
- {currentPage > 2 && (
185
- <IconButton
186
- title="first"
187
- onClick={() => onPageChangeHandler(0)}
188
- data-paginate="first"
189
- >
190
- <Icon icon={Icons.NavigationFirst} />
191
- </IconButton>
192
- )}
193
- {currentPage > 1 && (
194
- <IconButton
195
- title="previous"
196
- onClick={() => onPageChangeHandler(currentPage - 1)}
197
- data-paginate="previous"
198
- >
199
- <Icon icon={Icons.ChevronLeft} />
200
- </IconButton>
201
- )}
202
- </div>
203
- )}
204
- {totalElements && !!calculateAmountOfPages() && (
205
- <div className={classes["page"]}>{renderCurrentPageTranslation()}</div>
206
- )}
207
- <div className={classes["next"]}>
208
- {!!(
209
- (currentPage !== undefined && currentPage < calculateAmountOfPages()!) ||
210
- (currentPage !== undefined && !totalElements)
211
- ) && (
132
+ );
133
+ }
134
+
135
+ return <div key={string}>{string}&nbsp;</div>;
136
+ });
137
+ }
138
+
139
+ return null;
140
+ };
141
+
142
+ const onPageSizeChangeHandler = (event: React.ChangeEvent<HTMLSelectElement>) => {
143
+ const pageSizeNumber = Number(event.target.value) as PageSize;
144
+ onPageSizeChange(pageSizeNumber);
145
+ };
146
+
147
+ const onPageChangeHandler = (pageToGoTo: number) => {
148
+ onPageChange(pageToGoTo);
149
+ };
150
+
151
+ return (
152
+ <div
153
+ {...rest}
154
+ ref={ref}
155
+ className={`${classes["pagination-wrapper"]} ${className ? className : ""}`}
156
+ >
157
+ {totalElements && (
158
+ <div className={classes["total"]}>
159
+ <span tabIndex={0}>
160
+ {translate.totalItems}: <span>{totalElements}</span>
161
+ </span>
162
+ </div>
163
+ )}
164
+ <div className={classes["pagination"]}>
165
+ {pageSize && (
166
+ <div className={classes["per-page"]}>
167
+ <Label id="page-size-select-label">{translate.itemsPerPage}</Label>
168
+ <Select
169
+ labeledBy="page-size-select-label"
170
+ className={`${classes["form-element"]} ${classes["page-size-select"]}`}
171
+ value={pageSize.toString()}
172
+ onChange={onPageSizeChangeHandler}
173
+ >
174
+ <Option value="10">10</Option>
175
+ <Option value="25">25</Option>
176
+ <Option value="50">50</Option>
177
+ </Select>
178
+ </div>
179
+ )}
180
+ <Fragment>
181
+ {!!((currentPage && currentPage > 2) || (currentPage && currentPage > 1)) && (
182
+ <div className={classes["previous"]}>
183
+ {currentPage > 2 && (
212
184
  <IconButton
213
- title="next"
214
- onClick={() => onPageChangeHandler(currentPage + 1)}
215
- data-paginate="next"
185
+ title="first"
186
+ onClick={() => onPageChangeHandler(0)}
187
+ data-paginate="first"
216
188
  >
217
- <Icon icon={Icons.ChevronRight} />
189
+ <Icon icon={Icons.NavigationFirst} />
218
190
  </IconButton>
219
191
  )}
220
- {!!(currentPage && totalElements && currentPage < calculateAmountOfPages()! - 1) && (
192
+ {currentPage > 1 && (
221
193
  <IconButton
222
- title="last"
223
- onClick={() => onPageChangeHandler(totalElements / pageSize)}
224
- data-paginate="last"
194
+ title="previous"
195
+ onClick={() => onPageChangeHandler(currentPage - 1)}
196
+ data-paginate="previous"
225
197
  >
226
- <Icon icon={Icons.NavigationLast} />
198
+ <Icon icon={Icons.ChevronLeft} />
227
199
  </IconButton>
228
200
  )}
229
201
  </div>
230
- </Fragment>
231
- </div>
202
+ )}
203
+ {totalElements && !!calculateAmountOfPages() && (
204
+ <div className={classes["page"]}>{renderCurrentPageTranslation()}</div>
205
+ )}
206
+ <div className={classes["next"]}>
207
+ {!!(
208
+ (currentPage !== undefined && currentPage < calculateAmountOfPages()!) ||
209
+ (currentPage !== undefined && !totalElements)
210
+ ) && (
211
+ <IconButton
212
+ title="next"
213
+ onClick={() => onPageChangeHandler(currentPage + 1)}
214
+ data-paginate="next"
215
+ >
216
+ <Icon icon={Icons.ChevronRight} />
217
+ </IconButton>
218
+ )}
219
+ {!!(currentPage && totalElements && currentPage < calculateAmountOfPages()! - 1) && (
220
+ <IconButton
221
+ title="last"
222
+ onClick={() => onPageChangeHandler(totalElements / pageSize)}
223
+ data-paginate="last"
224
+ >
225
+ <Icon icon={Icons.NavigationLast} />
226
+ </IconButton>
227
+ )}
228
+ </div>
229
+ </Fragment>
232
230
  </div>
233
- );
234
- }
235
- );
231
+ </div>
232
+ );
233
+ };
234
+
235
+ export const Pagination = React.forwardRef(PaginationComponent);
@@ -20,6 +20,7 @@
20
20
  $transition-property: transform, opacity;
21
21
 
22
22
  position: fixed;
23
+ z-index: 1;
23
24
  pointer-events: none;
24
25
  background-color: var(--light);
25
26
  border-radius: 8px;
@@ -32,7 +32,7 @@ const createPopover = (params?: (defaultParams: Props) => Props) => {
32
32
  <li>Test</li>
33
33
  </ul>
34
34
  ),
35
- show: false,
35
+ show: true,
36
36
  placement: { vertical: "top", horizontal: "left" }
37
37
  };
38
38
  let parameters: Props = defaultParams;
@@ -95,6 +95,9 @@ describe("Popover should render", () => {
95
95
  it("renders without crashing and has default left and top attributes", () => {
96
96
  const { popover } = createPopover();
97
97
 
98
+ window.dispatchEvent(new Event("resize"));
99
+ window.dispatchEvent(new Event("scroll"));
100
+
98
101
  expect(popover).toBeTruthy();
99
102
  });
100
103
  });
@@ -14,7 +14,14 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import React, { ComponentPropsWithRef, ReactNode, RefObject, useEffect, useRef } from "react";
17
+ import React, {
18
+ ForwardRefRenderFunction,
19
+ ComponentPropsWithRef,
20
+ ReactNode,
21
+ RefObject,
22
+ useEffect,
23
+ useRef
24
+ } from "react";
18
25
  import { Offset, Placement, usePosition } from "../hooks/usePosition";
19
26
  import classes from "./Popover.module.scss";
20
27
 
@@ -24,45 +31,80 @@ export interface Props extends ComponentPropsWithRef<"div"> {
24
31
  anchorEl?: RefObject<HTMLOrSVGElement>; //eslint-disable-line no-undef
25
32
  placement?: Placement;
26
33
  offset?: Offset;
34
+ debounceAmount?: number;
27
35
  transformOrigin?: Placement;
36
+ onAnchorOutOfView?: () => void;
28
37
  }
29
38
 
30
- export const Popover = React.forwardRef<HTMLDivElement, Props>(
31
- ({ children, className, show, placement, offset, transformOrigin, anchorEl, ...rest }, ref) => {
32
- const elToBePositioned = useRef<HTMLDivElement>(null);
39
+ const PopoverComponent: ForwardRefRenderFunction<HTMLDivElement, Props> = (
40
+ {
41
+ children,
42
+ className,
43
+ show,
44
+ placement,
45
+ offset,
46
+ debounceAmount,
47
+ transformOrigin,
48
+ anchorEl,
49
+ onAnchorOutOfView,
50
+ ...rest
51
+ },
52
+ ref
53
+ ) => {
54
+ const elToBePositioned = useRef<HTMLDivElement>(null);
33
55
 
34
- if (show === undefined) {
35
- throw new Error('Please make sure to define the "show" property on your Popover component');
56
+ const { top, left, right, bottom, calculatePosition } = usePosition({
57
+ elementToBePositioned: elToBePositioned,
58
+ relativeElement: anchorEl,
59
+ offset: offset,
60
+ placement: placement,
61
+ transformOrigin: transformOrigin,
62
+ debounceAmount: debounceAmount || undefined
63
+ });
64
+
65
+ useEffect(() => {
66
+ if (!show) {
67
+ return;
36
68
  }
37
69
 
38
- const { top, left, right, bottom, calculatePosition } = usePosition({
39
- elementToBePositioned: elToBePositioned,
40
- relativeElement: anchorEl,
41
- offset: offset,
42
- placement: placement,
43
- transformOrigin: transformOrigin
44
- });
70
+ window.addEventListener("resize", calculatePosition);
71
+ window.addEventListener("scroll", calculatePosition);
72
+
73
+ return () => {
74
+ window.removeEventListener("resize", calculatePosition);
75
+ window.removeEventListener("scroll", calculatePosition);
76
+ };
77
+ }, [show]);
45
78
 
46
- useEffect(() => {
47
- window.addEventListener("resize", calculatePosition);
79
+ useEffect(() => {
80
+ calculatePosition();
81
+ }, [show]);
48
82
 
49
- return () => window.removeEventListener("resize", calculatePosition);
50
- }, []);
83
+ useEffect(() => {
84
+ const isAnchorOffscreen =
85
+ show &&
86
+ (top === 0 ||
87
+ left === 0 ||
88
+ right === 0 ||
89
+ bottom === 0 ||
90
+ window.innerHeight - (elToBePositioned.current as HTMLElement).offsetHeight === top);
51
91
 
52
- useEffect(() => {
53
- calculatePosition();
54
- }, [show]);
92
+ if (isAnchorOffscreen) {
93
+ onAnchorOutOfView && onAnchorOutOfView();
94
+ }
95
+ }, [top, left, right, bottom]);
55
96
 
56
- return (
57
- <div ref={ref} {...rest}>
58
- <div
59
- ref={elToBePositioned}
60
- className={`${classes.popover} ${className ?? ""} ${show ? classes.show : ""}`}
61
- style={{ top: top, left: left, right: right, bottom: bottom }}
62
- >
63
- {children}
64
- </div>
97
+ return (
98
+ <div ref={ref} {...rest}>
99
+ <div
100
+ ref={elToBePositioned}
101
+ className={`${classes.popover} ${className ?? ""} ${show ? classes.show : ""}`}
102
+ style={{ top: top, left: left, right: right, bottom: bottom }}
103
+ >
104
+ {children}
65
105
  </div>
66
- );
67
- }
68
- );
106
+ </div>
107
+ );
108
+ };
109
+
110
+ export const Popover = React.forwardRef(PopoverComponent);