@onewelcome/react-lib-components 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/Button/IconButton.d.ts +2 -1
  2. package/dist/DataGrid/DataGrid.d.ts +1 -0
  3. package/dist/DataGrid/DataGridActions/DataGridActions.d.ts +2 -1
  4. package/dist/Form/Form.d.ts +3 -3
  5. package/dist/Notifications/SlideInModal/SlideInModal.d.ts +1 -1
  6. package/dist/Tabs/Tab.d.ts +5 -9
  7. package/dist/Tabs/TabButton.d.ts +3 -6
  8. package/dist/Tabs/Tabs.d.ts +1 -2
  9. package/dist/hooks/useDebouncedCallback.d.ts +1 -0
  10. package/dist/index.d.ts +1 -0
  11. package/dist/react-lib-components.cjs.development.js +472 -395
  12. package/dist/react-lib-components.cjs.development.js.map +1 -1
  13. package/dist/react-lib-components.cjs.production.min.js +1 -1
  14. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  15. package/dist/react-lib-components.esm.js +473 -397
  16. package/dist/react-lib-components.esm.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/Breadcrumbs/Breadcrumbs.tsx +46 -38
  19. package/src/Button/BaseButton.tsx +23 -20
  20. package/src/Button/Button.module.scss +9 -0
  21. package/src/Button/Button.tsx +40 -40
  22. package/src/Button/IconButton.tsx +28 -28
  23. package/src/ContextMenu/ContextMenu.tsx +161 -160
  24. package/src/ContextMenu/ContextMenuItem.tsx +55 -49
  25. package/src/DataGrid/DataGrid.tsx +1 -0
  26. package/src/DataGrid/DataGridActions/DataGridActions.module.scss +1 -1
  27. package/src/DataGrid/DataGridActions/DataGridActions.test.tsx +4 -2
  28. package/src/DataGrid/DataGridActions/DataGridActions.tsx +95 -87
  29. package/src/DataGrid/DataGridActions/DataGridColumnsToggle.tsx +64 -64
  30. package/src/DataGrid/DataGridBody/DataGridCell.tsx +42 -44
  31. package/src/DataGrid/DataGridBody/DataGridRow.tsx +29 -29
  32. package/src/DataGrid/DataGridHeader/DataGridHeader.tsx +78 -78
  33. package/src/DataGrid/DataGridHeader/DataGridHeaderCell.tsx +48 -48
  34. package/src/Form/Checkbox/Checkbox.tsx +134 -130
  35. package/src/Form/Fieldset/Fieldset.tsx +81 -78
  36. package/src/Form/Form.tsx +9 -4
  37. package/src/Form/FormControl/FormControl.module.scss +1 -20
  38. package/src/Form/FormControl/FormControl.tsx +36 -35
  39. package/src/Form/FormGroup/FormGroup.tsx +62 -62
  40. package/src/Form/FormHelperText/FormHelperText.tsx +19 -18
  41. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +58 -53
  42. package/src/Form/Input/Input.tsx +90 -87
  43. package/src/Form/Label/Label.tsx +17 -16
  44. package/src/Form/Radio/Radio.tsx +91 -91
  45. package/src/Form/Select/Option.tsx +66 -60
  46. package/src/Form/Select/Select.tsx +207 -209
  47. package/src/Form/Textarea/Textarea.tsx +51 -53
  48. package/src/Form/Toggle/Toggle.tsx +26 -23
  49. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +51 -43
  50. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +112 -106
  51. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +67 -62
  52. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +42 -37
  53. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +94 -94
  54. package/src/Form/Wrapper/Wrapper/Wrapper.tsx +73 -73
  55. package/src/Icon/Icon.module.scss +1 -0
  56. package/src/Icon/Icon.tsx +19 -16
  57. package/src/Link/Link.tsx +68 -63
  58. package/src/Notifications/BaseModal/BaseModal.tsx +68 -68
  59. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.tsx +13 -10
  60. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.tsx +33 -25
  61. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.tsx +20 -17
  62. package/src/Notifications/Dialog/Dialog.tsx +83 -83
  63. package/src/Notifications/Dialog/DialogActions/DialogActions.tsx +17 -14
  64. package/src/Notifications/Dialog/DialogTitle/DialogTitle.tsx +15 -12
  65. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +40 -40
  66. package/src/Notifications/SlideInModal/SlideInModal.module.scss +5 -5
  67. package/src/Notifications/SlideInModal/SlideInModal.test.tsx +7 -2
  68. package/src/Notifications/SlideInModal/SlideInModal.tsx +47 -27
  69. package/src/Pagination/Pagination.tsx +169 -169
  70. package/src/Popover/Popover.module.scss +1 -0
  71. package/src/Popover/Popover.tsx +43 -33
  72. package/src/ProgressBar/ProgressBar.tsx +17 -14
  73. package/src/Skeleton/Skeleton.tsx +23 -20
  74. package/src/StatusIndicator/StatusIndicator.tsx +18 -15
  75. package/src/Tabs/{TabPanel.module.scss → Tab.module.scss} +1 -1
  76. package/src/Tabs/Tab.test.tsx +1 -39
  77. package/src/Tabs/Tab.tsx +16 -10
  78. package/src/Tabs/TabButton.module.scss +0 -4
  79. package/src/Tabs/TabButton.test.tsx +3 -31
  80. package/src/Tabs/TabButton.tsx +35 -49
  81. package/src/Tabs/Tabs.test.tsx +40 -33
  82. package/src/Tabs/Tabs.tsx +107 -101
  83. package/src/TextEllipsis/TextEllipsis.tsx +50 -41
  84. package/src/Tiles/Tile.tsx +58 -56
  85. package/src/Tiles/Tiles.tsx +44 -41
  86. package/src/Tooltip/Tooltip.tsx +101 -100
  87. package/src/Typography/Typography.tsx +47 -44
  88. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +55 -52
  89. package/src/Wizard/WizardSteps/WizardSteps.tsx +25 -22
  90. package/src/hooks/useDebouncedCallback.test.ts +140 -0
  91. package/src/hooks/useDebouncedCallback.tsx +32 -0
  92. package/src/index.ts +1 -0
  93. package/src/mixins.module.scss +2 -2
  94. package/src/util/helper.test.tsx +0 -28
  95. package/dist/Tabs/TabPanel.d.ts +0 -8
  96. package/src/Tabs/TabPanel.test.tsx +0 -92
  97. package/src/Tabs/TabPanel.tsx +0 -43
package/src/Tabs/Tabs.tsx CHANGED
@@ -17,149 +17,155 @@
17
17
  import React, {
18
18
  ComponentPropsWithRef,
19
19
  createRef,
20
- MutableRefObject,
20
+ ForwardRefRenderFunction,
21
+ ReactElement,
22
+ RefObject,
21
23
  useEffect,
22
24
  useRef,
23
25
  useState
24
26
  } from "react";
25
- import { generateID } from "../util/helper";
26
27
  import { Props as TabProps } from "./Tab";
27
28
  import { TabButton } from "./TabButton";
28
- import { TabPanel } from "./TabPanel";
29
29
  import classes from "./Tabs.module.scss";
30
30
 
31
31
  export interface Props extends ComponentPropsWithRef<"div"> {
32
32
  children: React.ReactElement<TabProps> | React.ReactElement<TabProps>[];
33
33
  selected?: number;
34
- "aria-label"?: string;
35
34
  onTabChange?: (index: number) => void;
36
35
  }
37
36
 
38
- export const Tabs = ({
39
- children,
40
- selected = 0,
41
- "aria-label": ariaLabel,
42
- onTabChange,
43
- className,
44
- ...rest
45
- }: Props) => {
37
+ const TabsComponent: ForwardRefRenderFunction<HTMLDivElement, Props> = (
38
+ { children, selected = 0, onTabChange, ...rest }: Props,
39
+ ref
40
+ ) => {
41
+ const [renderedButtons, setRenderedButtons] = useState<ReactElement[]>([]);
42
+ const [renderedTabs, setRenderedTabs] = useState<ReactElement[]>([]);
43
+ const [activeTabIndex, setActiveTabIndex] = useState(selected);
44
+ const [usingKeyboardNavigation, setUsingKeyboardNavigation] = useState(false);
45
+ const [indicatorStyles, setIndicatorStyles] = useState<{
46
+ width: number;
47
+ left: number;
48
+ top: number;
49
+ }>({
50
+ width: 0,
51
+ left: 0,
52
+ top: 0
53
+ });
54
+ const tabsRef = (ref as RefObject<HTMLDivElement>) || createRef<HTMLDivElement>();
46
55
  const indicatorRef = useRef<HTMLDivElement>(null);
47
- const [indicatorPosition, setIndicatorPosition] = useState({ left: 0, top: 0 });
48
- const [indicatorWidth, setIndicatorWidth] = useState(0);
49
56
 
50
- const [max] = useState(React.Children.count(children) - 1);
51
- const min = 0;
57
+ const handleKeyDown = (e: React.KeyboardEvent) => {
58
+ const totalAmountOfTabs = [children].flat().length;
59
+ if (!usingKeyboardNavigation) setUsingKeyboardNavigation(true);
60
+ if (totalAmountOfTabs === 1) return;
52
61
 
53
- const [selectedTab, setSelectedTab] = useState(Math.min(max, Math.max(min, selected)));
54
- const [focussedTab, setFocussedTab] = useState(-1);
55
- const [tabIds] = useState([...Array(max)].map(() => generateID()));
56
- const [tabPanelIds] = useState([...Array(max)].map(() => generateID()));
57
-
58
- useEffect(() => {
59
- setSelectedTab(Math.min(max, Math.max(min, selected)));
60
- setFocussedTab(-1);
61
- }, [selected]);
62
-
63
- useEffect(() => {
64
- calculateIndicatorPosition();
65
- }, [selectedTab]);
66
-
67
- const calculateIndicatorPosition = () => {
68
- const selectedTabButton = (
69
- tabButtons[selectedTab].ref as MutableRefObject<HTMLButtonElement | null>
70
- ).current as HTMLButtonElement;
71
-
72
- setIndicatorPosition({
73
- left: selectedTabButton.offsetLeft,
74
- top:
75
- selectedTabButton.offsetTop +
76
- selectedTabButton.offsetHeight -
77
- indicatorRef.current!.offsetHeight
78
- });
79
- setIndicatorWidth(selectedTabButton.offsetWidth);
80
- };
81
-
82
- const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
83
- // do not show focus unless we came here before
84
- let currentFocussedTab = focussedTab === -1 ? selectedTab : focussedTab;
85
-
86
- switch (e.code) {
87
- case "ArrowLeft":
88
- setFocussedTab(currentFocussedTab === min ? max : currentFocussedTab - 1);
89
- break;
62
+ switch (e.key) {
90
63
  case "ArrowRight":
91
- setFocussedTab(currentFocussedTab === max ? min : currentFocussedTab + 1);
64
+ setActiveTabIndex(currentIndex => {
65
+ return currentIndex + 1 > totalAmountOfTabs - 1 ? 0 : currentIndex + 1;
66
+ });
67
+ break;
68
+ case "ArrowLeft":
69
+ setActiveTabIndex(currentIndex => {
70
+ return currentIndex - 1 < 0 ? totalAmountOfTabs - 1 : currentIndex - 1;
71
+ });
92
72
  break;
93
73
  case "Home":
94
- setFocussedTab(min);
74
+ setActiveTabIndex(0);
95
75
  break;
96
76
  case "End":
97
- setFocussedTab(max);
98
- break;
99
- case "Space":
100
- case "Enter":
101
- activateTab(currentFocussedTab);
77
+ setActiveTabIndex(totalAmountOfTabs - 1);
102
78
  break;
79
+ default:
80
+ return;
103
81
  }
104
82
  };
105
83
 
106
- const handleBlur = () => {
107
- setFocussedTab(selectedTab);
84
+ const calculateIndicatorPosition = () => {
85
+ if (tabsRef.current) {
86
+ const selectedButton = tabsRef.current.querySelectorAll('button[role="tab"]')[
87
+ activeTabIndex
88
+ ] as HTMLElement;
89
+
90
+ setIndicatorStyles({
91
+ left: selectedButton.offsetLeft,
92
+ top:
93
+ selectedButton.offsetTop +
94
+ selectedButton.offsetHeight -
95
+ indicatorRef.current!.offsetHeight,
96
+ width: selectedButton.offsetWidth
97
+ });
98
+ }
108
99
  };
109
100
 
110
- const activateTab = (index: number) => {
111
- setSelectedTab(index);
112
- setFocussedTab(index);
113
- onTabChange && onTabChange(index);
114
- };
101
+ useEffect(() => {
102
+ if (tabsRef.current && renderedButtons.length) {
103
+ calculateIndicatorPosition();
104
+ }
105
+ }, [tabsRef.current, activeTabIndex, renderedButtons]);
115
106
 
116
- const tabButtons = React.Children.map(children, (child, index) =>
117
- React.createElement(TabButton, {
118
- ref: child.props.buttonRef || createRef<HTMLButtonElement>(),
119
- key: generateID(),
120
- tabId: tabIds[index],
121
- tabPanelId: tabPanelIds[index],
122
- selected: selectedTab === index,
123
- focussed: focussedTab === index,
124
- onTabButtonClick: () => activateTab(index),
125
- children: child.props.title
126
- })
127
- );
107
+ useEffect(() => {
108
+ onTabChange && onTabChange(activeTabIndex);
109
+ }, [activeTabIndex]);
128
110
 
129
- const tabPanels = React.Children.map(children, (child, index) =>
130
- React.createElement(TabPanel, {
131
- ref: child.props.panelRef || createRef<HTMLDivElement>(),
132
- key: generateID(),
133
- selected: selectedTab === index,
134
- tabId: tabIds[index],
135
- tabPanelId: tabPanelIds[index],
136
- children: child.props.children
137
- })
138
- );
111
+ useEffect(() => {
112
+ const buttons = React.Children.map(children, (child, index) => {
113
+ if (Object.prototype.hasOwnProperty.call(child.props, "title")) {
114
+ return React.createElement(TabButton, {
115
+ key: `${child.props.title.toLowerCase().replace(/\s/, "_")}_button`,
116
+ tabIndex: activeTabIndex === index ? 0 : -1,
117
+ "aria-selected": activeTabIndex === index,
118
+ focused: usingKeyboardNavigation && activeTabIndex === index,
119
+ tabActive: activeTabIndex === index,
120
+ "aria-controls": `tab_${index}`,
121
+ onClick: () => setActiveTabIndex(index),
122
+ children: child.props.title
123
+ });
124
+ }
125
+ return null;
126
+ });
127
+
128
+ setRenderedButtons(buttons);
129
+ }, [activeTabIndex]);
130
+
131
+ useEffect(() => {
132
+ const tabs = React.Children.map(children, (child, index) => {
133
+ if (Object.prototype.hasOwnProperty.call(child.props, "title")) {
134
+ return React.cloneElement(child, {
135
+ key: `${child.props.title.toLowerCase().replace(/\s/, "_")}_tab`,
136
+ tabIndex: activeTabIndex === index ? 0 : -1,
137
+ tabActive: activeTabIndex === index,
138
+ id: `tab_${index}`,
139
+ children: child.props.children
140
+ });
141
+ }
142
+
143
+ return null;
144
+ });
145
+
146
+ setRenderedTabs(tabs);
147
+ }, [activeTabIndex]);
139
148
 
140
149
  return (
141
- <div {...rest} className={`${classes["tabs"]} ${className ?? ""}`}>
142
- <div
143
- role="tablist"
144
- onKeyDown={handleKeyDown}
145
- onBlur={handleBlur}
146
- aria-label={ariaLabel}
147
- className={classes["tablist"]}
148
- >
150
+ <div {...rest} ref={tabsRef} className={`${classes["tabs"]} ${rest.className ?? ""}`}>
151
+ <div role="tablist" className={classes["tablist"]} onKeyDown={handleKeyDown}>
152
+ {renderedButtons}
149
153
  <div className={classes["tabdivider"]} />
150
- {tabButtons}
151
154
  <div
152
155
  className={classes["indicator"]}
153
156
  ref={indicatorRef}
154
157
  aria-hidden="true"
155
158
  tabIndex={-1}
156
159
  style={{
157
- width: indicatorWidth,
158
- ...indicatorPosition
160
+ width: indicatorStyles.width,
161
+ left: indicatorStyles.left,
162
+ top: indicatorStyles.top
159
163
  }}
160
164
  />
161
165
  </div>
162
- <div>{tabPanels}</div>
166
+ <div>{renderedTabs}</div>
163
167
  </div>
164
168
  );
165
169
  };
170
+
171
+ export const Tabs = React.forwardRef(TabsComponent);
@@ -14,7 +14,13 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import React, { useRef, MouseEventHandler, useState, ComponentPropsWithRef } from "react";
17
+ import React, {
18
+ ForwardRefRenderFunction,
19
+ useRef,
20
+ MouseEventHandler,
21
+ useState,
22
+ ComponentPropsWithRef
23
+ } from "react";
18
24
  import { Popover } from "../Popover/Popover";
19
25
  import classes from "./TextEllipsis.module.scss";
20
26
 
@@ -23,49 +29,52 @@ export interface Props extends ComponentPropsWithRef<"div"> {
23
29
  popoverClassName?: string;
24
30
  }
25
31
 
26
- export const TextEllipsis = React.forwardRef<HTMLDivElement, Props>(
27
- ({ children, popoverClassName, className, ...rest }: Props, ref) => {
28
- const [showPopover, setShowPopover] = useState(false);
29
- const textContainer = useRef<HTMLDivElement>(null);
32
+ const TextEllipsisComponent: ForwardRefRenderFunction<HTMLDivElement, Props> = (
33
+ { children, popoverClassName, className, ...rest }: Props,
34
+ ref
35
+ ) => {
36
+ const [showPopover, setShowPopover] = useState(false);
37
+ const textContainer = useRef<HTMLDivElement>(null);
30
38
 
31
- const ellipsisVisible = () => {
32
- if (
33
- textContainer.current &&
34
- textContainer.current.offsetWidth < textContainer.current.scrollWidth
35
- ) {
36
- return true;
37
- }
38
- return false;
39
- };
39
+ const ellipsisVisible = () => {
40
+ if (
41
+ textContainer.current &&
42
+ textContainer.current.offsetWidth < textContainer.current.scrollWidth
43
+ ) {
44
+ return true;
45
+ }
46
+ return false;
47
+ };
40
48
 
41
- const onMouseEnter: MouseEventHandler<HTMLDivElement> = () => {
42
- ellipsisVisible() && setShowPopover(true);
43
- };
49
+ const onMouseEnter: MouseEventHandler<HTMLDivElement> = () => {
50
+ ellipsisVisible() && setShowPopover(true);
51
+ };
44
52
 
45
- const onMouseLeave: MouseEventHandler<HTMLDivElement> = () => {
46
- ellipsisVisible() && setShowPopover(false);
47
- };
53
+ const onMouseLeave: MouseEventHandler<HTMLDivElement> = () => {
54
+ ellipsisVisible() && setShowPopover(false);
55
+ };
48
56
 
49
- return (
50
- <div
51
- {...rest}
52
- onMouseEnter={onMouseEnter}
53
- onMouseLeave={onMouseLeave}
54
- className={`${classes["text-ellipsis"]} ${className ?? ""}`}
55
- ref={ref || textContainer}
57
+ return (
58
+ <div
59
+ {...rest}
60
+ onMouseEnter={onMouseEnter}
61
+ onMouseLeave={onMouseLeave}
62
+ className={`${classes["text-ellipsis"]} ${className ?? ""}`}
63
+ ref={ref || textContainer}
64
+ >
65
+ {children}
66
+ <Popover
67
+ aria-hidden={true}
68
+ data-hidden={!showPopover}
69
+ show={showPopover}
70
+ role="tooltip"
71
+ anchorEl={textContainer}
72
+ className={`${classes.popover} ${popoverClassName ?? ""}`}
56
73
  >
57
74
  {children}
58
- <Popover
59
- aria-hidden={true}
60
- data-hidden={!showPopover}
61
- show={showPopover}
62
- role="tooltip"
63
- anchorEl={textContainer}
64
- className={`${classes.popover} ${popoverClassName ?? ""}`}
65
- >
66
- {children}
67
- </Popover>
68
- </div>
69
- );
70
- }
71
- );
75
+ </Popover>
76
+ </div>
77
+ );
78
+ };
79
+
80
+ export const TextEllipsis = React.forwardRef(TextEllipsisComponent);
@@ -17,6 +17,7 @@
17
17
  import React, {
18
18
  ComponentPropsWithoutRef,
19
19
  ComponentPropsWithRef,
20
+ ForwardRefRenderFunction,
20
21
  ReactElement,
21
22
  useState
22
23
  } from "react";
@@ -40,64 +41,65 @@ export interface Props extends ComponentPropsWithRef<"div"> {
40
41
  tileAction?: ReactElement<ContextMenuProps> | ReactElement<IconButtonProps>;
41
42
  }
42
43
 
43
- export const Tile = React.forwardRef<HTMLDivElement, Props>(
44
- ({ title, imageProps, enabled, className, loading, tileAction, ...rest }: Props, ref) => {
45
- const [tileDescriptionID] = useState(generateID(20));
44
+ const TileComponent: ForwardRefRenderFunction<HTMLDivElement, Props> = (
45
+ { title, imageProps, enabled, className, loading, tileAction, ...rest }: Props,
46
+ ref
47
+ ) => {
48
+ const [tileDescriptionID] = useState(generateID(20));
46
49
 
47
- if (!title) {
48
- throw new Error("Please make sure to pass a title prop to your Tile component.");
50
+ if (!title) {
51
+ throw new Error("Please make sure to pass a title prop to your Tile component.");
52
+ }
53
+
54
+ const statusMessage = () => {
55
+ if (enabled) {
56
+ return "Status: enabled";
49
57
  }
50
58
 
51
- const statusMessage = () => {
52
- if (enabled) {
53
- return "Status: enabled";
54
- }
59
+ return "Status: disabled";
60
+ };
55
61
 
56
- return "Status: disabled";
57
- };
62
+ return (
63
+ <article
64
+ {...rest}
65
+ tabIndex={0}
66
+ aria-labelledby={tileDescriptionID}
67
+ ref={ref}
68
+ className={`${classes["tile"]} ${loading ? classes["loading"] : ""}`}
69
+ >
70
+ <header style={{ justifyContent: enabled === undefined ? "flex-end" : "space-between" }}>
71
+ {enabled === true && (
72
+ <Icon
73
+ color="var(--success)"
74
+ icon={Icons.Checkmark}
75
+ className={`${classes["icon"]} ${className ?? ""}`}
76
+ />
77
+ )}
78
+ {enabled === false && (
79
+ <Icon
80
+ color="var(--greyed-out)"
81
+ icon={Icons.Forbidden}
82
+ className={`${classes["icon"]} ${className ?? ""}`}
83
+ />
84
+ )}
85
+ {enabled !== undefined && (
86
+ <span id={tileDescriptionID} className={readyClasses["sr-only"]}>
87
+ {`${title}. ${statusMessage()}`}
88
+ </span>
89
+ )}
90
+ {tileAction ?? null}
91
+ </header>
92
+ <div className={classes["content"]}>
93
+ {imageProps && imageProps.src.length > 0 && (
94
+ <figure className={classes["image"]}>{!loading && <img {...imageProps} alt="" />}</figure>
95
+ )}
96
+ {(!imageProps || imageProps.src.length === 0) && (
97
+ <Icon className={classes["placeholder"]} icon={Icons.Image} />
98
+ )}
99
+ <span className={classes["title"]}>{title}</span>
100
+ </div>
101
+ </article>
102
+ );
103
+ };
58
104
 
59
- return (
60
- <article
61
- {...rest}
62
- tabIndex={0}
63
- aria-labelledby={tileDescriptionID}
64
- ref={ref}
65
- className={`${classes["tile"]} ${loading ? classes["loading"] : ""}`}
66
- >
67
- <header style={{ justifyContent: enabled === undefined ? "flex-end" : "space-between" }}>
68
- {enabled === true && (
69
- <Icon
70
- color="var(--success)"
71
- icon={Icons.Checkmark}
72
- className={`${classes["icon"]} ${className ?? ""}`}
73
- />
74
- )}
75
- {enabled === false && (
76
- <Icon
77
- color="var(--greyed-out)"
78
- icon={Icons.Forbidden}
79
- className={`${classes["icon"]} ${className ?? ""}`}
80
- />
81
- )}
82
- {enabled !== undefined && (
83
- <span id={tileDescriptionID} className={readyClasses["sr-only"]}>
84
- {`${title}. ${statusMessage()}`}
85
- </span>
86
- )}
87
- {tileAction ?? null}
88
- </header>
89
- <div className={classes["content"]}>
90
- {imageProps && imageProps.src.length > 0 && (
91
- <figure className={classes["image"]}>
92
- {!loading && <img {...imageProps} alt="" />}
93
- </figure>
94
- )}
95
- {(!imageProps || imageProps.src.length === 0) && (
96
- <Icon className={classes["placeholder"]} icon={Icons.Image} />
97
- )}
98
- <span className={classes["title"]}>{title}</span>
99
- </div>
100
- </article>
101
- );
102
- }
103
- );
105
+ export const Tile = React.forwardRef(TileComponent);
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import React, { ComponentPropsWithRef, ReactNode } from "react";
17
+ import React, { ForwardRefRenderFunction, ComponentPropsWithRef, ReactNode } from "react";
18
18
  import classes from "./Tiles.module.scss";
19
19
  import { Tile } from "./Tile";
20
20
 
@@ -23,45 +23,48 @@ export interface Props extends ComponentPropsWithRef<"div"> {
23
23
  loading?: boolean;
24
24
  }
25
25
 
26
- export const Tiles = React.forwardRef<HTMLDivElement, Props>(
27
- ({ children, className, loading = false, ...rest }: Props, ref) => {
28
- const renderChildren = () => {
29
- if (loading) {
30
- return [
31
- <Tile
32
- key="placeholder1"
33
- title="placeholder"
34
- imageProps={{ src: "placeholder" }}
35
- loading={true}
36
- />,
37
- <Tile
38
- key="placeholder2"
39
- title="placeholder"
40
- imageProps={{ src: "placeholder" }}
41
- loading={true}
42
- />,
43
- <Tile
44
- key="placeholder3"
45
- title="placeholder"
46
- imageProps={{ src: "placeholder" }}
47
- loading={true}
48
- />
49
- ];
50
- }
26
+ const TilesComponent: ForwardRefRenderFunction<HTMLDivElement, Props> = (
27
+ { children, className, loading = false, ...rest }: Props,
28
+ ref
29
+ ) => {
30
+ const renderChildren = () => {
31
+ if (loading) {
32
+ return [
33
+ <Tile
34
+ key="placeholder1"
35
+ title="placeholder"
36
+ imageProps={{ src: "placeholder" }}
37
+ loading={true}
38
+ />,
39
+ <Tile
40
+ key="placeholder2"
41
+ title="placeholder"
42
+ imageProps={{ src: "placeholder" }}
43
+ loading={true}
44
+ />,
45
+ <Tile
46
+ key="placeholder3"
47
+ title="placeholder"
48
+ imageProps={{ src: "placeholder" }}
49
+ loading={true}
50
+ />
51
+ ];
52
+ }
51
53
 
52
- return children;
53
- };
54
+ return children;
55
+ };
54
56
 
55
- return (
56
- <div
57
- {...rest}
58
- ref={ref}
59
- className={`${classes["tiles"]} ${className ?? ""}`}
60
- aria-live="polite"
61
- aria-busy={loading}
62
- >
63
- {renderChildren()}
64
- </div>
65
- );
66
- }
67
- );
57
+ return (
58
+ <div
59
+ {...rest}
60
+ ref={ref}
61
+ className={`${classes["tiles"]} ${className ?? ""}`}
62
+ aria-live="polite"
63
+ aria-busy={loading}
64
+ >
65
+ {renderChildren()}
66
+ </div>
67
+ );
68
+ };
69
+
70
+ export const Tiles = React.forwardRef(TilesComponent);