@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.
Files changed (179) hide show
  1. package/README.md +16 -1
  2. package/dist/Breadcrumbs/Breadcrumbs.d.ts +3 -3
  3. package/dist/Button/BaseButton.d.ts +3 -4
  4. package/dist/Button/Button.d.ts +3 -4
  5. package/dist/Button/IconButton.d.ts +3 -4
  6. package/dist/ContextMenu/ContextMenu.d.ts +3 -3
  7. package/dist/Form/Checkbox/Checkbox.d.ts +5 -5
  8. package/dist/Form/Fieldset/Fieldset.d.ts +4 -4
  9. package/dist/Form/FormControl/FormControl.d.ts +5 -5
  10. package/dist/Form/FormGroup/FormGroup.d.ts +4 -4
  11. package/dist/Form/FormHelperText/FormHelperText.d.ts +4 -5
  12. package/dist/Form/FormSelectorWrapper/FormSelectorWrapper.d.ts +8 -12
  13. package/dist/Form/Input/Input.d.ts +7 -6
  14. package/dist/Form/Label/Label.d.ts +4 -5
  15. package/dist/Form/Radio/Radio.d.ts +5 -5
  16. package/dist/Form/Select/Option.d.ts +3 -4
  17. package/dist/Form/Select/Select.d.ts +4 -4
  18. package/dist/Form/Textarea/Textarea.d.ts +9 -5
  19. package/dist/Form/Toggle/Toggle.d.ts +3 -3
  20. package/dist/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.d.ts +4 -3
  21. package/dist/Form/Wrapper/InputWrapper/InputWrapper.d.ts +5 -5
  22. package/dist/Form/Wrapper/RadioWrapper/RadioWrapper.d.ts +4 -4
  23. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +7 -4
  24. package/dist/Form/Wrapper/TextareaWrapper/TextareaWrapper.d.ts +3 -3
  25. package/dist/Form/Wrapper/Wrapper/Wrapper.d.ts +6 -6
  26. package/dist/Form/form.interfaces.d.ts +4 -3
  27. package/dist/Icon/Icon.d.ts +4 -4
  28. package/dist/Link/Link.d.ts +3 -5
  29. package/dist/Notifications/BaseModal/BaseModal.d.ts +3 -4
  30. package/dist/Notifications/BaseModal/BaseModalActions/BaseModalActions.d.ts +3 -3
  31. package/dist/Notifications/BaseModal/BaseModalContent/BaseModalContent.d.ts +3 -3
  32. package/dist/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +3 -3
  33. package/dist/Notifications/Dialog/Dialog.d.ts +3 -3
  34. package/dist/Notifications/Dialog/DialogActions/DialogActions.d.ts +3 -3
  35. package/dist/Notifications/Dialog/DialogTitle/DialogTitle.d.ts +3 -3
  36. package/dist/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.d.ts +5 -4
  37. package/dist/Notifications/DiscardChangesModal/DiscardChangesModal.d.ts +3 -1
  38. package/dist/Pagination/Pagination.d.ts +19 -0
  39. package/dist/Popover/Popover.d.ts +3 -3
  40. package/dist/Tabs/Tab.d.ts +11 -0
  41. package/dist/Tabs/TabButton.d.ts +10 -0
  42. package/dist/Tabs/TabPanel.d.ts +8 -0
  43. package/dist/Tabs/Tabs.d.ts +9 -0
  44. package/dist/TextEllipsis/TextEllipsis.d.ts +6 -0
  45. package/dist/Tiles/Tile.d.ts +3 -3
  46. package/dist/Tiles/Tiles.d.ts +3 -3
  47. package/dist/Tooltip/Tooltip.d.ts +3 -3
  48. package/dist/Typography/Typography.d.ts +6 -4
  49. package/dist/Wizard/BaseWizardSteps/BaseWizardSteps.d.ts +3 -3
  50. package/dist/Wizard/WizardSteps/WizardSteps.d.ts +3 -3
  51. package/dist/_BaseStyling_/BaseStyling.d.ts +9 -0
  52. package/dist/hooks/useRepeater.d.ts +10 -0
  53. package/dist/hooks/useSpacing.d.ts +2 -2
  54. package/dist/hooks/useWrapper.d.ts +1 -1
  55. package/dist/index.d.ts +4 -0
  56. package/dist/interfaces.d.ts +2 -11
  57. package/dist/react-lib-components.cjs.development.js +2023 -1551
  58. package/dist/react-lib-components.cjs.development.js.map +1 -1
  59. package/dist/react-lib-components.cjs.production.min.js +1 -1
  60. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  61. package/dist/react-lib-components.esm.js +2021 -1553
  62. package/dist/react-lib-components.esm.js.map +1 -1
  63. package/dist/util/helper.d.ts +6 -1
  64. package/package.json +30 -24
  65. package/src/Breadcrumbs/Breadcrumbs.tsx +39 -37
  66. package/src/Button/BaseButton.test.tsx +65 -19
  67. package/src/Button/BaseButton.tsx +2 -3
  68. package/src/Button/Button.test.tsx +63 -17
  69. package/src/Button/Button.tsx +15 -4
  70. package/src/Button/IconButton.test.tsx +57 -22
  71. package/src/Button/IconButton.tsx +14 -9
  72. package/src/ContextMenu/ContextMenu.test.tsx +27 -1
  73. package/src/ContextMenu/ContextMenu.tsx +70 -65
  74. package/src/Form/Checkbox/Checkbox.test.tsx +28 -2
  75. package/src/Form/Checkbox/Checkbox.tsx +132 -122
  76. package/src/Form/Fieldset/Fieldset.test.tsx +28 -2
  77. package/src/Form/Fieldset/Fieldset.tsx +96 -50
  78. package/src/Form/FormControl/FormControl.test.tsx +27 -1
  79. package/src/Form/FormControl/FormControl.tsx +36 -39
  80. package/src/Form/FormGroup/FormGroup.test.tsx +27 -1
  81. package/src/Form/FormGroup/FormGroup.tsx +64 -58
  82. package/src/Form/FormHelperText/FormHelperText.test.tsx +27 -1
  83. package/src/Form/FormHelperText/FormHelperText.tsx +20 -16
  84. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.test.tsx +78 -0
  85. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +60 -55
  86. package/src/Form/Input/Input.module.scss +33 -15
  87. package/src/Form/Input/Input.test.tsx +27 -1
  88. package/src/Form/Input/Input.tsx +88 -47
  89. package/src/Form/Label/Label.test.tsx +27 -1
  90. package/src/Form/Label/Label.tsx +18 -14
  91. package/src/Form/Radio/Radio.test.tsx +28 -2
  92. package/src/Form/Radio/Radio.tsx +98 -90
  93. package/src/Form/Select/Option.test.tsx +27 -1
  94. package/src/Form/Select/Option.tsx +49 -42
  95. package/src/Form/Select/Select.module.scss +5 -1
  96. package/src/Form/Select/Select.test.tsx +224 -30
  97. package/src/Form/Select/Select.tsx +248 -182
  98. package/src/Form/Textarea/Textarea.module.scss +2 -1
  99. package/src/Form/Textarea/Textarea.test.tsx +28 -2
  100. package/src/Form/Textarea/Textarea.tsx +44 -29
  101. package/src/Form/Toggle/Toggle.module.scss +9 -0
  102. package/src/Form/Toggle/Toggle.test.tsx +27 -1
  103. package/src/Form/Toggle/Toggle.tsx +25 -12
  104. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +27 -1
  105. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +45 -48
  106. package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +11 -0
  107. package/src/Form/Wrapper/InputWrapper/InputWrapper.test.tsx +89 -1
  108. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +127 -74
  109. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +64 -59
  110. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.module.scss +1 -1
  111. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +43 -1
  112. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +54 -44
  113. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +3 -5
  114. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.test.tsx +43 -1
  115. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +100 -85
  116. package/src/Form/Wrapper/Wrapper/Wrapper.test.tsx +27 -1
  117. package/src/Form/Wrapper/Wrapper/Wrapper.tsx +76 -71
  118. package/src/Form/form.interfaces.ts +4 -3
  119. package/src/Icon/Icon.module.scss +4 -0
  120. package/src/Icon/Icon.test.tsx +30 -2
  121. package/src/Icon/Icon.tsx +5 -5
  122. package/src/Link/Link.test.tsx +27 -1
  123. package/src/Link/Link.tsx +4 -6
  124. package/src/Notifications/BaseModal/BaseModal.test.tsx +27 -1
  125. package/src/Notifications/BaseModal/BaseModal.tsx +59 -54
  126. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.test.tsx +26 -1
  127. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.tsx +11 -9
  128. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.test.tsx +27 -1
  129. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.tsx +27 -26
  130. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.test.tsx +29 -1
  131. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.tsx +18 -16
  132. package/src/Notifications/Dialog/Dialog.test.tsx +39 -1
  133. package/src/Notifications/Dialog/Dialog.tsx +84 -78
  134. package/src/Notifications/Dialog/DialogActions/DialogActions.test.tsx +27 -1
  135. package/src/Notifications/Dialog/DialogActions/DialogActions.tsx +15 -12
  136. package/src/Notifications/Dialog/DialogTitle/DialogTitle.test.tsx +28 -2
  137. package/src/Notifications/Dialog/DialogTitle/DialogTitle.tsx +13 -11
  138. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.test.tsx +41 -1
  139. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +43 -36
  140. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.test.tsx +52 -1
  141. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.tsx +8 -3
  142. package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.tsx +1 -1
  143. package/src/Pagination/Pagination.module.scss +120 -0
  144. package/src/Pagination/Pagination.test.tsx +176 -0
  145. package/src/Pagination/Pagination.tsx +205 -0
  146. package/src/Popover/Popover.tsx +3 -3
  147. package/src/Tabs/Tab.test.tsx +71 -0
  148. package/src/Tabs/Tab.tsx +17 -0
  149. package/src/Tabs/TabButton.module.scss +36 -0
  150. package/src/Tabs/TabButton.test.tsx +77 -0
  151. package/src/Tabs/TabButton.tsx +58 -0
  152. package/src/Tabs/TabPanel.module.scss +7 -0
  153. package/src/Tabs/TabPanel.test.tsx +76 -0
  154. package/src/Tabs/TabPanel.tsx +27 -0
  155. package/src/Tabs/Tabs.module.scss +41 -0
  156. package/src/Tabs/Tabs.test.tsx +268 -0
  157. package/src/Tabs/Tabs.tsx +149 -0
  158. package/src/TextEllipsis/TextEllipsis.module.scss +18 -0
  159. package/src/TextEllipsis/TextEllipsis.test.tsx +80 -0
  160. package/src/TextEllipsis/TextEllipsis.tsx +55 -0
  161. package/src/Tiles/Tile.test.tsx +27 -1
  162. package/src/Tiles/Tile.tsx +59 -62
  163. package/src/Tiles/Tiles.test.tsx +27 -1
  164. package/src/Tiles/Tiles.tsx +42 -39
  165. package/src/Tooltip/Tooltip.test.tsx +27 -1
  166. package/src/Tooltip/Tooltip.tsx +104 -92
  167. package/src/Typography/Typography.test.tsx +27 -1
  168. package/src/Typography/Typography.tsx +66 -68
  169. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +67 -62
  170. package/src/Wizard/WizardSteps/WizardSteps.tsx +24 -21
  171. package/src/_BaseStyling_/BaseStyling.tsx +19 -1
  172. package/src/hooks/useRepeater.test.tsx +139 -0
  173. package/src/hooks/useRepeater.ts +34 -0
  174. package/src/hooks/useSpacing.ts +1 -1
  175. package/src/hooks/useWrapper.ts +7 -2
  176. package/src/index.ts +12 -1
  177. package/src/interfaces.ts +2 -12
  178. package/src/util/helper.test.tsx +38 -1
  179. package/src/util/helper.tsx +21 -0
@@ -1 +1,6 @@
1
- export declare const generateID: (length?: number, stringToWeaveIn?: string | undefined) => string;
1
+ declare type KeyValuePair = {
2
+ [key: string]: unknown;
3
+ };
4
+ export declare const generateID: (length?: number, stringToWeaveIn?: string) => string;
5
+ export declare const filterProps: (props: any, regexPattern: RegExp, returnFiltered?: boolean) => KeyValuePair;
6
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onewelcome/react-lib-components",
3
- "version": "0.1.1-alpha",
3
+ "version": "0.1.2-alpha",
4
4
  "license": "Apache-2.0",
5
5
  "author": "OneWelcome B.V.",
6
6
  "main": "dist/index.js",
@@ -17,13 +17,15 @@
17
17
  "analyze": "size-limit --why",
18
18
  "build": "dts build",
19
19
  "build-storybook": "build-storybook",
20
+ "dev": "npm-run-all -p start test:watch storybook",
20
21
  "lint": "dts lint",
21
22
  "prepare": "husky install && dts build",
22
23
  "size": "size-limit",
23
24
  "start": "dts watch",
24
25
  "prettier": "prettier --write --ignore-path .gitignore \"**/*.+(ts|tsx|scss|mdx)\"",
25
26
  "storybook": "start-storybook -p 6006",
26
- "test": "dts test --passWithNoTests"
27
+ "test": "dts test --passWithNoTests",
28
+ "test:watch": "dts test --passWithNoTests --watch"
27
29
  },
28
30
  "jest": {
29
31
  "testEnvironment": "jsdom"
@@ -45,44 +47,48 @@
45
47
  }
46
48
  ],
47
49
  "devDependencies": {
48
- "@babel/core": "^7.17.8",
50
+ "@babel/core": "^7.18.0",
49
51
  "@size-limit/preset-small-lib": "^7.0.8",
50
- "@storybook/addon-a11y": "^6.4.18",
51
- "@storybook/addon-docs": "^6.4.19",
52
- "@storybook/addon-essentials": "^6.4.18",
53
- "@storybook/addon-links": "^6.4.18",
54
- "@storybook/addons": "^6.4.17",
55
- "@storybook/builder-webpack5": "^6.4.17",
56
- "@storybook/manager-webpack5": "^6.4.18",
52
+ "@storybook/addon-a11y": "^6.5.3",
53
+ "@storybook/addon-docs": "^6.5.3",
54
+ "@storybook/addon-essentials": "^6.5.3",
55
+ "@storybook/addon-links": "^6.5.3",
56
+ "@storybook/addons": "^6.5.3",
57
+ "@storybook/builder-webpack5": "^6.5.3",
58
+ "@storybook/manager-webpack5": "^6.5.3",
57
59
  "@storybook/preset-scss": "^1.0.3",
58
- "@storybook/react": "^6.4.13",
59
- "@testing-library/dom": "^8.12.0",
60
- "@testing-library/jest-dom": "^5.16.3",
61
- "@testing-library/react": "^12.1.4",
60
+ "@storybook/react": "^6.5.3",
61
+ "@storybook/theming": "^6.5.6",
62
+ "@testing-library/dom": "^8.13.0",
63
+ "@testing-library/jest-dom": "^5.16.4",
64
+ "@testing-library/react": "^12.1.5",
62
65
  "@testing-library/react-hooks": "^7.0.2",
63
66
  "@testing-library/user-event": "^13.5.0",
64
67
  "@tsconfig/create-react-app": "^1.0.2",
65
68
  "@tsconfig/recommended": "^1.0.1",
66
- "@types/mdx": "^2.0.1",
67
- "@types/react": "^17.0.43",
68
- "@types/react-dom": "^17.0.14",
69
+ "@types/color-convert": "^2.0.0",
70
+ "@types/mdx": "^2.0.2",
71
+ "@types/react": "^17.0.45",
72
+ "@types/react-dom": "^17.0.17",
69
73
  "@types/react-router": "^5.1.18",
70
74
  "@types/react-router-dom": "^5.3.3",
71
- "babel-loader": "^8.2.4",
72
- "dts-cli": "^1.4.0",
75
+ "babel-loader": "^8.2.5",
76
+ "dts-cli": "^1.5.1",
73
77
  "html-webpack-plugin": "^5.5.0",
74
78
  "husky": "^7.0.4",
75
79
  "identity-obj-proxy": "^3.0.0",
76
- "jest-junit": "^13.0.0",
77
- "lint-staged": "^12.3.7",
80
+ "jest-junit": "^13.2.0",
81
+ "lint-staged": "^12.4.1",
82
+ "npm-run-all": "^4.1.5",
78
83
  "react": "^17.0.2",
79
84
  "react-dom": "^17.0.2",
80
85
  "react-is": "^17.0.2",
81
86
  "react-router": "^6.3.0",
87
+ "react-router-dom": "^6.3.0",
82
88
  "rollup-plugin-styles": "^4.0.0",
83
- "sass": "^1.49.10",
89
+ "sass": "^1.52.0",
84
90
  "size-limit": "^7.0.8",
85
- "tslib": "^2.3.1",
86
- "typescript": "^4.6.3"
91
+ "tslib": "^2.4.0",
92
+ "typescript": "^4.6.4"
87
93
  }
88
94
  }
@@ -1,4 +1,4 @@
1
- import React, { Fragment, HTMLProps, ReactElement } from 'react';
1
+ import React, { ComponentPropsWithRef, Fragment, ReactElement } from 'react';
2
2
  import { Icon, Icons } from '../Icon/Icon';
3
3
  import { Link, Props as LinkProps } from '../Link/Link';
4
4
  import { Typography } from '../Typography/Typography';
@@ -6,43 +6,45 @@ import classes from './Breadcrumbs.module.scss';
6
6
 
7
7
  type ChildrenType = ReactElement<LinkProps, typeof Link>;
8
8
 
9
- export interface Props extends HTMLProps<HTMLElement> {
9
+ export interface Props extends ComponentPropsWithRef<'div'> {
10
10
  children: ChildrenType | ChildrenType[];
11
11
  'aria-label': string;
12
12
  }
13
13
 
14
- export const Breadcrumbs = ({
15
- children,
16
- 'aria-label': ariaLabel,
17
- className = '',
18
- ...rest
19
- }: Props) => {
20
- const items = React.Children.map(children, (child, index) => {
21
- const isLastElement = Array.isArray(children) ? index === children.length - 1 : true;
22
- if (isLastElement) {
23
- return (
24
- <Typography
25
- key={child.key}
26
- variant="body"
27
- tag="span"
28
- className={classes['last']}
29
- aria-current="page"
30
- >
31
- {child.props.children}
32
- </Typography>
33
- );
34
- } else {
35
- return (
36
- <Fragment key={child.key}>
37
- {React.cloneElement(child)}
38
- <Icon icon={Icons.ChevronRight} className={classes['icon']} />
39
- </Fragment>
40
- );
41
- }
42
- });
43
- return (
44
- <nav {...rest} aria-label={ariaLabel} className={`${classes['breadcrumbs']} ${className}`}>
45
- {items}
46
- </nav>
47
- );
48
- };
14
+ export const Breadcrumbs = React.forwardRef<HTMLDivElement, Props>(
15
+ ({ children, 'aria-label': ariaLabel, className = '', ...rest }: Props, ref) => {
16
+ const items = React.Children.map(children, (child, index) => {
17
+ const isLastElement = Array.isArray(children) ? index === children.length - 1 : true;
18
+ if (isLastElement) {
19
+ return (
20
+ <Typography
21
+ key={child.key}
22
+ variant="body"
23
+ tag="span"
24
+ className={classes['last']}
25
+ aria-current="page"
26
+ >
27
+ {child.props.children}
28
+ </Typography>
29
+ );
30
+ } else {
31
+ return (
32
+ <Fragment key={child.key}>
33
+ {React.cloneElement(child)}
34
+ <Icon icon={Icons.ChevronRight} className={classes['icon']} />
35
+ </Fragment>
36
+ );
37
+ }
38
+ });
39
+ return (
40
+ <nav
41
+ {...rest}
42
+ ref={ref}
43
+ aria-label={ariaLabel}
44
+ className={`${classes['breadcrumbs']} ${className}`}
45
+ >
46
+ {items}
47
+ </nav>
48
+ );
49
+ }
50
+ );
@@ -1,31 +1,46 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import { BaseButton, Props } from './BaseButton';
3
- import { render, getByRole } from '@testing-library/react';
3
+ import { render } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
5
 
6
- const createButton = (params?: Props, children = 'This is a button') => {
7
- const queries = render(<BaseButton {...params}>{children}</BaseButton>);
8
- const button = getByRole(queries.container, 'button');
6
+ const defaultParams: Props = {};
7
+
8
+ const createBaseButton = (params?: (defaultParams: Props) => Props) => {
9
+ let parameters: Props = defaultParams;
10
+ if (params) {
11
+ parameters = params(defaultParams);
12
+ }
13
+ const queries = render(
14
+ <BaseButton {...parameters} data-testid="baseButton">
15
+ baseButton content
16
+ </BaseButton>
17
+ );
18
+ const baseButton = queries.getByTestId('baseButton');
9
19
 
10
20
  return {
11
21
  ...queries,
12
- button,
22
+ baseButton,
13
23
  };
14
24
  };
15
25
 
16
26
  describe('BaseButton should render', () => {
17
- it('renders without crashing', async () => {
18
- const { findByText } = createButton();
19
- findByText('This is a button');
27
+ it('renders without crashing', () => {
28
+ const { baseButton } = createBaseButton();
29
+
30
+ expect(baseButton).toBeDefined();
31
+ expect(baseButton).toHaveTextContent('baseButton content');
20
32
  });
21
33
  });
22
34
 
23
35
  describe('On click handler', () => {
24
36
  it('executes the onclick handler', async () => {
25
37
  const onClickHandler = jest.fn();
26
- const { button } = createButton({ onClick: onClickHandler });
38
+ const { baseButton } = createBaseButton((defaultParams) => ({
39
+ ...defaultParams,
40
+ onClick: onClickHandler,
41
+ }));
27
42
 
28
- userEvent.click(button);
43
+ userEvent.click(baseButton);
29
44
 
30
45
  expect(onClickHandler).toBeCalled();
31
46
  });
@@ -34,26 +49,57 @@ describe('On click handler', () => {
34
49
  describe('Properties of the button', () => {
35
50
  it('should be disabled, function should not have been called', () => {
36
51
  const onClickHandler = jest.fn();
37
- const { button } = createButton({
52
+ const { baseButton } = createBaseButton((defaultParams) => ({
53
+ ...defaultParams,
38
54
  disabled: true,
39
55
  onClick: onClickHandler,
40
- });
56
+ }));
41
57
 
42
- userEvent.click(button);
58
+ userEvent.click(baseButton);
43
59
  expect(onClickHandler).toHaveBeenCalledTimes(0);
44
60
  });
45
61
 
46
62
  it('should have the class "TESTING"', () => {
47
- const { button } = createButton({
63
+ const { baseButton } = createBaseButton((defaultParams) => ({
64
+ ...defaultParams,
48
65
  className: 'TESTING',
49
- });
66
+ }));
50
67
 
51
- expect(button.classList.contains('TESTING')).toBe(true);
68
+ expect(baseButton).toHaveClass('TESTING');
52
69
  });
53
70
 
54
71
  it('should have a "name" property with the value "button"', () => {
55
- const { button } = createButton({ name: 'button' });
72
+ const { baseButton } = createBaseButton((defaultParams) => ({
73
+ ...defaultParams,
74
+ name: 'button',
75
+ }));
76
+
77
+ expect(baseButton).toHaveAttribute('name', 'button');
78
+ });
79
+ });
80
+
81
+ describe('ref should work', () => {
82
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
83
+ const ExampleComponent = ({
84
+ propagateRef,
85
+ }: {
86
+ propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
87
+ }) => {
88
+ const ref = useRef(null);
89
+
90
+ useEffect(() => {
91
+ if (ref.current) {
92
+ propagateRef && propagateRef(ref);
93
+ }
94
+ }, [ref]);
95
+
96
+ return <BaseButton {...defaultParams} data-ref="testing" ref={ref} />;
97
+ };
98
+
99
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
100
+ expect(ref.current).toHaveAttribute('data-ref', 'testing');
101
+ };
56
102
 
57
- expect(button.getAttribute('name')).toBe('button');
103
+ render(<ExampleComponent propagateRef={refCheck} />);
58
104
  });
59
105
  });
@@ -1,10 +1,9 @@
1
- import React, { RefObject } from 'react';
1
+ import React, { ComponentPropsWithRef } from 'react';
2
2
  import classes from './BaseButton.module.scss';
3
3
 
4
- export interface Props extends Omit<React.HTMLProps<HTMLButtonElement>, 'ref'> {
4
+ export interface Props extends ComponentPropsWithRef<'button'> {
5
5
  type?: 'submit' | 'button' | 'reset';
6
6
  disabled?: boolean;
7
- ref?: RefObject<HTMLButtonElement>;
8
7
  color?: 'primary' | 'secondary' | 'tertiary' | 'default';
9
8
  }
10
9
 
@@ -1,11 +1,21 @@
1
- import React from 'react';
1
+ import React, { useRef, useEffect } from 'react';
2
2
  import { Button, Props } from './Button';
3
- import { render, getByRole } from '@testing-library/react';
3
+ import { render } from '@testing-library/react';
4
4
  import { Icon, Icons } from '../Icon/Icon';
5
5
 
6
- const createButton = (params?: Props, children = 'This is a button') => {
7
- const queries = render(<Button {...params}>{children}</Button>);
8
- const button = getByRole(queries.container, 'button');
6
+ const defaultParams: Props = {};
7
+
8
+ const createButton = (params?: (defaultParams: Props) => Props) => {
9
+ let parameters: Props = defaultParams;
10
+ if (params) {
11
+ parameters = params(defaultParams);
12
+ }
13
+ const queries = render(
14
+ <Button {...parameters} data-testid="button">
15
+ button content
16
+ </Button>
17
+ );
18
+ const button = queries.getByTestId('button');
9
19
 
10
20
  return {
11
21
  ...queries,
@@ -21,56 +31,92 @@ describe('Button should render', () => {
21
31
  });
22
32
  });
23
33
 
34
+ describe('ref should work', () => {
35
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
36
+ const ExampleComponent = ({
37
+ propagateRef,
38
+ }: {
39
+ propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
40
+ }) => {
41
+ const ref = useRef(null);
42
+
43
+ useEffect(() => {
44
+ if (ref.current) {
45
+ propagateRef && propagateRef(ref);
46
+ }
47
+ }, [ref]);
48
+
49
+ return <Button {...defaultParams} data-ref="testing" ref={ref} />;
50
+ };
51
+
52
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
53
+ expect(ref.current).toHaveAttribute('data-ref', 'testing');
54
+ };
55
+
56
+ render(<ExampleComponent propagateRef={refCheck} />);
57
+ });
58
+ });
59
+
60
+ describe('Button should render', () => {
61
+ it('renders without crashing', () => {
62
+ const { button } = createButton();
63
+
64
+ expect(button).toBeDefined();
65
+ });
66
+ });
67
+
24
68
  describe('Different variants of the button', () => {
25
69
  it('should have a class of "primary"', () => {
26
- const { button } = createButton({ color: 'primary' });
70
+ const { button } = createButton();
27
71
  expect(button.classList.contains('primary')).toBe(true);
28
72
  });
29
73
 
30
74
  it('should have a class of "secondary"', () => {
31
- const { button } = createButton({ color: 'secondary' });
75
+ const { button } = createButton((defaultParams) => ({ ...defaultParams, color: 'secondary' }));
32
76
  expect(button.classList.contains('secondary')).toBe(true);
33
77
  });
34
78
 
35
79
  it('should have a class of "tertiary"', () => {
36
- const { button } = createButton({ color: 'tertiary' });
80
+ const { button } = createButton((defaultParams) => ({ ...defaultParams, color: 'tertiary' }));
37
81
  expect(button.classList.contains('tertiary')).toBe(true);
38
82
  });
39
83
 
40
84
  it('should have a class of "fill"', () => {
41
- const { button } = createButton({ variant: 'fill' });
85
+ const { button } = createButton((defaultParams) => ({ ...defaultParams, variant: 'fill' }));
42
86
  expect(button.classList.contains('fill')).toBe(true);
43
87
  });
44
88
 
45
89
  it('should have a class of "outline"', () => {
46
- const { button } = createButton({ variant: 'outline' });
90
+ const { button } = createButton((defaultParams) => ({ ...defaultParams, variant: 'outline' }));
47
91
  expect(button.classList.contains('outline')).toBe(true);
48
92
  });
49
93
 
50
94
  it('should have a class of "text"', () => {
51
- const { button } = createButton({ variant: 'text' });
95
+ const { button } = createButton((defaultParams) => ({ ...defaultParams, variant: 'text' }));
52
96
  expect(button.classList.contains('text')).toBe(true);
53
97
  });
54
98
  });
55
99
 
56
100
  describe('Button contains an icon', () => {
57
101
  it('Contains an icon at the start', () => {
58
- const { button } = createButton({
102
+ const { button } = createButton((defaultParams) => ({
103
+ ...defaultParams,
59
104
  startIcon: <Icon icon={Icons.Calendar} />,
60
- });
105
+ }));
61
106
 
62
107
  expect(button.classList.contains('has-icon')).toBe(true);
63
108
  expect(button.firstElementChild?.nodeName).toBe('I');
64
- expect(button.querySelector('i + span')!.innerHTML).toBe('This is a button');
109
+ expect(button.querySelector('i + span')!.innerHTML).toBe('button content');
65
110
  });
66
111
 
67
112
  it('Contains an icon at the end', () => {
68
- const { button } = createButton({
113
+ const { button } = createButton((defaultParams) => ({
114
+ ...defaultParams,
69
115
  endIcon: <Icon icon={Icons.Calendar} />,
70
- });
116
+ }));
71
117
 
72
118
  expect(button.classList.contains('has-icon')).toBe(true);
73
119
  expect(button.lastElementChild?.nodeName).toBe('I');
74
- expect(button.querySelector('span')!.innerHTML).toBe('This is a button');
120
+ expect(button.querySelector('span')!.innerHTML).toBe('button content');
75
121
  });
76
122
  });
@@ -1,18 +1,25 @@
1
- import React, { RefObject } from 'react';
1
+ import React from 'react';
2
2
  import { BaseButton, Props as BaseButtonProps } from './BaseButton';
3
3
  import classes from './Button.module.scss';
4
4
 
5
- export interface Props extends Omit<BaseButtonProps, 'ref'> {
5
+ export interface Props extends BaseButtonProps {
6
6
  startIcon?: React.ReactNode | false;
7
7
  endIcon?: React.ReactNode | false;
8
8
  children?: React.ReactNode;
9
9
  variant?: 'text' | 'fill' | 'outline';
10
- ref?: RefObject<HTMLButtonElement>;
11
10
  }
12
11
 
13
12
  export const Button = React.forwardRef<HTMLButtonElement, Props>(
14
13
  (
15
- { children, variant = 'fill', color = 'primary', startIcon = false, endIcon = false, ...rest },
14
+ {
15
+ children,
16
+ variant = 'fill',
17
+ color = 'primary',
18
+ startIcon = false,
19
+ endIcon = false,
20
+ className,
21
+ ...rest
22
+ },
16
23
  ref
17
24
  ) => {
18
25
  const additionalClasses = [];
@@ -29,6 +36,10 @@ export const Button = React.forwardRef<HTMLButtonElement, Props>(
29
36
  additionalClasses.push(classes['end-icon']);
30
37
  }
31
38
 
39
+ if (className) {
40
+ additionalClasses.push(className);
41
+ }
42
+
32
43
  return (
33
44
  <BaseButton
34
45
  {...rest}
@@ -1,47 +1,82 @@
1
- import React from 'react';
1
+ import React, { useRef, useEffect } from 'react';
2
2
  import { IconButton, Props } from './IconButton';
3
- import { render, getByRole } from '@testing-library/react';
4
- import { Icon, Icons } from '../Icon/Icon';
5
-
6
- const createButton = ({
7
- title = 'Icon',
8
- children = <Icon icon={Icons.Calendar} />,
9
- ...rest
10
- }: Partial<Props> = {}) => {
3
+ import { render } from '@testing-library/react';
4
+
5
+ const defaultParams: Props = {
6
+ title: 'iconbutton',
7
+ };
8
+
9
+ const createIconButton = (params?: (defaultParams: Props) => Props) => {
10
+ let parameters: Props = defaultParams;
11
+ if (params) {
12
+ parameters = params(defaultParams);
13
+ }
11
14
  const queries = render(
12
- <IconButton {...rest} title={title}>
13
- {children}
15
+ <IconButton {...parameters} data-testid="iconButton">
16
+ iconButton content
14
17
  </IconButton>
15
18
  );
16
- const button = getByRole(queries.container, 'button');
19
+ const iconButton = queries.getByTestId('iconButton');
17
20
 
18
21
  return {
19
22
  ...queries,
20
- button,
23
+ iconButton,
21
24
  };
22
25
  };
23
26
 
24
- describe('IconButton', () => {
27
+ describe('IconButton should render', () => {
25
28
  it('renders without crashing', () => {
26
- const { button } = createButton();
29
+ const { iconButton } = createIconButton();
30
+
31
+ expect(iconButton).toBeDefined();
32
+ });
33
+ });
34
+
35
+ describe('ref should work', () => {
36
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
37
+ const ExampleComponent = ({
38
+ propagateRef,
39
+ }: {
40
+ propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
41
+ }) => {
42
+ const ref = useRef(null);
43
+
44
+ useEffect(() => {
45
+ if (ref.current) {
46
+ propagateRef && propagateRef(ref);
47
+ }
48
+ }, [ref]);
49
+
50
+ return <IconButton {...defaultParams} data-ref="testing" ref={ref} />;
51
+ };
52
+
53
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
54
+ expect(ref.current).toHaveAttribute('data-ref', 'testing');
55
+ };
27
56
 
28
- expect(button).toBeDefined();
57
+ render(<ExampleComponent propagateRef={refCheck} />);
29
58
  });
30
59
  });
31
60
 
32
61
  describe('Different variants of the button', () => {
33
62
  it('should have a class of "primary"', () => {
34
- const { button } = createButton({ color: 'primary' });
35
- expect(button.classList.contains('primary')).toBe(true);
63
+ const { iconButton } = createIconButton();
64
+ expect(iconButton.classList.contains('primary')).toBe(true);
36
65
  });
37
66
 
38
67
  it('should have a class of "secondary"', () => {
39
- const { button } = createButton({ color: 'secondary' });
40
- expect(button.classList.contains('secondary')).toBe(true);
68
+ const { iconButton } = createIconButton((defaultParams) => ({
69
+ ...defaultParams,
70
+ color: 'secondary',
71
+ }));
72
+ expect(iconButton.classList.contains('secondary')).toBe(true);
41
73
  });
42
74
 
43
75
  it('should have a class of "tertiary"', () => {
44
- const { button } = createButton({ color: 'tertiary' });
45
- expect(button.classList.contains('tertiary')).toBe(true);
76
+ const { iconButton } = createIconButton((defaultParams) => ({
77
+ ...defaultParams,
78
+ color: 'tertiary',
79
+ }));
80
+ expect(iconButton.classList.contains('tertiary')).toBe(true);
46
81
  });
47
82
  });
@@ -1,28 +1,33 @@
1
- import React, { Fragment, RefObject } from 'react';
1
+ import React, { Fragment } from 'react';
2
2
  import { BaseButton, Props as BaseButtonProps } from './BaseButton';
3
3
  import classes from './IconButton.module.scss';
4
4
  import readyclasses from '../readyclasses.module.scss';
5
5
 
6
- export interface Props extends Omit<BaseButtonProps, 'ref'> {
6
+ export interface Props extends BaseButtonProps {
7
7
  children?: React.ReactNode;
8
8
  iconSize?: 's' | 'm' | 'l';
9
9
  className?: string;
10
10
  title?: string;
11
- ref?: RefObject<HTMLButtonElement>;
12
11
  }
13
12
 
14
13
  export const IconButton = React.forwardRef<HTMLButtonElement, Props>(
15
- ({ children, color = 'primary', iconSize = 'm', title, ...rest }, ref) => {
14
+ ({ children, color = 'primary', iconSize = 'm', title, className, ...rest }, ref) => {
16
15
  if (!title) {
17
16
  console.error("Please make sure to specify a 'title' prop to your IconButton component! ");
18
17
  }
19
18
 
19
+ const iconButtonClasses = [
20
+ classes['icon-button'],
21
+ classes[color],
22
+ classes['button-' + iconSize],
23
+ ];
24
+
25
+ if (className) {
26
+ iconButtonClasses.push(className);
27
+ }
28
+
20
29
  return (
21
- <BaseButton
22
- {...rest}
23
- ref={ref}
24
- className={`${classes['icon-button']} ${classes[color]} ${classes['button-' + iconSize]}`}
25
- >
30
+ <BaseButton {...rest} ref={ref} className={iconButtonClasses.join(' ')}>
26
31
  <Fragment>
27
32
  {children}
28
33
  <span className={readyclasses['sr-only']}>{title}</span>