@thecb/components 11.11.0-beta.7 → 11.11.0-beta.9
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/dist/index.cjs.js +130 -48
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +130 -48
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/molecules/editable-list/EditableList.js +3 -0
- package/src/components/molecules/tooltip/Tooltip.js +127 -49
- package/src/components/molecules/tooltip/Tooltip.stories.js +20 -9
- package/src/components/molecules/tooltip/index.d.ts +2 -1
package/package.json
CHANGED
|
@@ -23,6 +23,7 @@ const EditableList = ({
|
|
|
23
23
|
titleWeight = "400",
|
|
24
24
|
canAdd = true,
|
|
25
25
|
addItem,
|
|
26
|
+
addItemDestination,
|
|
26
27
|
removeItem,
|
|
27
28
|
editItem,
|
|
28
29
|
itemName,
|
|
@@ -168,6 +169,8 @@ const EditableList = ({
|
|
|
168
169
|
<Box padding={items.length === 0 ? "0" : "1rem 0 0"}>
|
|
169
170
|
<Placeholder
|
|
170
171
|
text={addText}
|
|
172
|
+
isLink={!!addItemDestination}
|
|
173
|
+
destination={addItemDestination}
|
|
171
174
|
action={addItem}
|
|
172
175
|
dataQa={"Add " + qaPrefix}
|
|
173
176
|
aria-label={addText}
|
|
@@ -8,6 +8,8 @@ import ButtonWithAction from "../../atoms/button-with-action";
|
|
|
8
8
|
import { noop, arrowBorder } from "../../../util/general";
|
|
9
9
|
import { fallbackValues } from "./Tooltip.theme";
|
|
10
10
|
|
|
11
|
+
const TOOLTIP_THEME_SOURCE = "Popover";
|
|
12
|
+
|
|
11
13
|
const Tooltip = ({
|
|
12
14
|
tooltipID,
|
|
13
15
|
children,
|
|
@@ -33,15 +35,24 @@ const Tooltip = ({
|
|
|
33
35
|
arrowBottom: "-8px",
|
|
34
36
|
arrowLeft: "auto"
|
|
35
37
|
},
|
|
38
|
+
customTriggerRole,
|
|
36
39
|
backgroundColor = WHITE
|
|
37
40
|
}) => {
|
|
41
|
+
/**
|
|
42
|
+
* closeTimeoutRef is used internally to store a timer ID for delaying tooltip close. It will have a `.current` property (the timer ID) when the effect or event handlers set it.
|
|
43
|
+
*/
|
|
38
44
|
const closeTimeoutRef = useRef(null);
|
|
45
|
+
/**
|
|
46
|
+
* containerRef is used to store a reference to the container element. It will have a `.current` property (the container element) when the effect or event handlers set it.
|
|
47
|
+
*/
|
|
48
|
+
const containerRef = useRef(null);
|
|
49
|
+
|
|
39
50
|
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
40
51
|
const themeContext = useContext(ThemeContext);
|
|
41
52
|
const themeValues = createThemeValues(
|
|
42
53
|
themeContext,
|
|
43
54
|
fallbackValues,
|
|
44
|
-
|
|
55
|
+
TOOLTIP_THEME_SOURCE
|
|
45
56
|
);
|
|
46
57
|
|
|
47
58
|
const { top, right, bottom, left } = contentPosition;
|
|
@@ -59,38 +70,50 @@ const Tooltip = ({
|
|
|
59
70
|
}
|
|
60
71
|
};
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
/**
|
|
74
|
+
* @function renderTrigger
|
|
75
|
+
* Renders the tooltip trigger element.
|
|
76
|
+
*
|
|
77
|
+
* When `hasCustomTrigger` is true, the provided child element is cloned and
|
|
78
|
+
* injected with the event handlers needed to control tooltip visibility:
|
|
79
|
+
* - onFocus/onBlur: open and close for keyboard users
|
|
80
|
+
* - onKeyDown: allows Escape to dismiss the tooltip
|
|
81
|
+
* - onTouchStart: open on tap for touch/mobile users (onFocus is unreliable on touch)
|
|
82
|
+
*
|
|
83
|
+
* Mouse interactions (hover) are handled at the container level via
|
|
84
|
+
* onMouseEnter/onMouseLeave, so they do not need to be injected here.
|
|
85
|
+
*
|
|
86
|
+
* Any existing event handlers on the child are preserved and called first,
|
|
87
|
+
* so the child's own behavior is not overridden.
|
|
88
|
+
*
|
|
89
|
+
* When no custom trigger is provided, a default ButtonWithAction is rendered
|
|
90
|
+
* using `triggerText` and `triggerButtonVariant`.
|
|
91
|
+
*/
|
|
92
|
+
const renderTrigger = () => {
|
|
93
|
+
if (hasCustomTrigger && !children) {
|
|
94
|
+
console.warn(
|
|
95
|
+
"Tooltip: children prop is required when hasCustomTrigger is true"
|
|
96
|
+
);
|
|
66
97
|
}
|
|
67
|
-
handleToggleTooltip(true);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const handleMouseLeave = () => {
|
|
71
|
-
closeTimeoutRef.current = setTimeout(() => {
|
|
72
|
-
handleToggleTooltip(false);
|
|
73
|
-
}, 300);
|
|
74
|
-
};
|
|
75
98
|
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
return () => {
|
|
78
|
-
if (closeTimeoutRef.current) {
|
|
79
|
-
clearTimeout(closeTimeoutRef.current);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}, []);
|
|
83
|
-
|
|
84
|
-
const renderTrigger = () => {
|
|
85
99
|
if (hasCustomTrigger && children) {
|
|
86
100
|
const child = React.Children.only(children);
|
|
101
|
+
/**
|
|
102
|
+
* Capture the child's existing handlers before overwriting
|
|
103
|
+
*/
|
|
87
104
|
const {
|
|
88
105
|
onFocus: childOnFocus,
|
|
89
106
|
onBlur: childOnBlur,
|
|
90
|
-
onKeyDown: childOnKeyDown
|
|
107
|
+
onKeyDown: childOnKeyDown,
|
|
108
|
+
onTouchStart: childOnTouchStart
|
|
91
109
|
} = child.props ?? {};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Clone the child element and add the necessary event handlers
|
|
113
|
+
*/
|
|
92
114
|
return React.cloneElement(child, {
|
|
93
|
-
|
|
115
|
+
tabIndex: child.props?.tabIndex ?? 0,
|
|
116
|
+
style: { cursor: `pointer`, ...child.props?.style },
|
|
94
117
|
onFocus: e => {
|
|
95
118
|
childOnFocus?.(e);
|
|
96
119
|
handleToggleTooltip(true);
|
|
@@ -103,35 +126,90 @@ const Tooltip = ({
|
|
|
103
126
|
childOnKeyDown?.(e);
|
|
104
127
|
handleKeyDown(e);
|
|
105
128
|
},
|
|
106
|
-
|
|
107
|
-
|
|
129
|
+
onTouchStart: e => {
|
|
130
|
+
childOnTouchStart?.(e);
|
|
131
|
+
handleToggleTooltip(true);
|
|
132
|
+
},
|
|
133
|
+
role: customTriggerRole || child.props?.role,
|
|
134
|
+
"aria-describedby": tooltipID,
|
|
135
|
+
"data-qa": `tooltip-trigger-${tooltipID}`
|
|
108
136
|
});
|
|
137
|
+
} else {
|
|
138
|
+
return (
|
|
139
|
+
<ButtonWithAction
|
|
140
|
+
action={noop}
|
|
141
|
+
onKeyDown={handleKeyDown}
|
|
142
|
+
variant={triggerButtonVariant}
|
|
143
|
+
text={triggerText}
|
|
144
|
+
tabIndex={0}
|
|
145
|
+
ariaDescribedby={tooltipID}
|
|
146
|
+
onFocus={() => handleToggleTooltip(true)}
|
|
147
|
+
onBlur={() => handleToggleTooltip(false)}
|
|
148
|
+
onTouchStart={() => handleToggleTooltip(true)}
|
|
149
|
+
dataQa={`tooltip-trigger-${tooltipID}`}
|
|
150
|
+
extraStyles={`
|
|
151
|
+
color: ${themeValues.linkColor};
|
|
152
|
+
&:hover { color: ${themeValues.hoverColor}; text-decoration: none;}
|
|
153
|
+
&:active, &:focus { color: ${themeValues.activeColor};text-decoration: none;}
|
|
154
|
+
button, span, &:hover span { text-decoration: none; }
|
|
155
|
+
`}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
109
158
|
}
|
|
159
|
+
};
|
|
110
160
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
)
|
|
161
|
+
/**
|
|
162
|
+
* @function handleMouseEnter
|
|
163
|
+
* Handles the mouse enter event for the tooltip container.
|
|
164
|
+
* It clears any existing timeout and opens the tooltip.
|
|
165
|
+
*/
|
|
166
|
+
const handleMouseEnter = () => {
|
|
167
|
+
if (closeTimeoutRef.current) {
|
|
168
|
+
clearTimeout(closeTimeoutRef.current);
|
|
169
|
+
closeTimeoutRef.current = null;
|
|
170
|
+
}
|
|
171
|
+
handleToggleTooltip(true);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @function handleMouseLeave
|
|
176
|
+
* Handles the mouse leave event for the tooltip container.
|
|
177
|
+
* It sets a timeout to close the tooltip after 200ms.
|
|
178
|
+
*/
|
|
179
|
+
const handleMouseLeave = () => {
|
|
180
|
+
closeTimeoutRef.current = setTimeout(() => {
|
|
181
|
+
handleToggleTooltip(false);
|
|
182
|
+
}, 200);
|
|
131
183
|
};
|
|
132
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Handles the touch start event for the tooltip container.
|
|
187
|
+
* It closes the tooltip if the touch is outside the container.
|
|
188
|
+
*/
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
if (!tooltipOpen) return;
|
|
191
|
+
|
|
192
|
+
const handleOutsideTouch = e => {
|
|
193
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
194
|
+
setTooltipOpen(false);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
document.addEventListener("touchstart", handleOutsideTouch);
|
|
198
|
+
return () => document.removeEventListener("touchstart", handleOutsideTouch);
|
|
199
|
+
}, [tooltipOpen]);
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Cleans up the timeout when the component unmounts.
|
|
203
|
+
*/
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
return () => {
|
|
206
|
+
if (closeTimeoutRef?.current) clearTimeout(closeTimeoutRef.current);
|
|
207
|
+
};
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
133
210
|
return (
|
|
134
211
|
<Box
|
|
212
|
+
ref={containerRef}
|
|
135
213
|
padding="0"
|
|
136
214
|
extraStyles={`position: relative; ${containerExtraStyles}`}
|
|
137
215
|
onMouseEnter={handleMouseEnter}
|
|
@@ -162,11 +240,11 @@ const Tooltip = ({
|
|
|
162
240
|
minWidth={minWidth}
|
|
163
241
|
maxWidth={maxWidth}
|
|
164
242
|
>
|
|
165
|
-
{typeof content === "string" ? (
|
|
243
|
+
{typeof content === "string" && content !== "" ? (
|
|
166
244
|
<Text color={themeValues.textColor}>{content}</Text>
|
|
167
|
-
) : (
|
|
245
|
+
) : content !== undefined && content !== null ? (
|
|
168
246
|
content
|
|
169
|
-
)}
|
|
247
|
+
) : null}
|
|
170
248
|
<Box
|
|
171
249
|
padding="0"
|
|
172
250
|
extraStyles={`
|
|
@@ -52,6 +52,15 @@ const meta = {
|
|
|
52
52
|
defaultValue: { summary: false }
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
|
+
customTriggerRole: {
|
|
56
|
+
description:
|
|
57
|
+
"Role for the custom trigger element for accessibility purposes. Defaults to undefined.",
|
|
58
|
+
control: { type: "text" },
|
|
59
|
+
table: {
|
|
60
|
+
type: { summary: "string" },
|
|
61
|
+
defaultValue: { summary: undefined }
|
|
62
|
+
}
|
|
63
|
+
},
|
|
55
64
|
children: {
|
|
56
65
|
description:
|
|
57
66
|
"Optional trigger element. When provided, it replaces the default ButtonWithAction trigger. The child element will receive aria-describedby, focus, blur, and keydown handlers.",
|
|
@@ -135,7 +144,7 @@ const meta = {
|
|
|
135
144
|
description: "Maximum width of the tooltip content box.",
|
|
136
145
|
table: {
|
|
137
146
|
type: { summary: "string" },
|
|
138
|
-
defaultValue: { summary: "
|
|
147
|
+
defaultValue: { summary: "100%" }
|
|
139
148
|
}
|
|
140
149
|
},
|
|
141
150
|
height: {
|
|
@@ -222,14 +231,16 @@ export const RichTooltipContent = {
|
|
|
222
231
|
bottom: "auto",
|
|
223
232
|
left: "-225px"
|
|
224
233
|
},
|
|
225
|
-
content: (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
234
|
+
content: React.createElement("div", { style: { padding: "8px" } }, [
|
|
235
|
+
React.createElement("strong", null, "Bold title"),
|
|
236
|
+
React.createElement(
|
|
237
|
+
"p",
|
|
238
|
+
null,
|
|
239
|
+
"With ",
|
|
240
|
+
React.createElement("em", null, "an italic text detail"),
|
|
241
|
+
" below."
|
|
242
|
+
)
|
|
243
|
+
])
|
|
233
244
|
}
|
|
234
245
|
};
|
|
235
246
|
|
|
@@ -3,7 +3,7 @@ import Expand from "../../../util/expand";
|
|
|
3
3
|
|
|
4
4
|
export interface TooltipProps {
|
|
5
5
|
children?: React.ReactNode;
|
|
6
|
-
content:
|
|
6
|
+
content: React.ReactNode;
|
|
7
7
|
tooltipID: string;
|
|
8
8
|
hasCustomTrigger?: boolean;
|
|
9
9
|
triggerText?: string;
|
|
@@ -27,6 +27,7 @@ export interface TooltipProps {
|
|
|
27
27
|
containerExtraStyles?: string;
|
|
28
28
|
contentExtraStyles?: string;
|
|
29
29
|
backgroundColor?: string;
|
|
30
|
+
customTriggerRole?: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export const Tooltip: React.FC<Expand<TooltipProps> &
|