@onewelcome/react-lib-components 0.1.1-alpha → 0.1.2-alpha
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/README.md +16 -1
- package/dist/Breadcrumbs/Breadcrumbs.d.ts +3 -3
- package/dist/Button/BaseButton.d.ts +3 -4
- package/dist/Button/Button.d.ts +3 -4
- package/dist/Button/IconButton.d.ts +3 -4
- package/dist/ContextMenu/ContextMenu.d.ts +3 -3
- package/dist/Form/Checkbox/Checkbox.d.ts +5 -5
- package/dist/Form/Fieldset/Fieldset.d.ts +4 -4
- package/dist/Form/FormControl/FormControl.d.ts +5 -5
- package/dist/Form/FormGroup/FormGroup.d.ts +4 -4
- package/dist/Form/FormHelperText/FormHelperText.d.ts +4 -5
- package/dist/Form/FormSelectorWrapper/FormSelectorWrapper.d.ts +8 -12
- package/dist/Form/Input/Input.d.ts +7 -6
- package/dist/Form/Label/Label.d.ts +4 -5
- package/dist/Form/Radio/Radio.d.ts +5 -5
- package/dist/Form/Select/Option.d.ts +3 -4
- package/dist/Form/Select/Select.d.ts +4 -4
- package/dist/Form/Textarea/Textarea.d.ts +9 -5
- package/dist/Form/Toggle/Toggle.d.ts +3 -3
- package/dist/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.d.ts +4 -3
- package/dist/Form/Wrapper/InputWrapper/InputWrapper.d.ts +5 -5
- package/dist/Form/Wrapper/RadioWrapper/RadioWrapper.d.ts +4 -4
- package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +7 -4
- package/dist/Form/Wrapper/TextareaWrapper/TextareaWrapper.d.ts +3 -3
- package/dist/Form/Wrapper/Wrapper/Wrapper.d.ts +6 -6
- package/dist/Form/form.interfaces.d.ts +4 -3
- package/dist/Icon/Icon.d.ts +4 -4
- package/dist/Link/Link.d.ts +3 -5
- package/dist/Notifications/BaseModal/BaseModal.d.ts +3 -4
- package/dist/Notifications/BaseModal/BaseModalActions/BaseModalActions.d.ts +3 -3
- package/dist/Notifications/BaseModal/BaseModalContent/BaseModalContent.d.ts +3 -3
- package/dist/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +3 -3
- package/dist/Notifications/Dialog/Dialog.d.ts +3 -3
- package/dist/Notifications/Dialog/DialogActions/DialogActions.d.ts +3 -3
- package/dist/Notifications/Dialog/DialogTitle/DialogTitle.d.ts +3 -3
- package/dist/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.d.ts +5 -4
- package/dist/Notifications/DiscardChangesModal/DiscardChangesModal.d.ts +3 -1
- package/dist/Pagination/Pagination.d.ts +19 -0
- package/dist/Popover/Popover.d.ts +3 -3
- package/dist/Tabs/Tab.d.ts +11 -0
- package/dist/Tabs/TabButton.d.ts +10 -0
- package/dist/Tabs/TabPanel.d.ts +8 -0
- package/dist/Tabs/Tabs.d.ts +9 -0
- package/dist/TextEllipsis/TextEllipsis.d.ts +6 -0
- package/dist/Tiles/Tile.d.ts +3 -3
- package/dist/Tiles/Tiles.d.ts +3 -3
- package/dist/Tooltip/Tooltip.d.ts +3 -3
- package/dist/Typography/Typography.d.ts +6 -4
- package/dist/Wizard/BaseWizardSteps/BaseWizardSteps.d.ts +3 -3
- package/dist/Wizard/WizardSteps/WizardSteps.d.ts +3 -3
- package/dist/_BaseStyling_/BaseStyling.d.ts +9 -0
- package/dist/hooks/useRepeater.d.ts +10 -0
- package/dist/hooks/useSpacing.d.ts +2 -2
- package/dist/hooks/useWrapper.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/interfaces.d.ts +2 -11
- package/dist/react-lib-components.cjs.development.js +2023 -1551
- package/dist/react-lib-components.cjs.development.js.map +1 -1
- package/dist/react-lib-components.cjs.production.min.js +1 -1
- package/dist/react-lib-components.cjs.production.min.js.map +1 -1
- package/dist/react-lib-components.esm.js +2021 -1553
- package/dist/react-lib-components.esm.js.map +1 -1
- package/dist/util/helper.d.ts +6 -1
- package/package.json +30 -24
- package/src/Breadcrumbs/Breadcrumbs.tsx +39 -37
- package/src/Button/BaseButton.test.tsx +65 -19
- package/src/Button/BaseButton.tsx +2 -3
- package/src/Button/Button.test.tsx +63 -17
- package/src/Button/Button.tsx +15 -4
- package/src/Button/IconButton.test.tsx +57 -22
- package/src/Button/IconButton.tsx +14 -9
- package/src/ContextMenu/ContextMenu.test.tsx +27 -1
- package/src/ContextMenu/ContextMenu.tsx +70 -65
- package/src/Form/Checkbox/Checkbox.test.tsx +28 -2
- package/src/Form/Checkbox/Checkbox.tsx +132 -122
- package/src/Form/Fieldset/Fieldset.test.tsx +28 -2
- package/src/Form/Fieldset/Fieldset.tsx +96 -50
- package/src/Form/FormControl/FormControl.test.tsx +27 -1
- package/src/Form/FormControl/FormControl.tsx +36 -39
- package/src/Form/FormGroup/FormGroup.test.tsx +27 -1
- package/src/Form/FormGroup/FormGroup.tsx +64 -58
- package/src/Form/FormHelperText/FormHelperText.test.tsx +27 -1
- package/src/Form/FormHelperText/FormHelperText.tsx +20 -16
- package/src/Form/FormSelectorWrapper/FormSelectorWrapper.test.tsx +78 -0
- package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +60 -55
- package/src/Form/Input/Input.module.scss +33 -15
- package/src/Form/Input/Input.test.tsx +27 -1
- package/src/Form/Input/Input.tsx +88 -47
- package/src/Form/Label/Label.test.tsx +27 -1
- package/src/Form/Label/Label.tsx +18 -14
- package/src/Form/Radio/Radio.test.tsx +28 -2
- package/src/Form/Radio/Radio.tsx +98 -90
- package/src/Form/Select/Option.test.tsx +27 -1
- package/src/Form/Select/Option.tsx +49 -42
- package/src/Form/Select/Select.module.scss +5 -1
- package/src/Form/Select/Select.test.tsx +224 -30
- package/src/Form/Select/Select.tsx +248 -182
- package/src/Form/Textarea/Textarea.module.scss +2 -1
- package/src/Form/Textarea/Textarea.test.tsx +28 -2
- package/src/Form/Textarea/Textarea.tsx +44 -29
- package/src/Form/Toggle/Toggle.module.scss +9 -0
- package/src/Form/Toggle/Toggle.test.tsx +27 -1
- package/src/Form/Toggle/Toggle.tsx +25 -12
- package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +27 -1
- package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +45 -48
- package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +11 -0
- package/src/Form/Wrapper/InputWrapper/InputWrapper.test.tsx +89 -1
- package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +127 -74
- package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +64 -59
- package/src/Form/Wrapper/SelectWrapper/SelectWrapper.module.scss +1 -1
- package/src/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +43 -1
- package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +54 -44
- package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +3 -5
- package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.test.tsx +43 -1
- package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +100 -85
- package/src/Form/Wrapper/Wrapper/Wrapper.test.tsx +27 -1
- package/src/Form/Wrapper/Wrapper/Wrapper.tsx +76 -71
- package/src/Form/form.interfaces.ts +4 -3
- package/src/Icon/Icon.module.scss +4 -0
- package/src/Icon/Icon.test.tsx +30 -2
- package/src/Icon/Icon.tsx +5 -5
- package/src/Link/Link.test.tsx +27 -1
- package/src/Link/Link.tsx +4 -6
- package/src/Notifications/BaseModal/BaseModal.test.tsx +27 -1
- package/src/Notifications/BaseModal/BaseModal.tsx +59 -54
- package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.test.tsx +26 -1
- package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.tsx +11 -9
- package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.test.tsx +27 -1
- package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.tsx +27 -26
- package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.test.tsx +29 -1
- package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.tsx +18 -16
- package/src/Notifications/Dialog/Dialog.test.tsx +39 -1
- package/src/Notifications/Dialog/Dialog.tsx +84 -78
- package/src/Notifications/Dialog/DialogActions/DialogActions.test.tsx +27 -1
- package/src/Notifications/Dialog/DialogActions/DialogActions.tsx +15 -12
- package/src/Notifications/Dialog/DialogTitle/DialogTitle.test.tsx +28 -2
- package/src/Notifications/Dialog/DialogTitle/DialogTitle.tsx +13 -11
- package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.test.tsx +41 -1
- package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +43 -36
- package/src/Notifications/DiscardChangesModal/DiscardChangesModal.test.tsx +52 -1
- package/src/Notifications/DiscardChangesModal/DiscardChangesModal.tsx +8 -3
- package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.tsx +1 -1
- package/src/Pagination/Pagination.module.scss +120 -0
- package/src/Pagination/Pagination.test.tsx +176 -0
- package/src/Pagination/Pagination.tsx +205 -0
- package/src/Popover/Popover.tsx +3 -3
- package/src/Tabs/Tab.test.tsx +71 -0
- package/src/Tabs/Tab.tsx +17 -0
- package/src/Tabs/TabButton.module.scss +36 -0
- package/src/Tabs/TabButton.test.tsx +77 -0
- package/src/Tabs/TabButton.tsx +58 -0
- package/src/Tabs/TabPanel.module.scss +7 -0
- package/src/Tabs/TabPanel.test.tsx +76 -0
- package/src/Tabs/TabPanel.tsx +27 -0
- package/src/Tabs/Tabs.module.scss +41 -0
- package/src/Tabs/Tabs.test.tsx +268 -0
- package/src/Tabs/Tabs.tsx +149 -0
- package/src/TextEllipsis/TextEllipsis.module.scss +18 -0
- package/src/TextEllipsis/TextEllipsis.test.tsx +80 -0
- package/src/TextEllipsis/TextEllipsis.tsx +55 -0
- package/src/Tiles/Tile.test.tsx +27 -1
- package/src/Tiles/Tile.tsx +59 -62
- package/src/Tiles/Tiles.test.tsx +27 -1
- package/src/Tiles/Tiles.tsx +42 -39
- package/src/Tooltip/Tooltip.test.tsx +27 -1
- package/src/Tooltip/Tooltip.tsx +104 -92
- package/src/Typography/Typography.test.tsx +27 -1
- package/src/Typography/Typography.tsx +66 -68
- package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +67 -62
- package/src/Wizard/WizardSteps/WizardSteps.tsx +24 -21
- package/src/_BaseStyling_/BaseStyling.tsx +19 -1
- package/src/hooks/useRepeater.test.tsx +139 -0
- package/src/hooks/useRepeater.ts +34 -0
- package/src/hooks/useSpacing.ts +1 -1
- package/src/hooks/useWrapper.ts +7 -2
- package/src/index.ts +12 -1
- package/src/interfaces.ts +2 -12
- package/src/util/helper.test.tsx +38 -1
- package/src/util/helper.tsx +21 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { DiscardChangesModal, Props } from './DiscardChangesModal';
|
|
3
3
|
import { findByTestId, getAllByRole, render, waitFor } from '@testing-library/react';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
@@ -109,3 +109,54 @@ describe('DiscardChangesModal should show DiscardChangesDialog', () => {
|
|
|
109
109
|
expect(defaultParams.onClose).toBeCalled();
|
|
110
110
|
});
|
|
111
111
|
});
|
|
112
|
+
|
|
113
|
+
describe('ref should work', () => {
|
|
114
|
+
it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
|
|
115
|
+
const ExampleComponent = ({
|
|
116
|
+
propagateRef,
|
|
117
|
+
}: {
|
|
118
|
+
propagateRef?: (
|
|
119
|
+
ref1: React.RefObject<HTMLElement>,
|
|
120
|
+
ref2: React.RefObject<HTMLElement>
|
|
121
|
+
) => void;
|
|
122
|
+
}) => {
|
|
123
|
+
const modalRef = useRef(null);
|
|
124
|
+
const dialogRef = useRef(null);
|
|
125
|
+
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (dialogRef.current && modalRef.current) {
|
|
128
|
+
propagateRef && propagateRef(modalRef, dialogRef);
|
|
129
|
+
}
|
|
130
|
+
}, [modalRef, dialogRef]);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<DiscardChangesModal
|
|
134
|
+
modalRef={modalRef}
|
|
135
|
+
dialogRef={dialogRef}
|
|
136
|
+
data-ref="testing"
|
|
137
|
+
hasUnsavedChanges={jest.fn()}
|
|
138
|
+
onClose={jest.fn()}
|
|
139
|
+
headerProps={{ title: 'test' }}
|
|
140
|
+
open={false}
|
|
141
|
+
children="test"
|
|
142
|
+
discardChangedDialogProps={{
|
|
143
|
+
contentLabel: 'test',
|
|
144
|
+
discardChangesButtonLabel: 'test',
|
|
145
|
+
keepEditingButtonLabel: 'test',
|
|
146
|
+
titleLabel: 'test',
|
|
147
|
+
'data-ref': 'testing',
|
|
148
|
+
}}
|
|
149
|
+
title="test"
|
|
150
|
+
id="test"
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const refCheck = (ref1: React.RefObject<HTMLElement>, ref2: React.RefObject<HTMLElement>) => {
|
|
156
|
+
expect(ref1.current).toHaveAttribute('data-ref', 'testing');
|
|
157
|
+
expect(ref2.current).toHaveAttribute('data-ref', 'testing');
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
render(<ExampleComponent propagateRef={refCheck} />);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -14,15 +14,19 @@ export interface Props extends Omit<ModalProps, 'onClose'> {
|
|
|
14
14
|
DiscardChangesDialogProps,
|
|
15
15
|
'open' | 'onKeepEditing' | 'onDiscardChanges'
|
|
16
16
|
>;
|
|
17
|
+
modalRef?: React.RefObject<HTMLDivElement>;
|
|
18
|
+
dialogRef?: React.RefObject<HTMLDivElement>;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export const DiscardChangesModal = ({
|
|
20
22
|
id,
|
|
21
|
-
onClose,
|
|
22
23
|
children,
|
|
23
|
-
hasUnsavedChanges,
|
|
24
24
|
headerProps,
|
|
25
25
|
discardChangedDialogProps,
|
|
26
|
+
modalRef,
|
|
27
|
+
dialogRef,
|
|
28
|
+
onClose,
|
|
29
|
+
hasUnsavedChanges,
|
|
26
30
|
...rest
|
|
27
31
|
}: Props) => {
|
|
28
32
|
const [openDiscardChangesDialog, setOpenDiscardChangesDialog] = useState(false);
|
|
@@ -41,12 +45,13 @@ export const DiscardChangesModal = ({
|
|
|
41
45
|
|
|
42
46
|
return (
|
|
43
47
|
<Fragment>
|
|
44
|
-
<Modal id={id} onClose={onCloseWrapper} {...rest}>
|
|
48
|
+
<Modal id={id} ref={modalRef} onClose={onCloseWrapper} {...rest}>
|
|
45
49
|
<ModalHeader {...headerProps} id={`${id}-label`} onClose={onCloseWrapper} />
|
|
46
50
|
{children}
|
|
47
51
|
</Modal>
|
|
48
52
|
<DiscardChangesDialog
|
|
49
53
|
{...discardChangedDialogProps}
|
|
54
|
+
ref={dialogRef}
|
|
50
55
|
open={openDiscardChangesDialog}
|
|
51
56
|
onKeepEditing={onDialogKeepEditing}
|
|
52
57
|
onDiscardChanges={onDialogDiscardChanges}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
.pagination-wrapper {
|
|
2
|
+
font-family: var(--font-family);
|
|
3
|
+
font-size: var(--font-size);
|
|
4
|
+
color: var(--greyed-out);
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.form-element {
|
|
10
|
+
height: 2.5rem;
|
|
11
|
+
|
|
12
|
+
button {
|
|
13
|
+
min-height: 2.5rem;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.total {
|
|
18
|
+
flex: 1 0 auto;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
|
|
23
|
+
span > span {
|
|
24
|
+
font-weight: 700;
|
|
25
|
+
font-family: var(--font-family);
|
|
26
|
+
font-size: var(--font-size);
|
|
27
|
+
margin-left: 0.25rem;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.pagination {
|
|
32
|
+
flex: 1 0 auto;
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
margin-top: 1rem;
|
|
37
|
+
|
|
38
|
+
.previous,
|
|
39
|
+
.next {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
|
|
43
|
+
button {
|
|
44
|
+
margin: 0.25rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
button span:before {
|
|
48
|
+
font-size: 0.75rem;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.per-page {
|
|
54
|
+
display: none;
|
|
55
|
+
align-items: center;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.page {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
flex-basis: min-content;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.page-size-select {
|
|
65
|
+
border-color: #fff;
|
|
66
|
+
margin: 0 0.25rem;
|
|
67
|
+
|
|
68
|
+
button {
|
|
69
|
+
min-width: 55px;
|
|
70
|
+
padding: 0;
|
|
71
|
+
|
|
72
|
+
div[data-display] {
|
|
73
|
+
left: 0.5rem;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
div:not([data-display]) {
|
|
77
|
+
right: 0.5rem;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.current-value-input {
|
|
83
|
+
margin: 0 0.25rem 0 0;
|
|
84
|
+
padding: 0;
|
|
85
|
+
|
|
86
|
+
input {
|
|
87
|
+
border-radius: var(--input-border-radius);
|
|
88
|
+
padding: 0 1rem;
|
|
89
|
+
width: auto;
|
|
90
|
+
text-align: center;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@media screen and (min-width: 30em) {
|
|
95
|
+
.pagination-wrapper {
|
|
96
|
+
flex-direction: row;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.total {
|
|
100
|
+
justify-content: flex-start;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.pagination {
|
|
104
|
+
margin-top: 0;
|
|
105
|
+
justify-content: flex-end;
|
|
106
|
+
|
|
107
|
+
.previous,
|
|
108
|
+
.next {
|
|
109
|
+
button {
|
|
110
|
+
margin: 0.5rem;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@media screen and (min-width: 48em) {
|
|
117
|
+
.per-page {
|
|
118
|
+
display: flex;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Pagination, Props } from './Pagination';
|
|
3
|
+
import { render, waitFor } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
|
|
6
|
+
const defaultParams: Props = {
|
|
7
|
+
currentPage: 1,
|
|
8
|
+
totalElements: 500,
|
|
9
|
+
onPageChange: jest.fn(),
|
|
10
|
+
onPageSizeChange: jest.fn(),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const createPagination = (params?: (defaultParams: Props) => Props) => {
|
|
14
|
+
let parameters: Props = defaultParams;
|
|
15
|
+
|
|
16
|
+
if (params) {
|
|
17
|
+
parameters = params(defaultParams);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const queries = render(<Pagination data-testid="pagination" {...parameters} />);
|
|
21
|
+
|
|
22
|
+
const pagination = queries.getByTestId('pagination');
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
...queries,
|
|
26
|
+
pagination,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe('Pagination should render', () => {
|
|
31
|
+
it('renders without crashing', () => {
|
|
32
|
+
const { pagination } = createPagination();
|
|
33
|
+
|
|
34
|
+
expect(pagination).toBeTruthy();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('Pagination events', () => {
|
|
39
|
+
it('should give us the correct values', async () => {
|
|
40
|
+
const onPageChange = jest.fn();
|
|
41
|
+
|
|
42
|
+
const onPageSizeChange = jest.fn();
|
|
43
|
+
|
|
44
|
+
const { pagination } = createPagination((defaultParams) => ({
|
|
45
|
+
...defaultParams,
|
|
46
|
+
currentPage: 10,
|
|
47
|
+
onPageChange: onPageChange,
|
|
48
|
+
onPageSizeChange: onPageSizeChange,
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
const next = pagination.querySelector('[data-paginate="next"]')!;
|
|
52
|
+
const previous = pagination.querySelector('[data-paginate="previous"]')!;
|
|
53
|
+
const first = pagination.querySelector('[data-paginate="first"]')!;
|
|
54
|
+
const last = pagination.querySelector('[data-paginate="last"]')!;
|
|
55
|
+
const pageSizeSelect = pagination.querySelector('.page-size-select')!;
|
|
56
|
+
const currentPageInput = pagination.querySelector('#current-value-input')!;
|
|
57
|
+
|
|
58
|
+
userEvent.click(next);
|
|
59
|
+
|
|
60
|
+
await waitFor(() => expect(onPageChange).toHaveBeenCalledWith(11));
|
|
61
|
+
|
|
62
|
+
userEvent.click(previous);
|
|
63
|
+
await waitFor(() => expect(onPageChange).toHaveBeenCalledWith(9));
|
|
64
|
+
|
|
65
|
+
userEvent.click(first);
|
|
66
|
+
await waitFor(() => expect(onPageChange).toHaveBeenCalledWith(0));
|
|
67
|
+
|
|
68
|
+
userEvent.click(last);
|
|
69
|
+
await waitFor(() => expect(onPageChange).toHaveBeenCalledWith(50));
|
|
70
|
+
|
|
71
|
+
userEvent.click(pageSizeSelect);
|
|
72
|
+
|
|
73
|
+
const option25 = pageSizeSelect.querySelector('[data-value="25"]')!;
|
|
74
|
+
|
|
75
|
+
userEvent.click(option25);
|
|
76
|
+
|
|
77
|
+
await waitFor(() => expect(onPageSizeChange).toHaveBeenCalledWith(25));
|
|
78
|
+
|
|
79
|
+
(currentPageInput as HTMLInputElement).focus();
|
|
80
|
+
|
|
81
|
+
userEvent.keyboard('{backspace}{backspace}30{enter}');
|
|
82
|
+
|
|
83
|
+
await waitFor(() => expect(onPageChange).toHaveBeenCalledWith(30));
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('different current pages and their effect on what renders', () => {
|
|
88
|
+
it('is on the first page and does not render previous and first', () => {
|
|
89
|
+
const { pagination } = createPagination();
|
|
90
|
+
|
|
91
|
+
expect(pagination.querySelector('.next')).toBeTruthy();
|
|
92
|
+
expect(pagination.querySelector('.previous')).toBeFalsy();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('is on the second page and does not render first', () => {
|
|
96
|
+
const { pagination } = createPagination((defaultParams) => ({
|
|
97
|
+
...defaultParams,
|
|
98
|
+
currentPage: 2,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
expect(pagination.querySelector('[data-paginate="first"]')).toBeFalsy();
|
|
102
|
+
expect((pagination.querySelector('.current-value-input input') as HTMLInputElement).value).toBe(
|
|
103
|
+
'2'
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('is on the second to last page and does not render last', () => {
|
|
108
|
+
const { pagination } = createPagination((defaultParams) => ({
|
|
109
|
+
...defaultParams,
|
|
110
|
+
currentPage: 499,
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
expect(pagination.querySelector('[data-paginate="last"]')).toBeFalsy();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('is on the last page and does not render next & last', () => {
|
|
117
|
+
const { pagination } = createPagination((defaultParams) => ({
|
|
118
|
+
...defaultParams,
|
|
119
|
+
currentPage: 500,
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
expect(pagination.querySelector('[data-paginate="last"]')).toBeFalsy();
|
|
123
|
+
expect(pagination.querySelector('[data-paginate="next"]')).toBeFalsy();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('omitted attributes still renders correctly', () => {
|
|
128
|
+
it("still renders next if totalItems prop isn't given and we're on the first page", () => {
|
|
129
|
+
const { pagination } = createPagination((defaultParams) => ({
|
|
130
|
+
...defaultParams,
|
|
131
|
+
totalElements: undefined,
|
|
132
|
+
}));
|
|
133
|
+
|
|
134
|
+
expect(pagination.querySelector('.page')).toBeFalsy();
|
|
135
|
+
expect(pagination.querySelector('.next')).toBeTruthy();
|
|
136
|
+
expect(pagination.querySelector('.previous')).toBeFalsy();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("still renders next if totalItems prop isn't given and we're on the first page", () => {
|
|
140
|
+
const { pagination } = createPagination((defaultParams) => ({
|
|
141
|
+
...defaultParams,
|
|
142
|
+
currentPage: 4,
|
|
143
|
+
totalElements: undefined,
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
expect(pagination.querySelector('.page')).toBeFalsy();
|
|
147
|
+
expect(pagination.querySelector('.next')).toBeTruthy();
|
|
148
|
+
expect(pagination.querySelector('.previous')).toBeTruthy();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('ref should work', () => {
|
|
153
|
+
it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
|
|
154
|
+
const ExampleComponent = ({
|
|
155
|
+
propagateRef,
|
|
156
|
+
}: {
|
|
157
|
+
propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
|
|
158
|
+
}) => {
|
|
159
|
+
const ref = useRef(null);
|
|
160
|
+
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
if (ref.current) {
|
|
163
|
+
propagateRef && propagateRef(ref);
|
|
164
|
+
}
|
|
165
|
+
}, [ref]);
|
|
166
|
+
|
|
167
|
+
return <Pagination {...defaultParams} data-ref="testing" ref={ref} />;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const refCheck = (ref: React.RefObject<HTMLElement>) => {
|
|
171
|
+
expect(ref.current).toHaveAttribute('data-ref', 'testing');
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
render(<ExampleComponent propagateRef={refCheck} />);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import React, { ComponentPropsWithRef, Fragment, useState } from 'react';
|
|
2
|
+
import classes from './Pagination.module.scss';
|
|
3
|
+
import readyclasses from '../readyclasses.module.scss';
|
|
4
|
+
import { IconButton } from '../Button/IconButton';
|
|
5
|
+
import { Icons, Icon } from '../Icon/Icon';
|
|
6
|
+
import { Input } from '../Form/Input/Input';
|
|
7
|
+
import { Select } from '../Form/Select/Select';
|
|
8
|
+
import { Option } from '../Form/Select/Option';
|
|
9
|
+
import { Label } from '../Form/Label/Label';
|
|
10
|
+
|
|
11
|
+
export type PaginationTranslations = {
|
|
12
|
+
totalItems: string;
|
|
13
|
+
itemsPerPage: string;
|
|
14
|
+
itemsPerPageLabel: string;
|
|
15
|
+
currentPage: string;
|
|
16
|
+
currentPageLabel: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type PageChangeLabels = 'next' | 'previous' | 'first' | 'last';
|
|
20
|
+
|
|
21
|
+
enum DefaultTranslations {
|
|
22
|
+
totalItems = 'Total items',
|
|
23
|
+
itemsPerPage = 'Items per page',
|
|
24
|
+
currentPage = 'Page %1 of %2',
|
|
25
|
+
itemsPerPageLabel = 'Select how many items per page you want to see.',
|
|
26
|
+
currentPageLabel = 'What page you are currently on.',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type PageSize = 10 | 25 | 50;
|
|
30
|
+
|
|
31
|
+
export interface Props extends Omit<ComponentPropsWithRef<'div'>, 'translate'> {
|
|
32
|
+
currentPage?: number;
|
|
33
|
+
totalElements?: number;
|
|
34
|
+
pageSize?: PageSize;
|
|
35
|
+
translate?: PaginationTranslations;
|
|
36
|
+
onPageChange: (pageToGoTo: number) => void;
|
|
37
|
+
onPageSizeChange: (pageSize: PageSize) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Pagination = React.forwardRef<HTMLDivElement, Props>(
|
|
41
|
+
(
|
|
42
|
+
{
|
|
43
|
+
totalElements,
|
|
44
|
+
pageSize = 10,
|
|
45
|
+
translate = DefaultTranslations,
|
|
46
|
+
currentPage,
|
|
47
|
+
className,
|
|
48
|
+
onPageChange,
|
|
49
|
+
onPageSizeChange,
|
|
50
|
+
...rest
|
|
51
|
+
}: Props,
|
|
52
|
+
ref
|
|
53
|
+
) => {
|
|
54
|
+
/** 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. */
|
|
55
|
+
const [internalCurrentPage, setInternalCurrentPage] = useState(currentPage?.toString() || '1');
|
|
56
|
+
const calculateAmountOfPages = () => (totalElements ? Math.ceil(totalElements / pageSize) : 0);
|
|
57
|
+
|
|
58
|
+
const onEnterListener = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
59
|
+
if (event.code === 'Enter') {
|
|
60
|
+
onPageChange(Number(internalCurrentPage));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const renderCurrentPageTranslation = () => {
|
|
65
|
+
const amountOfPages = calculateAmountOfPages();
|
|
66
|
+
|
|
67
|
+
if (amountOfPages) {
|
|
68
|
+
const splitCurrentPageTranslation = translate.currentPage.split(' ');
|
|
69
|
+
|
|
70
|
+
return splitCurrentPageTranslation.map((string) => {
|
|
71
|
+
if (string.includes('%1')) {
|
|
72
|
+
return (
|
|
73
|
+
<Fragment key={string}>
|
|
74
|
+
<Label
|
|
75
|
+
id="current-value-input-label"
|
|
76
|
+
htmlFor="current-value-input"
|
|
77
|
+
className={readyclasses['sr-only']}
|
|
78
|
+
>
|
|
79
|
+
{translate.currentPageLabel}
|
|
80
|
+
</Label>
|
|
81
|
+
<Input
|
|
82
|
+
aria-labelledby="current-value-input-label"
|
|
83
|
+
key="input"
|
|
84
|
+
id="current-value-input"
|
|
85
|
+
type="text"
|
|
86
|
+
size={currentPage?.toString().length}
|
|
87
|
+
max={calculateAmountOfPages()}
|
|
88
|
+
wrapperProps={{ className: classes['current-value-input'] }}
|
|
89
|
+
onKeyUp={onEnterListener}
|
|
90
|
+
onBlur={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
91
|
+
onPageChange(Number(event.target.value))
|
|
92
|
+
}
|
|
93
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
94
|
+
setInternalCurrentPage(e.target.value)
|
|
95
|
+
}
|
|
96
|
+
name="current-value-input"
|
|
97
|
+
value={internalCurrentPage}
|
|
98
|
+
className={`${classes['form-element']} ${classes['current-page-input']}`}
|
|
99
|
+
/>
|
|
100
|
+
</Fragment>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (string.includes('%2')) {
|
|
105
|
+
return <div key={string}>{string.replace('%2', amountOfPages.toString())} </div>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return <div key={string}>{string} </div>;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return null;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const onPageSizeChangeHandler = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
116
|
+
const pageSizeNumber = Number(event.target.value) as PageSize;
|
|
117
|
+
onPageSizeChange(pageSizeNumber);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const onPageChangeHandler = (pageToGoTo: number) => {
|
|
121
|
+
onPageChange(pageToGoTo);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div
|
|
126
|
+
{...rest}
|
|
127
|
+
ref={ref}
|
|
128
|
+
className={`${classes['pagination-wrapper']} ${className ? className : ''}`}
|
|
129
|
+
>
|
|
130
|
+
{totalElements && (
|
|
131
|
+
<div className={classes['total']}>
|
|
132
|
+
<span tabIndex={0}>
|
|
133
|
+
{translate.totalItems}: <span>{totalElements}</span>
|
|
134
|
+
</span>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
<div className={classes['pagination']}>
|
|
138
|
+
{totalElements && pageSize && (
|
|
139
|
+
<div className={classes['per-page']}>
|
|
140
|
+
<Label id="page-size-select-label">{translate.itemsPerPage}</Label>
|
|
141
|
+
<Select
|
|
142
|
+
labeledBy="page-size-select-label"
|
|
143
|
+
className={`${classes['form-element']} ${classes['page-size-select']}`}
|
|
144
|
+
value={pageSize.toString()}
|
|
145
|
+
onChange={onPageSizeChangeHandler}
|
|
146
|
+
>
|
|
147
|
+
<Option value="10">10</Option>
|
|
148
|
+
<Option value="25">25</Option>
|
|
149
|
+
<Option value="50">50</Option>
|
|
150
|
+
</Select>
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
<Fragment>
|
|
154
|
+
{((currentPage && currentPage > 2) || (currentPage && currentPage > 1)) && (
|
|
155
|
+
<div className={classes['previous']}>
|
|
156
|
+
{currentPage > 2 && (
|
|
157
|
+
<IconButton
|
|
158
|
+
title="first"
|
|
159
|
+
onClick={() => onPageChangeHandler(0)}
|
|
160
|
+
data-paginate="first"
|
|
161
|
+
>
|
|
162
|
+
<Icon icon={Icons.NavigationFirst} />
|
|
163
|
+
</IconButton>
|
|
164
|
+
)}
|
|
165
|
+
{currentPage > 1 && (
|
|
166
|
+
<IconButton
|
|
167
|
+
title="previous"
|
|
168
|
+
onClick={() => onPageChangeHandler(currentPage - 1)}
|
|
169
|
+
data-paginate="previous"
|
|
170
|
+
>
|
|
171
|
+
<Icon icon={Icons.ChevronLeft} />
|
|
172
|
+
</IconButton>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
)}
|
|
176
|
+
{totalElements && (
|
|
177
|
+
<div className={classes['page']}>{renderCurrentPageTranslation()}</div>
|
|
178
|
+
)}
|
|
179
|
+
<div className={classes['next']}>
|
|
180
|
+
{((currentPage && currentPage < calculateAmountOfPages()) ||
|
|
181
|
+
(currentPage && !totalElements)) && (
|
|
182
|
+
<IconButton
|
|
183
|
+
title="next"
|
|
184
|
+
onClick={() => onPageChangeHandler(currentPage + 1)}
|
|
185
|
+
data-paginate="next"
|
|
186
|
+
>
|
|
187
|
+
<Icon icon={Icons.ChevronRight} />
|
|
188
|
+
</IconButton>
|
|
189
|
+
)}
|
|
190
|
+
{currentPage && totalElements && currentPage < calculateAmountOfPages()! - 1 && (
|
|
191
|
+
<IconButton
|
|
192
|
+
title="last"
|
|
193
|
+
onClick={() => onPageChangeHandler(totalElements / pageSize)}
|
|
194
|
+
data-paginate="last"
|
|
195
|
+
>
|
|
196
|
+
<Icon icon={Icons.NavigationLast} />
|
|
197
|
+
</IconButton>
|
|
198
|
+
)}
|
|
199
|
+
</div>
|
|
200
|
+
</Fragment>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
);
|
package/src/Popover/Popover.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { ComponentPropsWithRef, ReactNode, RefObject, useEffect, useRef } from 'react';
|
|
2
2
|
import { usePosition, Offset, Placement } from '../hooks/usePosition';
|
|
3
3
|
import classes from './Popover.module.scss';
|
|
4
4
|
|
|
5
|
-
export interface Props extends
|
|
5
|
+
export interface Props extends ComponentPropsWithRef<'div'> {
|
|
6
6
|
children?: ReactNode;
|
|
7
7
|
show?: boolean;
|
|
8
8
|
anchorEl?: RefObject<HTMLOrSVGElement>;
|
|
@@ -11,7 +11,7 @@ export interface Props extends HTMLProps<HTMLDivElement> {
|
|
|
11
11
|
transformOrigin?: Placement;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export const Popover = forwardRef<HTMLDivElement, Props>(
|
|
14
|
+
export const Popover = React.forwardRef<HTMLDivElement, Props>(
|
|
15
15
|
({ children, className, show, placement, offset, transformOrigin, anchorEl, ...rest }, ref) => {
|
|
16
16
|
const elToBePositioned = useRef<HTMLDivElement>(null);
|
|
17
17
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Tabs } from './Tabs';
|
|
3
|
+
import { Tab, Props } from './Tab';
|
|
4
|
+
import { render } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
const defaultParams: Props = {
|
|
7
|
+
title: 'Title of tab',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const createTab = (params?: (defaultParams: Props) => Props) => {
|
|
11
|
+
let parameters: Props = defaultParams;
|
|
12
|
+
if (params) {
|
|
13
|
+
parameters = params(defaultParams);
|
|
14
|
+
}
|
|
15
|
+
const queries = render(
|
|
16
|
+
<Tab {...parameters} data-testid="tab">
|
|
17
|
+
tab content
|
|
18
|
+
</Tab>
|
|
19
|
+
);
|
|
20
|
+
const tab = queries.getByTestId('tab');
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
...queries,
|
|
24
|
+
tab,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe('Tab should render', () => {
|
|
29
|
+
it('renders without crashing', () => {
|
|
30
|
+
const { tab } = createTab();
|
|
31
|
+
|
|
32
|
+
expect(tab).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('Tab useRef should work for panel and button', () => {
|
|
37
|
+
it('gives us back the proper refs', () => {
|
|
38
|
+
type logRefsFunction = (
|
|
39
|
+
buttonRef: React.RefObject<HTMLButtonElement>,
|
|
40
|
+
panelRef: React.RefObject<HTMLDivElement>
|
|
41
|
+
) => void;
|
|
42
|
+
const ExampleComponent = ({ logRefs }: { logRefs: logRefsFunction }) => {
|
|
43
|
+
const ref1 = useRef<HTMLButtonElement>(null);
|
|
44
|
+
const ref2 = useRef<HTMLDivElement>(null);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (ref1.current && ref2.current) {
|
|
48
|
+
logRefs(ref1, ref2);
|
|
49
|
+
}
|
|
50
|
+
}, [ref1, ref2]);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Tabs>
|
|
54
|
+
<Tab buttonRef={ref1} panelRef={ref2} title="ShouldBeButtonRef">
|
|
55
|
+
<span>Should be panel ref</span>
|
|
56
|
+
</Tab>
|
|
57
|
+
</Tabs>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const setCorrectText = (
|
|
62
|
+
buttonRef: React.RefObject<HTMLButtonElement>,
|
|
63
|
+
panelRef: React.RefObject<HTMLDivElement>
|
|
64
|
+
) => {
|
|
65
|
+
expect(buttonRef.current?.innerHTML).toContain('ShouldBeButtonRef');
|
|
66
|
+
expect(panelRef.current?.innerHTML).toEqual('<span>Should be panel ref</span>');
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
render(<ExampleComponent logRefs={setCorrectText} />);
|
|
70
|
+
});
|
|
71
|
+
});
|