@rovula/ui 0.1.23 → 0.1.25
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/cjs/bundle.js +1 -1
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/patterns/menu/Menu.d.ts +2 -0
- package/dist/esm/bundle.js +1 -1
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/patterns/menu/Menu.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/patterns/menu/Menu.js +9 -4
- package/package.json +1 -1
- package/src/patterns/menu/Menu.tsx +29 -18
package/dist/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ function renderMenuItems(items, selectedValues, onSelect) {
|
|
|
23
23
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(val, opt);
|
|
24
24
|
(_a = opt.onClick) === null || _a === void 0 ? void 0 : _a.call(opt);
|
|
25
25
|
}
|
|
26
|
-
}, children: buffer.map((opt) => (_jsx(DropdownMenuRadioItem, { value: opt.value, disabled: opt.disabled, icon: opt.icon, children: opt.label }, opt.value))) }, key));
|
|
26
|
+
}, children: buffer.map((opt) => (_jsx(DropdownMenuRadioItem, { value: opt.value, disabled: opt.disabled, icon: opt.icon, "data-testid": opt.testId, children: opt.label }, opt.value))) }, key));
|
|
27
27
|
};
|
|
28
28
|
items.forEach((item, index) => {
|
|
29
29
|
var _a;
|
|
@@ -54,7 +54,7 @@ function renderMenuItems(items, selectedValues, onSelect) {
|
|
|
54
54
|
const opt = item.item;
|
|
55
55
|
const isSelected = (_a = opt.checked) !== null && _a !== void 0 ? _a : selectedValues.includes(opt.value);
|
|
56
56
|
if (opt.type === "checkbox") {
|
|
57
|
-
result.push(_jsx(DropdownMenuCheckboxItem, { checked: isSelected, disabled: opt.disabled, onCheckedChange: () => {
|
|
57
|
+
result.push(_jsx(DropdownMenuCheckboxItem, { checked: isSelected, disabled: opt.disabled, "data-testid": opt.testId, onCheckedChange: () => {
|
|
58
58
|
var _a;
|
|
59
59
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(opt.value, opt);
|
|
60
60
|
(_a = opt.onClick) === null || _a === void 0 ? void 0 : _a.call(opt);
|
|
@@ -62,7 +62,7 @@ function renderMenuItems(items, selectedValues, onSelect) {
|
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
// default item
|
|
65
|
-
result.push(_jsx(DropdownMenuItem, { selected: isSelected, icon: opt.icon, disabled: opt.disabled, className: cn(opt.danger && "text-red-500"), onSelect: () => {
|
|
65
|
+
result.push(_jsx(DropdownMenuItem, { selected: isSelected, icon: opt.icon, disabled: opt.disabled, className: cn(opt.danger && "text-red-500"), "data-testid": opt.testId, onSelect: () => {
|
|
66
66
|
var _a;
|
|
67
67
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(opt.value, opt);
|
|
68
68
|
(_a = opt.onClick) === null || _a === void 0 ? void 0 : _a.call(opt);
|
|
@@ -86,7 +86,12 @@ function renderMenuItems(items, selectedValues, onSelect) {
|
|
|
86
86
|
// - Full a11y / WAI-ARIA
|
|
87
87
|
// - Sub-menu support via DropdownMenuSub
|
|
88
88
|
// ---------------------------------------------------------------------------
|
|
89
|
-
export const Menu = ({ trigger, items, selectedValues = [], onSelect, header, open, onOpenChange, align = "start", side = "bottom", sideOffset = 4, contentClassName, testId, }) => (
|
|
89
|
+
export const Menu = ({ trigger, items, selectedValues = [], onSelect, header, open, onOpenChange, align = "start", side = "bottom", sideOffset = 4, contentClassName, testId, }) => (
|
|
90
|
+
// Stop click events from bubbling through React's portal event system.
|
|
91
|
+
// DropdownMenuContent renders in a DOM portal (document.body) but React
|
|
92
|
+
// synthetic events still bubble through the component tree, so clicks on
|
|
93
|
+
// menu items reach ancestor onClick handlers (e.g. a clickable card).
|
|
94
|
+
_jsx("div", { onClick: (e) => e.stopPropagation(), children: _jsxs(DropdownMenuRoot, { open: open, onOpenChange: onOpenChange, children: [trigger && (_jsx(DropdownMenuTrigger, { asChild: true, children: trigger })), _jsxs(DropdownMenuContent, { align: align, side: side, sideOffset: sideOffset, className: contentClassName, "data-testid": testId, children: [header && (_jsx("div", { className: "sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]", children: header })), renderMenuItems(items, selectedValues, onSelect)] })] }) }));
|
|
90
95
|
Menu.displayName = "Menu";
|
|
91
96
|
// ---------------------------------------------------------------------------
|
|
92
97
|
// Re-exports — backward compat for consumers using Menu's sub-components
|
package/package.json
CHANGED
|
@@ -36,6 +36,8 @@ export type MenuOption = {
|
|
|
36
36
|
danger?: boolean;
|
|
37
37
|
checked?: boolean;
|
|
38
38
|
onClick?: () => void;
|
|
39
|
+
/** data-testid attached to the item element */
|
|
40
|
+
testId?: string;
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
export type MenuItemType =
|
|
@@ -121,6 +123,7 @@ function renderMenuItems(
|
|
|
121
123
|
value={opt.value}
|
|
122
124
|
disabled={opt.disabled}
|
|
123
125
|
icon={opt.icon as React.ReactNode}
|
|
126
|
+
data-testid={opt.testId}
|
|
124
127
|
>
|
|
125
128
|
{opt.label}
|
|
126
129
|
</DropdownMenuRadioItem>
|
|
@@ -187,6 +190,7 @@ function renderMenuItems(
|
|
|
187
190
|
key={opt.value}
|
|
188
191
|
checked={isSelected}
|
|
189
192
|
disabled={opt.disabled}
|
|
193
|
+
data-testid={opt.testId}
|
|
190
194
|
onCheckedChange={() => {
|
|
191
195
|
onSelect?.(opt.value, opt);
|
|
192
196
|
opt.onClick?.();
|
|
@@ -206,6 +210,7 @@ function renderMenuItems(
|
|
|
206
210
|
icon={opt.icon as React.ReactNode}
|
|
207
211
|
disabled={opt.disabled}
|
|
208
212
|
className={cn(opt.danger && "text-red-500")}
|
|
213
|
+
data-testid={opt.testId}
|
|
209
214
|
onSelect={() => {
|
|
210
215
|
onSelect?.(opt.value, opt);
|
|
211
216
|
opt.onClick?.();
|
|
@@ -250,25 +255,31 @@ export const Menu = ({
|
|
|
250
255
|
contentClassName,
|
|
251
256
|
testId,
|
|
252
257
|
}: MenuProps) => (
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
className={contentClassName}
|
|
262
|
-
data-testid={testId}
|
|
263
|
-
>
|
|
264
|
-
{header && (
|
|
265
|
-
<div className="sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]">
|
|
266
|
-
{header}
|
|
267
|
-
</div>
|
|
258
|
+
// Stop click events from bubbling through React's portal event system.
|
|
259
|
+
// DropdownMenuContent renders in a DOM portal (document.body) but React
|
|
260
|
+
// synthetic events still bubble through the component tree, so clicks on
|
|
261
|
+
// menu items reach ancestor onClick handlers (e.g. a clickable card).
|
|
262
|
+
<div onClick={(e) => e.stopPropagation()}>
|
|
263
|
+
<DropdownMenuRoot open={open} onOpenChange={onOpenChange}>
|
|
264
|
+
{trigger && (
|
|
265
|
+
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
|
268
266
|
)}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
267
|
+
<DropdownMenuContent
|
|
268
|
+
align={align}
|
|
269
|
+
side={side}
|
|
270
|
+
sideOffset={sideOffset}
|
|
271
|
+
className={contentClassName}
|
|
272
|
+
data-testid={testId}
|
|
273
|
+
>
|
|
274
|
+
{header && (
|
|
275
|
+
<div className="sticky top-0 z-10 bg-modal-surface border-b border-[var(--dropdown-menu-seperator-bg)]">
|
|
276
|
+
{header}
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
{renderMenuItems(items, selectedValues, onSelect)}
|
|
280
|
+
</DropdownMenuContent>
|
|
281
|
+
</DropdownMenuRoot>
|
|
282
|
+
</div>
|
|
272
283
|
);
|
|
273
284
|
|
|
274
285
|
Menu.displayName = "Menu";
|