@onewelcome/react-lib-components 0.1.0-alpha → 0.1.1-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 (100) hide show
  1. package/dist/Button/IconButton.d.ts +1 -1
  2. package/dist/Form/Fieldset/Fieldset.d.ts +6 -4
  3. package/dist/Form/FormControl/FormControl.d.ts +2 -1
  4. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +2 -1
  5. package/dist/{BaseModal → Notifications/BaseModal}/BaseModal.d.ts +4 -2
  6. package/dist/{BaseModal → Notifications/BaseModal}/BaseModalActions/BaseModalActions.d.ts +0 -0
  7. package/dist/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.d.ts +0 -0
  8. package/dist/{BaseModal → Notifications/BaseModal}/BaseModalContext.d.ts +0 -0
  9. package/dist/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.d.ts +1 -1
  10. package/dist/{Dialog → Notifications/Dialog}/Dialog.d.ts +4 -3
  11. package/dist/{Dialog → Notifications/Dialog}/DialogActions/DialogActions.d.ts +0 -0
  12. package/dist/{Dialog → Notifications/Dialog}/DialogTitle/DialogTitle.d.ts +0 -0
  13. package/dist/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.d.ts +12 -0
  14. package/dist/Notifications/DiscardChangesModal/DiscardChangesModal.d.ts +11 -0
  15. package/dist/{Modal → Notifications/Modal}/Modal.d.ts +0 -0
  16. package/dist/{Modal → Notifications/Modal}/ModalActions/ModalActions.d.ts +0 -0
  17. package/dist/{Modal → Notifications/Modal}/ModalContent/ModalContent.d.ts +0 -0
  18. package/dist/{Modal → Notifications/Modal}/ModalHeader/ModalHeader.d.ts +0 -0
  19. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.d.ts +0 -0
  20. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.d.ts +0 -0
  21. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.d.ts +1 -1
  22. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarStateProvider.d.ts +0 -0
  23. package/dist/{Snackbar → Notifications/Snackbar}/interfaces.d.ts +0 -0
  24. package/dist/{Snackbar → Notifications/Snackbar}/useSnackbar.d.ts +0 -0
  25. package/dist/Tiles/Tile.d.ts +4 -5
  26. package/dist/index.d.ts +8 -7
  27. package/dist/react-lib-components.cjs.development.js +226 -125
  28. package/dist/react-lib-components.cjs.development.js.map +1 -1
  29. package/dist/react-lib-components.cjs.production.min.js +1 -1
  30. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  31. package/dist/react-lib-components.esm.js +226 -126
  32. package/dist/react-lib-components.esm.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/Button/IconButton.tsx +8 -4
  35. package/src/Form/Checkbox/Checkbox.module.scss +4 -0
  36. package/src/Form/Checkbox/Checkbox.tsx +11 -6
  37. package/src/Form/Fieldset/Fieldset.module.scss +11 -1
  38. package/src/Form/Fieldset/Fieldset.test.tsx +2 -2
  39. package/src/Form/Fieldset/Fieldset.tsx +22 -10
  40. package/src/Form/FormControl/FormControl.tsx +3 -0
  41. package/src/Form/Radio/Radio.module.scss +4 -0
  42. package/src/Form/Radio/Radio.tsx +12 -2
  43. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +1 -1
  44. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +1 -1
  45. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.test.tsx +1 -1
  46. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +1 -1
  47. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +2 -1
  48. package/src/{BaseModal → Notifications/BaseModal}/BaseModal.module.scss +0 -0
  49. package/src/{BaseModal → Notifications/BaseModal}/BaseModal.test.tsx +8 -15
  50. package/src/{BaseModal → Notifications/BaseModal}/BaseModal.tsx +11 -24
  51. package/src/{BaseModal → Notifications/BaseModal}/BaseModalActions/BaseModalActions.module.scss +0 -0
  52. package/src/{BaseModal → Notifications/BaseModal}/BaseModalActions/BaseModalActions.test.tsx +0 -0
  53. package/src/{BaseModal → Notifications/BaseModal}/BaseModalActions/BaseModalActions.tsx +0 -0
  54. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.module.scss +0 -0
  55. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.test.tsx +0 -0
  56. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.tsx +0 -0
  57. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContext.ts +0 -0
  58. package/src/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.module.scss +0 -0
  59. package/src/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.test.tsx +0 -0
  60. package/src/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.tsx +6 -6
  61. package/src/{Dialog → Notifications/Dialog}/Dialog.module.scss +0 -0
  62. package/src/{Dialog → Notifications/Dialog}/Dialog.test.tsx +13 -16
  63. package/src/{Dialog → Notifications/Dialog}/Dialog.tsx +17 -6
  64. package/src/{Dialog → Notifications/Dialog}/DialogActions/DialogActions.module.scss +0 -0
  65. package/src/{Dialog → Notifications/Dialog}/DialogActions/DialogActions.test.tsx +0 -0
  66. package/src/{Dialog → Notifications/Dialog}/DialogActions/DialogActions.tsx +0 -0
  67. package/src/{Dialog → Notifications/Dialog}/DialogTitle/DialogTitle.module.scss +0 -0
  68. package/src/{Dialog → Notifications/Dialog}/DialogTitle/DialogTitle.test.tsx +0 -0
  69. package/src/{Dialog → Notifications/Dialog}/DialogTitle/DialogTitle.tsx +3 -3
  70. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.test.tsx +55 -0
  71. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +48 -0
  72. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.test.tsx +111 -0
  73. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.tsx +56 -0
  74. package/src/{Modal → Notifications/Modal}/Modal.test.tsx +0 -0
  75. package/src/{Modal → Notifications/Modal}/Modal.tsx +0 -0
  76. package/src/{Modal → Notifications/Modal}/ModalActions/ModalActions.tsx +0 -0
  77. package/src/{Modal → Notifications/Modal}/ModalContent/ModalContent.tsx +0 -0
  78. package/src/{Modal → Notifications/Modal}/ModalHeader/ModalHeader.tsx +0 -0
  79. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.module.scss +0 -0
  80. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.test.tsx +0 -0
  81. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.tsx +0 -0
  82. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.module.scss +1 -1
  83. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.test.tsx +0 -0
  84. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.tsx +5 -6
  85. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.test.tsx +0 -0
  86. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.tsx +2 -2
  87. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarStateProvider.tsx +0 -0
  88. package/src/{Snackbar → Notifications/Snackbar}/interfaces.ts +0 -0
  89. package/src/{Snackbar → Notifications/Snackbar}/useSnackbar.ts +0 -0
  90. package/src/Popover/Popover.test.tsx +3 -3
  91. package/src/Tiles/Tile.module.scss +1 -1
  92. package/src/Tiles/Tile.test.tsx +21 -11
  93. package/src/Tiles/Tile.tsx +52 -15
  94. package/src/Tiles/Tiles.test.tsx +11 -9
  95. package/src/Tiles/Tiles.tsx +3 -3
  96. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +3 -3
  97. package/src/Wizard/Wizard.tsx +2 -2
  98. package/src/Wizard/WizardActions/WizardActions.tsx +3 -3
  99. package/src/hooks/usePosition.test.tsx +3 -3
  100. package/src/index.ts +8 -7
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Typography } from '../../Typography/Typography';
2
+ import { Typography } from '../../../Typography/Typography';
3
3
  import classes from './DialogTitle.module.scss';
4
4
 
5
5
  export interface Props {
@@ -9,10 +9,10 @@ export interface Props {
9
9
 
10
10
  export const DialogTitle = ({ id, title }: Props) => {
11
11
  return (
12
- <header className={classes['header']}>
12
+ <div className={classes['header']}>
13
13
  <Typography id={id} className={classes['title']} tag="h1" variant="h4">
14
14
  {title}
15
15
  </Typography>
16
- </header>
16
+ </div>
17
17
  );
18
18
  };
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import { DiscardChangesDialog, Props } from './DiscardChangesDialog';
3
+ import { render } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+
6
+ const defaultParams: Props = {
7
+ open: true,
8
+ onKeepEditing: jest.fn(),
9
+ onDiscardChanges: jest.fn(),
10
+ discardChangesButtonLabel: 'Discard Changes',
11
+ keepEditingButtonLabel: 'Keep Editing',
12
+ contentLabel: 'Content',
13
+ titleLabel: 'Unsaved changes',
14
+ };
15
+
16
+ const createDiscardChangesDialog = (params?: (defaultParams: Props) => Props) => {
17
+ let parameters: Props = defaultParams;
18
+ if (params) {
19
+ parameters = params(defaultParams);
20
+ }
21
+ const queries = render(
22
+ <DiscardChangesDialog {...parameters} data-testid="DiscardChangesDialog"></DiscardChangesDialog>
23
+ );
24
+ const discardChangesDialog = queries.getByTestId('DiscardChangesDialog');
25
+ const discardChangesBtn = queries.getByRole('button', {
26
+ name: defaultParams.discardChangesButtonLabel,
27
+ });
28
+ const keepEditingBtn = queries.getByRole('button', {
29
+ name: defaultParams.keepEditingButtonLabel,
30
+ });
31
+
32
+ return {
33
+ ...queries,
34
+ discardChangesDialog,
35
+ discardChangesBtn,
36
+ keepEditingBtn,
37
+ };
38
+ };
39
+
40
+ describe('DiscardChangesDialog should render', () => {
41
+ it('renders without crashing', () => {
42
+ const { discardChangesDialog, discardChangesBtn, keepEditingBtn } =
43
+ createDiscardChangesDialog();
44
+
45
+ expect(discardChangesDialog).toBeDefined();
46
+ expect(discardChangesDialog).toHaveTextContent(defaultParams.contentLabel);
47
+ expect(discardChangesDialog).toHaveTextContent(defaultParams.titleLabel);
48
+
49
+ userEvent.click(discardChangesBtn);
50
+ expect(defaultParams.onDiscardChanges).toBeCalledTimes(1);
51
+
52
+ userEvent.click(keepEditingBtn);
53
+ expect(defaultParams.onKeepEditing).toBeCalledTimes(1);
54
+ });
55
+ });
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { Dialog } from '../../Dialog/Dialog';
3
+ import { HTMLProps } from '../../../interfaces';
4
+ import { Typography } from '../../../Typography/Typography';
5
+
6
+ export interface Props extends Omit<HTMLProps<HTMLDivElement>, 'children'> {
7
+ open: boolean;
8
+ discardChangesButtonLabel: string;
9
+ keepEditingButtonLabel: string;
10
+ contentLabel: string;
11
+ titleLabel: string;
12
+ onKeepEditing: () => void;
13
+ onDiscardChanges: () => void;
14
+ }
15
+
16
+ export const DiscardChangesDialog = ({
17
+ open,
18
+ onKeepEditing,
19
+ onDiscardChanges,
20
+ discardChangesButtonLabel,
21
+ keepEditingButtonLabel,
22
+ contentLabel,
23
+ titleLabel,
24
+ ...rest
25
+ }: Props) => {
26
+ return (
27
+ <Dialog
28
+ {...rest}
29
+ open={open}
30
+ alignActions="left"
31
+ title={titleLabel}
32
+ onClose={onKeepEditing}
33
+ primaryAction={{
34
+ label: discardChangesButtonLabel,
35
+ onClick: onDiscardChanges,
36
+ }}
37
+ secondaryAction={{
38
+ label: keepEditingButtonLabel,
39
+ onClick: onKeepEditing,
40
+ }}
41
+ disableEscapeKeyDown={false}
42
+ >
43
+ <Typography variant="body" spacing={{ margin: 0 }}>
44
+ {contentLabel}
45
+ </Typography>
46
+ </Dialog>
47
+ );
48
+ };
@@ -0,0 +1,111 @@
1
+ import React from 'react';
2
+ import { DiscardChangesModal, Props } from './DiscardChangesModal';
3
+ import { findByTestId, getAllByRole, render, waitFor } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+
6
+ const defaultParams: Props = {
7
+ id: 'modal',
8
+ open: true,
9
+ hasUnsavedChanges: jest.fn(),
10
+ onClose: jest.fn(),
11
+ headerProps: {
12
+ title: 'Header',
13
+ },
14
+ discardChangedDialogProps: {
15
+ 'data-testid': 'discardChangesDialog',
16
+ discardChangesButtonLabel: 'Discard',
17
+ keepEditingButtonLabel: 'Keep editing',
18
+ contentLabel: 'Unsaved changes',
19
+ titleLabel: 'Dialog',
20
+ },
21
+ children: <span>children</span>,
22
+ };
23
+
24
+ const createDiscardChangesModal = (params?: (defaultParams: Props) => Props) => {
25
+ let parameters: Props = defaultParams;
26
+ if (params) {
27
+ parameters = params(defaultParams);
28
+ }
29
+ const queries = render(<DiscardChangesModal {...parameters} data-testid="discardChangesModal" />);
30
+ const discardChangesModal = queries.getByTestId('discardChangesModal');
31
+ const closeBtn = queries.getByRole('button', { name: 'close modal' });
32
+
33
+ return {
34
+ ...queries,
35
+ discardChangesModal,
36
+ closeBtn,
37
+ };
38
+ };
39
+
40
+ const getDiscardChangesButtons = (container: HTMLElement) =>
41
+ getAllByRole(container, 'button', {
42
+ name: new RegExp(
43
+ `(${defaultParams.discardChangedDialogProps.discardChangesButtonLabel}|${defaultParams.discardChangedDialogProps.keepEditingButtonLabel})`
44
+ ),
45
+ });
46
+
47
+ const findDiscardChangesDialog = (container: HTMLElement) =>
48
+ findByTestId(container, 'discardChangesDialog');
49
+
50
+ describe('DiscardChangesModal should render', () => {
51
+ it('renders without crashing', () => {
52
+ const { discardChangesModal, container } = createDiscardChangesModal();
53
+
54
+ expect(discardChangesModal).toBeDefined();
55
+ expect(discardChangesModal).toHaveTextContent('children');
56
+ expect(discardChangesModal).toHaveTextContent(defaultParams.headerProps.title);
57
+ expect(container).not.toHaveTextContent(defaultParams.discardChangedDialogProps.titleLabel);
58
+ });
59
+
60
+ it('clicking on close button without making any changes close the modal', () => {
61
+ (
62
+ defaultParams.hasUnsavedChanges as jest.MockedFunction<typeof defaultParams.hasUnsavedChanges>
63
+ ).mockReturnValue(false);
64
+ const { closeBtn } = createDiscardChangesModal();
65
+
66
+ userEvent.click(closeBtn);
67
+ expect(defaultParams.onClose).toBeCalledTimes(1);
68
+ });
69
+ });
70
+
71
+ describe('DiscardChangesModal should show DiscardChangesDialog', () => {
72
+ it('showing DiscardChangesDialog and clicking on `keep editing` button', async () => {
73
+ (
74
+ defaultParams.hasUnsavedChanges as jest.MockedFunction<typeof defaultParams.hasUnsavedChanges>
75
+ ).mockReturnValue(true);
76
+ const { closeBtn } = createDiscardChangesModal();
77
+ const container = document.body;
78
+ expect(defaultParams.onClose).not.toBeCalled();
79
+
80
+ userEvent.click(closeBtn);
81
+
82
+ expect(defaultParams.onClose).not.toBeCalled();
83
+ await findDiscardChangesDialog(container);
84
+ const [_, keepEditingBtn] = getDiscardChangesButtons(container);
85
+ expect(container).toHaveTextContent(defaultParams.discardChangedDialogProps.titleLabel);
86
+ expect(container).toHaveTextContent(defaultParams.discardChangedDialogProps.contentLabel);
87
+
88
+ userEvent.click(keepEditingBtn);
89
+ expect(defaultParams.onClose).not.toBeCalled();
90
+ await waitFor(() =>
91
+ expect(container).not.toHaveTextContent(defaultParams.discardChangedDialogProps.titleLabel)
92
+ );
93
+ });
94
+
95
+ it('showing DiscardChangesDialog and clicking on `discard` button', async () => {
96
+ (
97
+ defaultParams.hasUnsavedChanges as jest.MockedFunction<typeof defaultParams.hasUnsavedChanges>
98
+ ).mockReturnValue(true);
99
+ const { closeBtn } = createDiscardChangesModal();
100
+ const container = document.body;
101
+
102
+ userEvent.click(closeBtn);
103
+
104
+ await findDiscardChangesDialog(container);
105
+ const [discardBtn] = getDiscardChangesButtons(container);
106
+ expect(container).toHaveTextContent(defaultParams.discardChangedDialogProps.titleLabel);
107
+
108
+ userEvent.click(discardBtn);
109
+ expect(defaultParams.onClose).toBeCalled();
110
+ });
111
+ });
@@ -0,0 +1,56 @@
1
+ import React, { Fragment, useState } from 'react';
2
+ import { Modal, Props as ModalProps } from '../Modal/Modal';
3
+ import { ModalHeader, Props as ModalHeaderProps } from '../Modal/ModalHeader/ModalHeader';
4
+ import {
5
+ DiscardChangesDialog,
6
+ Props as DiscardChangesDialogProps,
7
+ } from './DiscardChangesDialog/DiscardChangesDialog';
8
+
9
+ export interface Props extends Omit<ModalProps, 'onClose'> {
10
+ hasUnsavedChanges: () => boolean;
11
+ onClose: (event?: React.MouseEvent<HTMLElement>) => unknown;
12
+ headerProps: Omit<ModalHeaderProps, 'onClose' | 'id'>;
13
+ discardChangedDialogProps: Omit<
14
+ DiscardChangesDialogProps,
15
+ 'open' | 'onKeepEditing' | 'onDiscardChanges'
16
+ >;
17
+ }
18
+
19
+ export const DiscardChangesModal = ({
20
+ id,
21
+ onClose,
22
+ children,
23
+ hasUnsavedChanges,
24
+ headerProps,
25
+ discardChangedDialogProps,
26
+ ...rest
27
+ }: Props) => {
28
+ const [openDiscardChangesDialog, setOpenDiscardChangesDialog] = useState(false);
29
+
30
+ const onCloseWrapper = () =>
31
+ hasUnsavedChanges() ? setOpenDiscardChangesDialog(true) : onClose();
32
+
33
+ const onDialogKeepEditing = () => {
34
+ setOpenDiscardChangesDialog(false);
35
+ };
36
+
37
+ const onDialogDiscardChanges = () => {
38
+ setOpenDiscardChangesDialog(false);
39
+ onClose();
40
+ };
41
+
42
+ return (
43
+ <Fragment>
44
+ <Modal id={id} onClose={onCloseWrapper} {...rest}>
45
+ <ModalHeader {...headerProps} id={`${id}-label`} onClose={onCloseWrapper} />
46
+ {children}
47
+ </Modal>
48
+ <DiscardChangesDialog
49
+ {...discardChangedDialogProps}
50
+ open={openDiscardChangesDialog}
51
+ onKeepEditing={onDialogKeepEditing}
52
+ onDiscardChanges={onDialogDiscardChanges}
53
+ />
54
+ </Fragment>
55
+ );
56
+ };
File without changes
@@ -1,4 +1,4 @@
1
- @import '../../readyclasses.module.scss';
1
+ @import '../../../readyclasses.module.scss';
2
2
 
3
3
  .snackbar {
4
4
  padding: 1rem 1.25rem;
@@ -1,11 +1,11 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
- import { IconButton } from '../../Button/IconButton';
3
- import { Icon, Icons } from '../../Icon/Icon';
2
+ import { IconButton } from '../../../Button/IconButton';
3
+ import { Icon, Icons } from '../../../Icon/Icon';
4
4
  import { Variant, Actions } from '../interfaces';
5
5
  import classes from './SnackbarItem.module.scss';
6
- import readyclasses from '../../readyclasses.module.scss';
7
- import { useAnimation } from '../../hooks/useAnimation';
8
- import { Typography } from '../../Typography/Typography';
6
+ import readyclasses from '../../../readyclasses.module.scss';
7
+ import { useAnimation } from '../../../hooks/useAnimation';
8
+ import { Typography } from '../../../Typography/Typography';
9
9
 
10
10
  const textColor = 'var(--snackbar-text-color)';
11
11
 
@@ -92,7 +92,6 @@ export const SnackbarItem = ({
92
92
  <Icon icon={Icons.Times} color={textColor} />
93
93
  </IconButton>
94
94
  </div>
95
- {/* @TODO: change it to Typography*/}
96
95
  {!!content && (
97
96
  <Typography className={classes['content']} variant="body">
98
97
  {content}
@@ -3,7 +3,7 @@ import { createPortal } from 'react-dom';
3
3
  import { SnackbarContextProvider } from './SnackbarStateProvider';
4
4
  import { Actions, SnackbarOptionsProps, Variant } from '../interfaces';
5
5
  import { Placement, SnackbarContainer } from '../SnackbarContainer/SnackbarContainer';
6
- import { generateID } from '../../util/helper';
6
+ import { generateID } from '../../../util/helper';
7
7
  import { SnackbarItem } from '../SnackbarItem/SnackbarItem';
8
8
 
9
9
  /** Short msg is when only title is provided. Long one when content or/and actions are provided (or type is error). */
@@ -14,7 +14,7 @@ interface Duration {
14
14
 
15
15
  export interface Props {
16
16
  closeButtonTitle: string;
17
- children?: ReactNode | ReactNode[];
17
+ children?: ReactNode;
18
18
  placement?: Placement;
19
19
  stackSize?: number;
20
20
  domRoot?: HTMLElement;
@@ -1,4 +1,4 @@
1
- import React, { useRef } from 'react';
1
+ import React, { Fragment, useRef } from 'react';
2
2
  import { Popover, Props } from './Popover';
3
3
  import { render } from '@testing-library/react';
4
4
  import { usePosition } from '../hooks/usePosition';
@@ -32,7 +32,7 @@ const createPopover = (params?: (defaultParams: Props) => Props) => {
32
32
  });
33
33
 
34
34
  return (
35
- <>
35
+ <Fragment>
36
36
  <button data-testid="button" onClick={calculatePosition} ref={relativeElement}>
37
37
  Test
38
38
  </button>
@@ -44,7 +44,7 @@ const createPopover = (params?: (defaultParams: Props) => Props) => {
44
44
  >
45
45
  Test
46
46
  </Popover>
47
- </>
47
+ </Fragment>
48
48
  );
49
49
  };
50
50
 
@@ -7,6 +7,7 @@
7
7
  padding: 0 0 2rem;
8
8
  background-color: #fff;
9
9
  transition: box-shadow 0.2s ease-in-out;
10
+ font-family: var(--font-family);
10
11
 
11
12
  &:hover {
12
13
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.29);
@@ -14,7 +15,6 @@
14
15
 
15
16
  header {
16
17
  display: flex;
17
- justify-content: space-between;
18
18
  align-items: center;
19
19
 
20
20
  .icon {
@@ -10,7 +10,6 @@ import userEvent from '@testing-library/user-event';
10
10
  const onShow = jest.fn();
11
11
  const onClose = jest.fn();
12
12
  const contextMenuItemOnClick = jest.fn();
13
- const iconClick = jest.fn();
14
13
 
15
14
  const contextMenu = (
16
15
  <ContextMenu
@@ -38,8 +37,8 @@ const contextMenu = (
38
37
 
39
38
  const defaultParams: Props = {
40
39
  title: 'tile',
41
- iconProps: { icon: Icons.Bell, onClick: iconClick, 'data-testid': 'icon' },
42
- menu: contextMenu,
40
+ enabled: true,
41
+ tileAction: contextMenu,
43
42
  };
44
43
 
45
44
  const createTile = (params?: (defaultParams: Props) => Props) => {
@@ -54,25 +53,36 @@ const createTile = (params?: (defaultParams: Props) => Props) => {
54
53
  );
55
54
  const tile = queries.getByTestId('tile');
56
55
  const menutrigger = queries.getByTestId('contextmenu-trigger');
57
- const icon = queries.getByTestId('icon');
58
56
 
59
57
  return {
60
58
  ...queries,
61
59
  tile,
62
60
  menutrigger,
63
- icon,
64
61
  };
65
62
  };
66
63
 
67
64
  describe('Tile should render', () => {
68
- it('renders without crashing', () => {
69
- const { tile, icon } = createTile();
65
+ it('renders without crashing and enabled', () => {
66
+ const { tile } = createTile();
70
67
 
71
- userEvent.click(icon);
72
-
73
- expect(iconClick).toHaveBeenCalled();
68
+ expect(tile.querySelector('.icon-checkmark')).toBeTruthy();
69
+ expect(tile.querySelector('.icon-forbidden')).toBeFalsy();
74
70
  expect(tile).toBeDefined();
75
71
  });
72
+
73
+ it('renders disabled', () => {
74
+ const { tile } = createTile((defaultParams) => ({ ...defaultParams, enabled: false }));
75
+
76
+ expect(tile.querySelector('.icon-checkmark')).toBeFalsy();
77
+ expect(tile.querySelector('.icon-forbidden')).toBeTruthy();
78
+ });
79
+
80
+ it('renders no status', () => {
81
+ const { tile } = createTile((defaultParams) => ({ ...defaultParams, enabled: undefined }));
82
+
83
+ expect(tile.querySelector('.icon-checkmark')).toBeFalsy();
84
+ expect(tile.querySelector('.icon-forbidden')).toBeFalsy();
85
+ });
76
86
  });
77
87
 
78
88
  describe("should throw errors since we don't pass props", () => {
@@ -85,7 +95,7 @@ describe("should throw errors since we don't pass props", () => {
85
95
 
86
96
  try {
87
97
  // @ts-ignore: mandatory props (test for non-typescript react projects)
88
- render(<Tile imageProps={{ src: 'test', alt: 'test' }} />);
98
+ render(<Tile imageProps={{ src: 'test' }} />);
89
99
  } catch (e: any) {
90
100
  actual = e.message;
91
101
  }
@@ -1,48 +1,85 @@
1
- import React, { HTMLProps, ReactElement } from 'react';
2
- import { Icon, Icons, Props as IconProps } from '../Icon/Icon';
1
+ import React, { HTMLProps, ReactElement, useState } from 'react';
2
+ import { Icon, Icons } from '../Icon/Icon';
3
3
  import classes from './Tile.module.scss';
4
+ import readyClasses from '../readyclasses.module.scss';
4
5
 
5
6
  import { Props as ContextMenuProps } from '../ContextMenu/ContextMenu';
7
+ import { generateID } from '../util/helper';
8
+ import { Props as IconButtonProps } from '../Button/IconButton';
6
9
 
7
10
  interface ImageProps {
8
11
  src: string;
9
- alt: string;
10
12
  }
11
13
 
12
14
  export interface Props extends Omit<HTMLProps<HTMLDivElement>, 'contextMenu'> {
13
15
  title: string;
14
16
  imageProps?: ImageProps;
15
- iconProps?: IconProps;
17
+ enabled?: boolean;
16
18
  loading?: boolean;
17
- menu?: ReactElement<ContextMenuProps>;
19
+ tileAction?: ReactElement<ContextMenuProps> | ReactElement<IconButtonProps>;
18
20
  }
19
21
 
20
22
  export const Tile = ({
21
23
  title,
22
24
  imageProps,
23
- iconProps,
25
+ enabled,
24
26
  className,
25
27
  loading,
26
- menu,
28
+ tileAction,
27
29
  ...rest
28
30
  }: Props) => {
31
+ const [tileDescriptionID] = useState(generateID(20));
32
+
29
33
  if (!title) {
30
34
  throw new Error('Please make sure to pass a title prop to your Tile component.');
31
35
  }
32
36
 
37
+ const statusMessage = () => {
38
+ if (enabled) {
39
+ return 'Status: enabled';
40
+ }
41
+
42
+ return 'Status: disabled';
43
+ };
44
+
33
45
  return (
34
- <div {...rest} className={`${classes['tile']} ${loading ? classes['loading'] : ''}`}>
35
- <header>
36
- {iconProps && <Icon {...iconProps} className={`${classes['icon']} ${className ?? ''}`} />}
37
- {menu ?? null}
46
+ <article
47
+ tabIndex={0}
48
+ aria-labelledby={tileDescriptionID}
49
+ {...rest}
50
+ className={`${classes['tile']} ${loading ? classes['loading'] : ''}`}
51
+ >
52
+ <header style={{ justifyContent: enabled === undefined ? 'flex-end' : 'space-between' }}>
53
+ {enabled === true && (
54
+ <Icon
55
+ color="var(--success)"
56
+ icon={Icons.Checkmark}
57
+ className={`${classes['icon']} ${className ?? ''}`}
58
+ />
59
+ )}
60
+ {enabled === false && (
61
+ <Icon
62
+ color="var(--greyed-out)"
63
+ icon={Icons.Forbidden}
64
+ className={`${classes['icon']} ${className ?? ''}`}
65
+ />
66
+ )}
67
+ {enabled !== undefined && (
68
+ <span id={tileDescriptionID} className={readyClasses['sr-only']}>
69
+ {`${title}. ${statusMessage()}`}
70
+ </span>
71
+ )}
72
+ {tileAction ?? null}
38
73
  </header>
39
74
  <div className={classes['content']}>
40
- {imageProps && imageProps.src && imageProps.alt && (
41
- <figure className={classes['image']}>{!loading && <img {...imageProps} />}</figure>
75
+ {imageProps && imageProps.src.length > 0 && (
76
+ <figure className={classes['image']}>{!loading && <img {...imageProps} alt="" />}</figure>
77
+ )}
78
+ {(!imageProps || imageProps.src.length === 0) && (
79
+ <Icon className={classes['placeholder']} icon={Icons.Image} />
42
80
  )}
43
- {!imageProps && <Icon className={classes['placeholder']} icon={Icons.Image} />}
44
81
  <span className={classes['title']}>{title}</span>
45
82
  </div>
46
- </div>
83
+ </article>
47
84
  );
48
85
  };
@@ -34,6 +34,12 @@ const contextMenu = (
34
34
  </ContextMenu>
35
35
  );
36
36
 
37
+ const addToFavoriteButton = (
38
+ <IconButton title="Add to favorite">
39
+ <Icon icon={Icons.Star} />
40
+ </IconButton>
41
+ );
42
+
37
43
  const defaultParams: Props = {
38
44
  children: [
39
45
  <Tile
@@ -42,10 +48,9 @@ const defaultParams: Props = {
42
48
  title="Tile1"
43
49
  imageProps={{
44
50
  src: 'https://www.onegini.com/hubfs/OneWelcome_Beeldmerk.svg',
45
- alt: 'OneWelcome logo',
46
51
  }}
47
- iconProps={{ icon: Icons.Checkmark }}
48
- menu={contextMenu}
52
+ enabled={true}
53
+ tileAction={contextMenu}
49
54
  />,
50
55
  <Tile
51
56
  data-testid="tile"
@@ -53,10 +58,9 @@ const defaultParams: Props = {
53
58
  title="Tile2"
54
59
  imageProps={{
55
60
  src: 'https://www.onegini.com/hubfs/OneWelcome_Beeldmerk.svg',
56
- alt: 'OneWelcome logo',
57
61
  }}
58
- iconProps={{ icon: Icons.Forbidden }}
59
- menu={contextMenu}
62
+ enabled={false}
63
+ tileAction={addToFavoriteButton}
60
64
  />,
61
65
  <Tile
62
66
  data-testid="tile"
@@ -64,10 +68,8 @@ const defaultParams: Props = {
64
68
  title="Tile3"
65
69
  imageProps={{
66
70
  src: 'https://www.onegini.com/hubfs/OneWelcome_Beeldmerk.svg',
67
- alt: 'OneWelcome logo',
68
71
  }}
69
- iconProps={{ icon: Icons.Checkmark }}
70
- menu={contextMenu}
72
+ tileAction={contextMenu}
71
73
  />,
72
74
  ],
73
75
  className: 'example-classname',
@@ -14,19 +14,19 @@ export const Tiles = ({ children, className, loading = false, ...rest }: Props)
14
14
  <Tile
15
15
  key="placeholder1"
16
16
  title="placeholder"
17
- imageProps={{ src: 'placeholder', alt: 'placeholder' }}
17
+ imageProps={{ src: 'placeholder' }}
18
18
  loading={true}
19
19
  />,
20
20
  <Tile
21
21
  key="placeholder2"
22
22
  title="placeholder"
23
- imageProps={{ src: 'placeholder', alt: 'placeholder' }}
23
+ imageProps={{ src: 'placeholder' }}
24
24
  loading={true}
25
25
  />,
26
26
  <Tile
27
27
  key="placeholder3"
28
28
  title="placeholder"
29
- imageProps={{ src: 'placeholder', alt: 'placeholder' }}
29
+ imageProps={{ src: 'placeholder' }}
30
30
  loading={true}
31
31
  />,
32
32
  ];