@bearmenu/ui 0.8.40 → 0.8.42
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/{chunk-TB2K5JN3.js → chunk-BFS4M7KD.js} +26 -9
- package/dist/{chunk-KGXTBTCJ.js → chunk-CLFE4U5T.js} +1 -1
- package/dist/{chunk-NF5UMLSL.js → chunk-FVFAOW36.js} +46 -40
- package/dist/{chunk-5SGKGFMN.js → chunk-K6ACNU2L.js} +6 -1
- package/dist/{chunk-ZUBBMLBD.js → chunk-RQPUBKFU.js} +1 -1
- package/dist/{chunk-FQAIYKAS.js → chunk-WHUG6QEI.js} +8 -2
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/booking-row.js +1 -1
- package/dist/components/button.d.ts +2 -2
- package/dist/components/button.js +1 -1
- package/dist/components/calendar.js +1 -1
- package/dist/components/carousel.js +1 -1
- package/dist/components/coach-marks.js +1 -1
- package/dist/components/combobox.js +1 -1
- package/dist/components/command.d.ts +3 -3
- package/dist/components/concept-convergence.js +1 -1
- package/dist/components/drawer.d.ts +9 -0
- package/dist/components/drawer.js +1 -1
- package/dist/components/empty-state.js +4 -2
- package/dist/components/event-detail-panel.js +1 -1
- package/dist/components/event-list-row-wide.js +1 -1
- package/dist/components/filter-category-row.js +1 -1
- package/dist/components/link-button.js +1 -1
- package/dist/components/menu-family-block.js +1 -1
- package/dist/components/menu-index-list.js +1 -1
- package/dist/components/menu-item.js +3 -3
- package/dist/components/multi-select-combobox.js +2 -2
- package/dist/components/pagination.js +1 -1
- package/dist/components/pan-zoom-viewport.js +1 -1
- package/dist/components/place-card.js +1 -1
- package/dist/components/place-details-mobile.js +1 -1
- package/dist/components/responsive-overlay.d.ts +18 -0
- package/dist/components/responsive-overlay.js +55 -30
- package/dist/components/searchable-select.js +1 -1
- package/dist/components/signature-chip.js +2 -2
- package/dist/components/sliding-panels.js +1 -1
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/tag.d.ts +2 -2
- package/dist/components/text.d.ts +2 -2
- package/dist/components/toggle-group.js +9 -5
- package/dist/components/toggle.d.ts +11 -2
- package/dist/components/toggle.js +1 -1
- package/dist/components/top-ranking-entry.js +3 -3
- package/package.json +1 -1
- package/src/components/badge.stories.tsx +22 -1
- package/src/components/badge.test.tsx +37 -0
- package/src/components/badge.tsx +8 -2
- package/src/components/button.test.tsx +10 -0
- package/src/components/button.tsx +6 -1
- package/src/components/drawer.stories.tsx +25 -0
- package/src/components/drawer.test.tsx +50 -0
- package/src/components/drawer.tsx +70 -47
- package/src/components/empty-state.test.tsx +8 -0
- package/src/components/empty-state.tsx +3 -1
- package/src/components/responsive-overlay.stories.tsx +3 -0
- package/src/components/responsive-overlay.test.tsx +93 -0
- package/src/components/responsive-overlay.tsx +52 -3
- package/src/components/toggle-group.stories.tsx +17 -0
- package/src/components/toggle-group.test.tsx +65 -0
- package/src/components/toggle-group.tsx +13 -5
- package/src/components/toggle.test.tsx +50 -1
- package/src/components/toggle.tsx +35 -4
- package/src/globals.css +7 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useMediaQuery, DESKTOP_MEDIA_QUERY } from '../chunk-OPVKL4VS.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { SheetTitle, SheetDescription, Sheet, SheetContent } from '../chunk-LAEVMNJN.js';
|
|
3
|
+
import { DrawerTitle, DrawerDescription, Drawer, DrawerContent } from '../chunk-FVFAOW36.js';
|
|
4
|
+
import { DialogTitle, DialogDescription, Dialog, DialogContent } from '../chunk-IQIJCMG2.js';
|
|
5
5
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
6
6
|
import { __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
7
7
|
import * as React from 'react';
|
|
8
8
|
import { X } from 'lucide-react';
|
|
9
|
-
import {
|
|
9
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
10
10
|
|
|
11
11
|
var MD_MEDIA_QUERY = "(min-width: 768px)";
|
|
12
12
|
var CONTAINER_TITLE = { drawer: DrawerTitle, dialog: DialogTitle, sheet: SheetTitle };
|
|
@@ -55,38 +55,60 @@ var ResponsiveOverlay = React.forwardRef(
|
|
|
55
55
|
modal = true,
|
|
56
56
|
wideFrom = "md",
|
|
57
57
|
className,
|
|
58
|
-
bodyClassName
|
|
58
|
+
bodyClassName,
|
|
59
|
+
initialFocusRef,
|
|
60
|
+
drawerHeight
|
|
59
61
|
}, ref) => {
|
|
60
62
|
const isWide = useMediaQuery(wideFrom === "lg" ? DESKTOP_MEDIA_QUERY : MD_MEDIA_QUERY);
|
|
61
63
|
const container = isWide ? desktop : "drawer";
|
|
62
64
|
useFocusReturn(open);
|
|
65
|
+
const handleOpenAutoFocus = React.useCallback(
|
|
66
|
+
(event) => {
|
|
67
|
+
if (!(initialFocusRef == null ? void 0 : initialFocusRef.current)) return;
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
initialFocusRef.current.focus();
|
|
70
|
+
},
|
|
71
|
+
[initialFocusRef]
|
|
72
|
+
);
|
|
63
73
|
const describedBy = subtitle ? {} : { "aria-describedby": void 0 };
|
|
64
74
|
const Title = CONTAINER_TITLE[container];
|
|
65
75
|
const Description = CONTAINER_DESCRIPTION[container];
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
const isDrawer = container === "drawer";
|
|
77
|
+
const header = /* @__PURE__ */ jsxs(
|
|
78
|
+
"div",
|
|
79
|
+
{
|
|
80
|
+
"data-slot": "responsive-overlay-header",
|
|
81
|
+
className: cn("flex items-start gap-3", isDrawer ? "px-4 pt-4" : "px-5 pt-5"),
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
84
|
+
/* @__PURE__ */ jsx(Title, { className: cn("font-bold leading-tight", isDrawer ? "text-[22px]" : "text-[19px]"), children: title }),
|
|
85
|
+
subtitle ? /* @__PURE__ */ jsx(Description, { className: "mt-1 text-[13px] text-muted-foreground", children: subtitle }) : null
|
|
86
|
+
] }),
|
|
87
|
+
/* @__PURE__ */ jsxs(
|
|
88
|
+
"button",
|
|
89
|
+
{
|
|
90
|
+
type: "button",
|
|
91
|
+
"data-slot": "responsive-overlay-close",
|
|
92
|
+
onClick: () => onOpenChange(false),
|
|
93
|
+
className: "-m-1.5 grid size-11 shrink-0 place-items-center rounded-full text-muted-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsx("span", { className: "grid size-8 place-items-center rounded-full bg-surface-2 transition-colors hover:bg-muted", children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": true }) }),
|
|
96
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: closeLabel })
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
);
|
|
85
103
|
const body = /* @__PURE__ */ jsx(
|
|
86
104
|
"div",
|
|
87
105
|
{
|
|
88
106
|
"data-slot": "responsive-overlay-body",
|
|
89
|
-
className: cn(
|
|
107
|
+
className: cn(
|
|
108
|
+
"min-h-0 flex-1 overflow-y-auto py-4",
|
|
109
|
+
isDrawer ? "px-4" : "px-5",
|
|
110
|
+
bodyClassName
|
|
111
|
+
),
|
|
90
112
|
children
|
|
91
113
|
}
|
|
92
114
|
);
|
|
@@ -106,7 +128,8 @@ var ResponsiveOverlay = React.forwardRef(
|
|
|
106
128
|
side: "right",
|
|
107
129
|
attachment: "inset",
|
|
108
130
|
hideClose: true,
|
|
109
|
-
overlayClassName: "bg-scrim-overlay backdrop-blur-none"
|
|
131
|
+
overlayClassName: "bg-scrim-overlay backdrop-blur-none",
|
|
132
|
+
onOpenAutoFocus: handleOpenAutoFocus
|
|
110
133
|
}, describedBy), {
|
|
111
134
|
"data-slot": "responsive-overlay-sheet",
|
|
112
135
|
"data-modal": modal ? "true" : "false"
|
|
@@ -130,7 +153,8 @@ var ResponsiveOverlay = React.forwardRef(
|
|
|
130
153
|
ref,
|
|
131
154
|
size,
|
|
132
155
|
showCloseButton: false,
|
|
133
|
-
overlayClassName: "bg-scrim-overlay backdrop-blur-none"
|
|
156
|
+
overlayClassName: "bg-scrim-overlay backdrop-blur-none",
|
|
157
|
+
onOpenAutoFocus: handleOpenAutoFocus
|
|
134
158
|
}, describedBy), {
|
|
135
159
|
className: cn(
|
|
136
160
|
// The dialog's own `grid gap-4 p-6` would put air between the
|
|
@@ -148,10 +172,11 @@ var ResponsiveOverlay = React.forwardRef(
|
|
|
148
172
|
}
|
|
149
173
|
return /* @__PURE__ */ jsx(Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
|
|
150
174
|
DrawerContent,
|
|
151
|
-
__spreadProps(__spreadValues({
|
|
175
|
+
__spreadProps(__spreadValues(__spreadValues({
|
|
152
176
|
ref,
|
|
153
|
-
overlayClassName: "bg-scrim-overlay"
|
|
154
|
-
|
|
177
|
+
overlayClassName: "bg-scrim-overlay",
|
|
178
|
+
onOpenAutoFocus: handleOpenAutoFocus
|
|
179
|
+
}, drawerHeight !== void 0 ? { snapHeight: drawerHeight } : {}), describedBy), {
|
|
155
180
|
className: cn("flex max-h-[85vh] flex-col p-0", className),
|
|
156
181
|
children: [
|
|
157
182
|
header,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Popover, PopoverTrigger, PopoverContent } from '../chunk-2ADCZXZJ.js';
|
|
2
2
|
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../chunk-PGQXVJXW.js';
|
|
3
3
|
import '../chunk-IQIJCMG2.js';
|
|
4
|
-
import { Button } from '../chunk-
|
|
4
|
+
import { Button } from '../chunk-K6ACNU2L.js';
|
|
5
5
|
import '../chunk-RIH2IS7X.js';
|
|
6
6
|
import '../chunk-5YNIQ2BL.js';
|
|
7
7
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
@@ -4,11 +4,11 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
|
|
5
5
|
declare const spinnerVariants: (props?: ({
|
|
6
6
|
size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
|
|
7
|
-
tone?: "
|
|
7
|
+
tone?: "default" | "inherit" | "primary" | "destructive" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare const Spinner: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
|
|
10
10
|
size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
|
|
11
|
-
tone?: "
|
|
11
|
+
tone?: "default" | "inherit" | "primary" | "destructive" | null | undefined;
|
|
12
12
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & {
|
|
13
13
|
/** Visually-hidden label for screen readers. Defaults to "Loading". */
|
|
14
14
|
label?: string;
|
package/dist/components/tag.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const tagVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "default" | "primary" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare const Tag: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
|
|
10
|
-
variant?: "
|
|
10
|
+
variant?: "default" | "primary" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
|
|
11
11
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
12
12
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & {
|
|
13
13
|
onRemove?: () => void;
|
|
@@ -22,8 +22,8 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
22
22
|
* Weights are 400 / 600 / 700. 500 is not in the system.
|
|
23
23
|
*/
|
|
24
24
|
declare const textVariants: (props?: ({
|
|
25
|
-
variant?: "
|
|
26
|
-
color?: "
|
|
25
|
+
variant?: "body" | "small" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
|
|
26
|
+
color?: "default" | "inherit" | "primary" | "destructive" | "muted" | "primary-foreground" | null | undefined;
|
|
27
27
|
weight?: "bold" | "medium" | "normal" | "semibold" | null | undefined;
|
|
28
28
|
leading?: "none" | "normal" | "tight" | "snug" | "relaxed" | "loose" | null | undefined;
|
|
29
29
|
tracking?: "normal" | "tight" | "wide" | "wider" | null | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toggleVariants } from '../chunk-
|
|
1
|
+
import { toggleVariants, SegmentedToggleContent } from '../chunk-BFS4M7KD.js';
|
|
2
2
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
3
3
|
import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
4
4
|
import * as React from 'react';
|
|
@@ -30,8 +30,12 @@ function ToggleGroup(_a) {
|
|
|
30
30
|
className: cn(
|
|
31
31
|
"group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
|
|
32
32
|
// The segmented control's capsule: muted ground, the on cell a raised
|
|
33
|
-
// card inside it (see `toggleVariants.segmented`).
|
|
34
|
-
|
|
33
|
+
// card inside it (see `toggleVariants.segmented`). No padding here —
|
|
34
|
+
// each cell is the full 44px capsule height (the hit target), and
|
|
35
|
+
// draws its own smaller 36px card centred inside
|
|
36
|
+
// (`SegmentedToggleContent`), so the capsule stays the 44px the
|
|
37
|
+
// booking frames draw it at.
|
|
38
|
+
"data-[variant=segmented]:gap-0.5 data-[variant=segmented]:rounded-full data-[variant=segmented]:bg-muted",
|
|
35
39
|
// Chips wrap onto as many rows as they need, 7px apart.
|
|
36
40
|
"data-[variant=chip]:flex-wrap data-[variant=chip]:gap-[7px] data-[variant=chip]:rounded-none",
|
|
37
41
|
// The tab bar spans its container: a muted capsule, 4px inset.
|
|
@@ -71,7 +75,7 @@ function ToggleGroupItem(_a) {
|
|
|
71
75
|
size: context.size || size
|
|
72
76
|
}),
|
|
73
77
|
"min-w-0 flex-1 shrink-0 shadow-none focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
|
|
74
|
-
resolved === "segmented" ? "rounded-full
|
|
78
|
+
resolved === "segmented" ? "rounded-full" : (
|
|
75
79
|
// Tabs and chips size themselves (`toggleVariants`' compounds);
|
|
76
80
|
// the item's own rounding must not square them off.
|
|
77
81
|
resolved === "tab" ? "rounded-full" : resolved === "chip" ? (
|
|
@@ -82,7 +86,7 @@ function ToggleGroupItem(_a) {
|
|
|
82
86
|
className
|
|
83
87
|
)
|
|
84
88
|
}, props), {
|
|
85
|
-
children
|
|
89
|
+
children: resolved === "segmented" ? /* @__PURE__ */ jsx(SegmentedToggleContent, { children }) : children
|
|
86
90
|
})
|
|
87
91
|
);
|
|
88
92
|
}
|
|
@@ -14,6 +14,15 @@ declare const toggleVariants: (props?: ({
|
|
|
14
14
|
variant?: "default" | "tab" | "outline" | "chip" | "segmented" | null | undefined;
|
|
15
15
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
16
16
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* The segmented variant's drawn pill — 36px tall, centred in the cell's 44px
|
|
19
|
+
* hit box. Painted here, on `group-data-[state=on]/segment:`, so the outer
|
|
20
|
+
* button (`toggleVariants({variant: "segmented"})`) can stay unpainted and
|
|
21
|
+
* full height. Shared by `Toggle` and `ToggleGroupItem`.
|
|
22
|
+
*/
|
|
23
|
+
declare function SegmentedToggleContent({ children }: {
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
}): react_jsx_runtime.JSX.Element;
|
|
26
|
+
declare function Toggle({ className, variant, size, children, ...props }: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>): react_jsx_runtime.JSX.Element;
|
|
18
27
|
|
|
19
|
-
export { Toggle, toggleVariants };
|
|
28
|
+
export { SegmentedToggleContent, Toggle, toggleVariants };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { MenuItem } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
import { MenuItem } from '../chunk-RQPUBKFU.js';
|
|
2
|
+
import '../chunk-CLFE4U5T.js';
|
|
3
3
|
import { Separator } from '../chunk-NWZGSLPU.js';
|
|
4
4
|
import { Skeleton } from '../chunk-XBBEX4SF.js';
|
|
5
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-WHUG6QEI.js';
|
|
6
6
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
7
7
|
import { __objRest, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
8
8
|
import { MapPin, Star, MessageSquareQuote } from 'lucide-react';
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Armchair, Check, ChevronDown, CircleHelp, Clock, Users } from "lucide-react";
|
|
2
|
+
import { Armchair, Check, ChevronDown, CircleCheck, CircleHelp, Clock, Users } from "lucide-react";
|
|
3
3
|
import { Avatar, AvatarFallback, AvatarGroup } from "./avatar";
|
|
4
4
|
import { Badge } from "./badge";
|
|
5
5
|
import { Button } from "./button";
|
|
@@ -91,6 +91,27 @@ export const BookingStatus: Story = {
|
|
|
91
91
|
),
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* `size="status"` — the taller pill beside a summary, not the inline row chip
|
|
96
|
+
* `BookingStatus` above draws. Measured off the booking details panel
|
|
97
|
+
* (Bookings Calendar 09): a 24px pill at 12px semibold, icon included.
|
|
98
|
+
*/
|
|
99
|
+
export const StatusSize: Story = {
|
|
100
|
+
render: () => (
|
|
101
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
102
|
+
<Badge as="span" variant="confirmed" size="status" icon={<CircleCheck />} className="gap-[5px]">
|
|
103
|
+
Confirmed
|
|
104
|
+
</Badge>
|
|
105
|
+
<Badge as="span" variant="pending" size="status">
|
|
106
|
+
Pending
|
|
107
|
+
</Badge>
|
|
108
|
+
<Badge as="span" variant="completed" size="status">
|
|
109
|
+
Completed
|
|
110
|
+
</Badge>
|
|
111
|
+
</div>
|
|
112
|
+
),
|
|
113
|
+
};
|
|
114
|
+
|
|
94
115
|
/**
|
|
95
116
|
* `assignee` — who an item belongs to, in the shared table's organise view. It
|
|
96
117
|
* is a control, not a status: the whole pill opens "Who had this?", so it keeps
|
|
@@ -42,4 +42,41 @@ describe("Badge", () => {
|
|
|
42
42
|
render(<Badge variant="success">Solid</Badge>);
|
|
43
43
|
expect(screen.getByText("Solid")).toHaveClass("bg-success", "text-success-foreground");
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
describe('size="status"', () => {
|
|
47
|
+
// Measured off the booking details panel (Bookings Calendar 09): a 24px
|
|
48
|
+
// pill (18px line-height + 3px top/bottom), not the old py-[11px]'s 38px.
|
|
49
|
+
it("draws a 24px pill, not the old 38px one", () => {
|
|
50
|
+
render(<Badge size="status">Confirmed</Badge>);
|
|
51
|
+
const badge = screen.getByText("Confirmed");
|
|
52
|
+
expect(badge).toHaveClass("py-[3px]", "leading-[18px]");
|
|
53
|
+
expect(badge).not.toHaveClass("py-[11px]");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("overrides a colour variant's compact, inline-row type scale", () => {
|
|
57
|
+
// `confirmed` alone is the small 10.5px bold row chip; `status` is the
|
|
58
|
+
// taller 12px semibold scale the booking details header draws, and
|
|
59
|
+
// must win since it is applied after the variant in the merge.
|
|
60
|
+
render(<Badge size="status">Confirmed</Badge>);
|
|
61
|
+
const badge = screen.getByText("Confirmed");
|
|
62
|
+
expect(badge).toHaveClass("text-xs", "font-semibold");
|
|
63
|
+
expect(badge.className).not.toContain("text-[10.5px]");
|
|
64
|
+
|
|
65
|
+
render(
|
|
66
|
+
<Badge variant="confirmed" size="status">
|
|
67
|
+
Confirmed
|
|
68
|
+
</Badge>
|
|
69
|
+
);
|
|
70
|
+
const withVariant = screen.getAllByText("Confirmed")[1];
|
|
71
|
+
expect(withVariant).toHaveClass("text-xs", "font-semibold", "py-[3px]");
|
|
72
|
+
expect(withVariant.className).not.toContain("text-[10.5px]");
|
|
73
|
+
expect(withVariant.className).not.toContain("font-bold");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("leaves the default chip size's compact padding alone", () => {
|
|
77
|
+
render(<Badge variant="confirmed">Confirmed</Badge>);
|
|
78
|
+
const badge = screen.getByText("Confirmed");
|
|
79
|
+
expect(badge).toHaveClass("py-[3px]", "text-[10.5px]", "font-bold");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
45
82
|
});
|
package/src/components/badge.tsx
CHANGED
|
@@ -78,11 +78,17 @@ const badgeVariants = cva(
|
|
|
78
78
|
/**
|
|
79
79
|
* `status` is the taller pill the booking design draws beside a
|
|
80
80
|
* summary — the same ink and radius, more air. `chip` (the default) is
|
|
81
|
-
* the inline one that sits in a row of text.
|
|
81
|
+
* the inline one that sits in a row of text. Measured off the booking
|
|
82
|
+
* details panel (Bookings Calendar 09, the header status badge): a 24px
|
|
83
|
+
* pill, not the 38px `py-[11px]` previously drew. Resets text size and
|
|
84
|
+
* weight too, because the colour variants (`confirmed`/`pending`/…)
|
|
85
|
+
* tune those for their compact, inline-with-a-row scale (10.5px bold);
|
|
86
|
+
* `status` is a different scale (12px semibold) and wins the merge by
|
|
87
|
+
* coming after the variant classes.
|
|
82
88
|
*/
|
|
83
89
|
size: {
|
|
84
90
|
chip: "",
|
|
85
|
-
status: "py-[
|
|
91
|
+
status: "gap-1 px-2 py-[3px] text-xs leading-[18px] font-semibold",
|
|
86
92
|
},
|
|
87
93
|
// v1.0 motion: "live" surface signal — applies `live-pulse` archetype.
|
|
88
94
|
// Pair with a colored variant (e.g. variant="destructive" live).
|
|
@@ -158,6 +158,16 @@ describe("Button component", () => {
|
|
|
158
158
|
expect(screen.getByRole("button").className).toContain("h-9");
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
+
// The booking frames' 44px controls (Reports/02-p4 "Try again" + its report-
|
|
162
|
+
// range trigger, Calendar/08-r0 "New reservation") draw a 10px radius, not
|
|
163
|
+
// the base `rounded-xl` (12px) this size otherwise inherits.
|
|
164
|
+
it("draws the default size at a 10px radius, not the base rounded-xl", () => {
|
|
165
|
+
render(<Button>x</Button>);
|
|
166
|
+
const classes = screen.getByRole("button").className;
|
|
167
|
+
expect(classes).toContain("rounded-[10px]");
|
|
168
|
+
expect(classes).not.toMatch(/(^|\s)rounded-xl(\s|$)/);
|
|
169
|
+
});
|
|
170
|
+
|
|
161
171
|
it("applies fullWidth class when fullWidth is true", () => {
|
|
162
172
|
render(<Button fullWidth>x</Button>);
|
|
163
173
|
expect(screen.getByRole("button").className).toContain("w-full");
|
|
@@ -75,7 +75,12 @@ const buttonVariants = cva(
|
|
|
75
75
|
size: {
|
|
76
76
|
xs: "h-8 rounded-lg px-3 text-xs [&_svg]:size-3.5",
|
|
77
77
|
sm: "h-9 rounded-lg px-4",
|
|
78
|
-
|
|
78
|
+
/**
|
|
79
|
+
* The booking frames' 44px controls (Reports/02-p4 "Try again" and its
|
|
80
|
+
* report-range trigger, Calendar/08-r0 "New reservation") all draw
|
|
81
|
+
* 10px, not the base `rounded-xl` (12px) this size otherwise inherits.
|
|
82
|
+
*/
|
|
83
|
+
default: "h-11 rounded-[10px] px-6 py-2",
|
|
79
84
|
lg: "h-12 rounded-2xl px-8",
|
|
80
85
|
/** The `ink` action's shape. Owns the radius so no variant fights it. */
|
|
81
86
|
pill: "h-11 rounded-full px-5 text-[13.5px] font-bold",
|
|
@@ -45,3 +45,28 @@ export const Default: Story = {
|
|
|
45
45
|
</Drawer>
|
|
46
46
|
),
|
|
47
47
|
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `snapHeight` — a fixed sheet instead of one that grows with its content,
|
|
51
|
+
* for a phone drawer the frames draw at a set height (the floor plan's table
|
|
52
|
+
* sheet, ~640px). Opt-in: every other drawer keeps sizing to its content.
|
|
53
|
+
*/
|
|
54
|
+
export const FixedHeight: Story = {
|
|
55
|
+
render: () => (
|
|
56
|
+
<Drawer>
|
|
57
|
+
<DrawerTrigger asChild>
|
|
58
|
+
<Button variant="outline">Open table sheet</Button>
|
|
59
|
+
</DrawerTrigger>
|
|
60
|
+
<DrawerContent snapHeight={420}>
|
|
61
|
+
<DrawerHeader>
|
|
62
|
+
<DrawerTitle>T5</DrawerTitle>
|
|
63
|
+
<DrawerDescription>6 seats · Inside</DrawerDescription>
|
|
64
|
+
</DrawerHeader>
|
|
65
|
+
<div className="flex-1 p-4 text-muted-foreground">
|
|
66
|
+
The sheet holds this height regardless of how little content sits
|
|
67
|
+
inside it.
|
|
68
|
+
</div>
|
|
69
|
+
</DrawerContent>
|
|
70
|
+
</Drawer>
|
|
71
|
+
),
|
|
72
|
+
};
|
|
@@ -52,4 +52,54 @@ describe("Drawer", () => {
|
|
|
52
52
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
53
53
|
expect(dialog.contains(document.activeElement)).toBe(false);
|
|
54
54
|
});
|
|
55
|
+
|
|
56
|
+
// Measured off the booking frames' table sheet (floor/14-w1): a 40×4px
|
|
57
|
+
// handle, not the previous 60×6.
|
|
58
|
+
it("draws the smaller handle by default", () => {
|
|
59
|
+
render(<Harness />);
|
|
60
|
+
const handle = document.querySelector('[data-slot="drawer-content"] > [aria-hidden]');
|
|
61
|
+
expect(handle).toHaveClass("h-1", "w-10");
|
|
62
|
+
expect(handle).not.toHaveClass("h-1.5", "w-[60px]");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("snapHeight", () => {
|
|
66
|
+
it("is content-sized (no inline height) when omitted", () => {
|
|
67
|
+
render(<Harness />);
|
|
68
|
+
const dialog = screen.getByRole("dialog");
|
|
69
|
+
expect(dialog.style.height).toBe("");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("sets a fixed pixel height from a number", () => {
|
|
73
|
+
render(
|
|
74
|
+
<Drawer open onOpenChange={() => {}}>
|
|
75
|
+
<DrawerContent snapHeight={640}>
|
|
76
|
+
<DrawerTitle>T5</DrawerTitle>
|
|
77
|
+
</DrawerContent>
|
|
78
|
+
</Drawer>
|
|
79
|
+
);
|
|
80
|
+
expect(screen.getByRole("dialog").style.height).toBe("640px");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("passes a string height through as-is", () => {
|
|
84
|
+
render(
|
|
85
|
+
<Drawer open onOpenChange={() => {}}>
|
|
86
|
+
<DrawerContent snapHeight="70vh">
|
|
87
|
+
<DrawerTitle>T5</DrawerTitle>
|
|
88
|
+
</DrawerContent>
|
|
89
|
+
</Drawer>
|
|
90
|
+
);
|
|
91
|
+
expect(screen.getByRole("dialog").style.height).toBe("70vh");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("is ignored on a horizontal (left/right) drawer", () => {
|
|
95
|
+
render(
|
|
96
|
+
<Drawer open onOpenChange={() => {}} direction="right">
|
|
97
|
+
<DrawerContent snapHeight={640}>
|
|
98
|
+
<DrawerTitle>T5</DrawerTitle>
|
|
99
|
+
</DrawerContent>
|
|
100
|
+
</Drawer>
|
|
101
|
+
);
|
|
102
|
+
expect(screen.getByRole("dialog").style.height).toBe("");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
55
105
|
});
|