@thecb/components 11.11.0-beta.7 → 11.11.0-beta.8
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 +100 -48
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +100 -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 +99 -50
- 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,18 @@ const Tooltip = ({
|
|
|
33
35
|
arrowBottom: "-8px",
|
|
34
36
|
arrowLeft: "auto"
|
|
35
37
|
},
|
|
38
|
+
customTriggerRole,
|
|
36
39
|
backgroundColor = WHITE
|
|
37
40
|
}) => {
|
|
38
41
|
const closeTimeoutRef = useRef(null);
|
|
42
|
+
const containerRef = useRef(null);
|
|
43
|
+
|
|
39
44
|
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
40
45
|
const themeContext = useContext(ThemeContext);
|
|
41
46
|
const themeValues = createThemeValues(
|
|
42
47
|
themeContext,
|
|
43
48
|
fallbackValues,
|
|
44
|
-
|
|
49
|
+
TOOLTIP_THEME_SOURCE
|
|
45
50
|
);
|
|
46
51
|
|
|
47
52
|
const { top, right, bottom, left } = contentPosition;
|
|
@@ -59,38 +64,44 @@ const Tooltip = ({
|
|
|
59
64
|
}
|
|
60
65
|
};
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Renders the tooltip trigger element.
|
|
69
|
+
*
|
|
70
|
+
* When `hasCustomTrigger` is true, the provided child element is cloned and
|
|
71
|
+
* injected with the event handlers needed to control tooltip visibility:
|
|
72
|
+
* - onFocus/onBlur: open and close for keyboard users
|
|
73
|
+
* - onKeyDown: allows Escape to dismiss the tooltip
|
|
74
|
+
* - onTouchStart: open on tap for touch/mobile users (onFocus is unreliable on touch)
|
|
75
|
+
*
|
|
76
|
+
* Mouse interactions (hover) are handled at the container level via
|
|
77
|
+
* onMouseEnter/onMouseLeave, so they do not need to be injected here.
|
|
78
|
+
*
|
|
79
|
+
* Any existing event handlers on the child are preserved and called first,
|
|
80
|
+
* so the child's own behavior is not overridden.
|
|
81
|
+
*
|
|
82
|
+
* When no custom trigger is provided, a default ButtonWithAction is rendered
|
|
83
|
+
* using `triggerText` and `triggerButtonVariant`.
|
|
84
|
+
*/
|
|
85
|
+
const renderTrigger = () => {
|
|
86
|
+
if (hasCustomTrigger && !children) {
|
|
87
|
+
console.warn(
|
|
88
|
+
"Tooltip: children prop is required when hasCustomTrigger is true"
|
|
89
|
+
);
|
|
66
90
|
}
|
|
67
|
-
handleToggleTooltip(true);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const handleMouseLeave = () => {
|
|
71
|
-
closeTimeoutRef.current = setTimeout(() => {
|
|
72
|
-
handleToggleTooltip(false);
|
|
73
|
-
}, 300);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
return () => {
|
|
78
|
-
if (closeTimeoutRef.current) {
|
|
79
|
-
clearTimeout(closeTimeoutRef.current);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}, []);
|
|
83
91
|
|
|
84
|
-
const renderTrigger = () => {
|
|
85
92
|
if (hasCustomTrigger && children) {
|
|
86
93
|
const child = React.Children.only(children);
|
|
94
|
+
// Capture the child's existing handlers before overwriting
|
|
87
95
|
const {
|
|
88
96
|
onFocus: childOnFocus,
|
|
89
97
|
onBlur: childOnBlur,
|
|
90
|
-
onKeyDown: childOnKeyDown
|
|
98
|
+
onKeyDown: childOnKeyDown,
|
|
99
|
+
onTouchStart: childOnTouchStart
|
|
91
100
|
} = child.props ?? {};
|
|
101
|
+
|
|
92
102
|
return React.cloneElement(child, {
|
|
93
|
-
|
|
103
|
+
tabIndex: child.props?.tabIndex ?? 0,
|
|
104
|
+
style: { cursor: `pointer`, ...child.props?.style },
|
|
94
105
|
onFocus: e => {
|
|
95
106
|
childOnFocus?.(e);
|
|
96
107
|
handleToggleTooltip(true);
|
|
@@ -103,35 +114,73 @@ const Tooltip = ({
|
|
|
103
114
|
childOnKeyDown?.(e);
|
|
104
115
|
handleKeyDown(e);
|
|
105
116
|
},
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
onTouchStart: e => {
|
|
118
|
+
childOnTouchStart?.(e);
|
|
119
|
+
handleToggleTooltip(true);
|
|
120
|
+
},
|
|
121
|
+
role: customTriggerRole || child.props?.role,
|
|
122
|
+
"aria-describedby": tooltipID,
|
|
123
|
+
"data-qa": `tooltip-trigger-${tooltipID}`
|
|
108
124
|
});
|
|
125
|
+
} else {
|
|
126
|
+
return (
|
|
127
|
+
<ButtonWithAction
|
|
128
|
+
action={noop}
|
|
129
|
+
onKeyDown={handleKeyDown}
|
|
130
|
+
variant={triggerButtonVariant}
|
|
131
|
+
text={triggerText}
|
|
132
|
+
tabIndex={0}
|
|
133
|
+
ariaDescribedby={tooltipID}
|
|
134
|
+
onFocus={() => handleToggleTooltip(true)}
|
|
135
|
+
onBlur={() => handleToggleTooltip(false)}
|
|
136
|
+
onTouchStart={() => handleToggleTooltip(true)}
|
|
137
|
+
dataQa={`tooltip-trigger-${tooltipID}`}
|
|
138
|
+
extraStyles={`
|
|
139
|
+
color: ${themeValues.linkColor};
|
|
140
|
+
&:hover { color: ${themeValues.hoverColor}; text-decoration: none;}
|
|
141
|
+
&:active, &:focus { color: ${themeValues.activeColor};text-decoration: none;}
|
|
142
|
+
button, span, &:hover span { text-decoration: none; }
|
|
143
|
+
`}
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
109
146
|
}
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<ButtonWithAction
|
|
113
|
-
action={noop}
|
|
114
|
-
aria-describedby={tooltipID}
|
|
115
|
-
onKeyDown={handleKeyDown}
|
|
116
|
-
variant={triggerButtonVariant}
|
|
117
|
-
onFocus={() => handleToggleTooltip(true)}
|
|
118
|
-
onBlur={() => handleToggleTooltip(false)}
|
|
119
|
-
onTouchStart={() => handleToggleTooltip(true)}
|
|
120
|
-
data-qa={`tooltip-trigger-${tooltipID}`}
|
|
121
|
-
text={triggerText}
|
|
122
|
-
tabIndex="0"
|
|
123
|
-
extraStyles={`
|
|
124
|
-
color: ${themeValues.linkColor};
|
|
125
|
-
&:hover { color: ${themeValues.hoverColor}; text-decoration: none;}
|
|
126
|
-
&:active, &:focus { color: ${themeValues.activeColor};text-decoration: none;}
|
|
127
|
-
button, span, &:hover span { text-decoration: none; }
|
|
128
|
-
`}
|
|
129
|
-
/>
|
|
130
|
-
);
|
|
131
147
|
};
|
|
148
|
+
const handleMouseEnter = () => {
|
|
149
|
+
if (closeTimeoutRef.current) {
|
|
150
|
+
clearTimeout(closeTimeoutRef.current);
|
|
151
|
+
closeTimeoutRef.current = null;
|
|
152
|
+
}
|
|
153
|
+
handleToggleTooltip(true);
|
|
154
|
+
};
|
|
155
|
+
const handleMouseLeave = () => {
|
|
156
|
+
closeTimeoutRef.current = setTimeout(() => {
|
|
157
|
+
handleToggleTooltip(false);
|
|
158
|
+
}, 300);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Touch listener effect
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!tooltipOpen) return;
|
|
164
|
+
|
|
165
|
+
const handleOutsideTouch = e => {
|
|
166
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
167
|
+
setTooltipOpen(false);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
document.addEventListener("touchstart", handleOutsideTouch);
|
|
171
|
+
return () => document.removeEventListener("touchstart", handleOutsideTouch);
|
|
172
|
+
}, [tooltipOpen]);
|
|
173
|
+
|
|
174
|
+
// Unmount cleanup only
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
return () => {
|
|
177
|
+
if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
|
|
178
|
+
};
|
|
179
|
+
}, []);
|
|
132
180
|
|
|
133
181
|
return (
|
|
134
182
|
<Box
|
|
183
|
+
ref={containerRef}
|
|
135
184
|
padding="0"
|
|
136
185
|
extraStyles={`position: relative; ${containerExtraStyles}`}
|
|
137
186
|
onMouseEnter={handleMouseEnter}
|
|
@@ -162,11 +211,11 @@ const Tooltip = ({
|
|
|
162
211
|
minWidth={minWidth}
|
|
163
212
|
maxWidth={maxWidth}
|
|
164
213
|
>
|
|
165
|
-
{typeof content === "string" ? (
|
|
214
|
+
{typeof content === "string" && content !== "" ? (
|
|
166
215
|
<Text color={themeValues.textColor}>{content}</Text>
|
|
167
|
-
) : (
|
|
216
|
+
) : content !== undefined && content !== null ? (
|
|
168
217
|
content
|
|
169
|
-
)}
|
|
218
|
+
) : null}
|
|
170
219
|
<Box
|
|
171
220
|
padding="0"
|
|
172
221
|
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> &
|