@bearmenu/ui 0.8.56 → 0.8.57

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.
@@ -49,6 +49,7 @@ type DayRailChipLinkProps = {
49
49
  className?: string;
50
50
  children: React.ReactNode;
51
51
  "aria-current"?: "page";
52
+ "aria-label"?: string;
52
53
  onClick?: () => void;
53
54
  };
54
55
  type DayRailChipProps = {
@@ -66,8 +67,14 @@ type DayRailChipProps = {
66
67
  linkComponent?: React.ComponentType<DayRailChipLinkProps>;
67
68
  onClick?: () => void;
68
69
  className?: string;
70
+ /**
71
+ * The day for assistive tech — "Vineri 13 noiembrie, 12 evenimente". Without
72
+ * it a screen reader reads the chip's text run together: "Vin 1312", the
73
+ * date and the count as one number (bearmenu-fe HIG review, 2026-11-10).
74
+ */
75
+ "aria-label"?: string;
69
76
  };
70
- declare function DayRailChip({ label, dayNumber, count, isActive, isWeekend, labels, href, linkComponent: LinkComponent, onClick, className, }: DayRailChipProps): react_jsx_runtime.JSX.Element;
77
+ declare function DayRailChip({ label, dayNumber, count, isActive, isWeekend, labels, href, linkComponent: LinkComponent, onClick, className, "aria-label": ariaLabel, }: DayRailChipProps): react_jsx_runtime.JSX.Element;
71
78
  type DayRailChipSkeletonProps = {
72
79
  className?: string;
73
80
  };
@@ -17,7 +17,8 @@ function DayRailChip({
17
17
  href,
18
18
  linkComponent: LinkComponent = DefaultLink,
19
19
  onClick,
20
- className
20
+ className,
21
+ "aria-label": ariaLabel
21
22
  }) {
22
23
  const hasEvents = count > 0;
23
24
  const rootClasses = cn(
@@ -79,6 +80,7 @@ function DayRailChip({
79
80
  href,
80
81
  className: rootClasses,
81
82
  "aria-current": isActive ? "page" : void 0,
83
+ "aria-label": ariaLabel,
82
84
  onClick,
83
85
  children: body
84
86
  }
@@ -91,12 +93,13 @@ function DayRailChip({
91
93
  type: "button",
92
94
  className: rootClasses,
93
95
  "aria-current": isActive ? "true" : void 0,
96
+ "aria-label": ariaLabel,
94
97
  onClick,
95
98
  children: body
96
99
  }
97
100
  );
98
101
  }
99
- return /* @__PURE__ */ jsx("div", { className: rootClasses, children: body });
102
+ return /* @__PURE__ */ jsx("div", { className: rootClasses, role: ariaLabel ? "group" : void 0, "aria-label": ariaLabel, children: body });
100
103
  }
101
104
  function DayRailChipSkeleton({ className }) {
102
105
  return /* @__PURE__ */ jsx(Skeleton, { "aria-hidden": "true", className: cn("h-[50px] w-[62px] shrink-0", className) });
@@ -9,17 +9,28 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
9
9
  * over the date, no count — and the rail shares the width equally.
10
10
  *
11
11
  * Measured from the booking design (`.dchip`): flexes to an equal share, 1px
12
- * hairline, 11px radius, 7px vertical inset, centred. The weekday at 10px
12
+ * hairline, 11px radius, 7px vertical inset, centred. The weekday at 11px
13
13
  * uppercase, 0.05em tracking, muted; the date at 15px bold, tabular, 2px
14
14
  * below. Selected fills with `--gradient-primary`, white type, the weekday at
15
15
  * 75%. A day that cannot be booked (closed, past the window) fades to 40% and
16
16
  * is disabled.
17
+ *
18
+ * The weekday was 10px, under the HIG's 11pt floor for iPhone text; measured
19
+ * when the events map took this chip for its week picker (bearmenu-fe,
20
+ * 2026-11-10). 11px still holds a three-letter weekday in a 46px seventh of a
21
+ * phone's width.
22
+ *
23
+ * `detail` is an optional third line under the date: the events map prints
24
+ * that day's event count there. A chip without it keeps its booking height.
17
25
  */
18
26
  type WeekdayChipProps = {
19
27
  /** "Mon", "Tue" — already localised; rendered uppercase. */
20
28
  weekday: string;
21
29
  /** Day of month as displayed — "14". */
22
30
  day: string;
31
+ /** A short line under the date — "12", that day's events. Give the button
32
+ * an `aria-label` that says it with its unit; a bare number is unclear. */
33
+ detail?: string;
23
34
  selected?: boolean;
24
35
  /** Closed that day or outside the booking window. */
25
36
  unavailable?: boolean;
@@ -28,6 +39,6 @@ type WeekdayChipProps = {
28
39
  "aria-label"?: string;
29
40
  className?: string;
30
41
  };
31
- declare function WeekdayChip({ weekday, day, selected, unavailable, onSelect, "aria-label": ariaLabel, className, }: WeekdayChipProps): react_jsx_runtime.JSX.Element;
42
+ declare function WeekdayChip({ weekday, day, detail, selected, unavailable, onSelect, "aria-label": ariaLabel, className, }: WeekdayChipProps): react_jsx_runtime.JSX.Element;
32
43
 
33
44
  export { WeekdayChip, type WeekdayChipProps };
@@ -5,6 +5,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
5
5
  function WeekdayChip({
6
6
  weekday,
7
7
  day,
8
+ detail,
8
9
  selected,
9
10
  unavailable,
10
11
  onSelect,
@@ -33,13 +34,24 @@ function WeekdayChip({
33
34
  "span",
34
35
  {
35
36
  className: cn(
36
- "block text-[10px] tracking-[0.05em] uppercase",
37
+ "block text-[11px] tracking-[0.05em] uppercase",
37
38
  selected ? "text-white/75" : "text-muted-foreground"
38
39
  ),
39
40
  children: weekday
40
41
  }
41
42
  ),
42
- /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-[15px] font-bold tabular-nums", children: day })
43
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-[15px] font-bold tabular-nums", children: day }),
44
+ detail !== void 0 && /* @__PURE__ */ jsx(
45
+ "span",
46
+ {
47
+ "data-slot": "weekday-chip-detail",
48
+ className: cn(
49
+ "mt-0.5 block font-mono text-[11px] tabular-nums",
50
+ selected ? "text-white/85" : "text-muted-foreground"
51
+ ),
52
+ children: detail
53
+ }
54
+ )
43
55
  ]
44
56
  }
45
57
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bearmenu/ui",
3
- "version": "0.8.56",
3
+ "version": "0.8.57",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "description": "BearMenu design system \u2014 shared UI tokens, primitives, and components",
6
6
  "license": "UNLICENSED",
@@ -82,6 +82,13 @@ describe("WeekdayChip", () => {
82
82
  render(<WeekdayChip weekday="Tue" day="11" unavailable />);
83
83
  expect(screen.getByRole("button")).toBeDisabled();
84
84
  });
85
+
86
+ it("prints a detail line under the date only when given one", () => {
87
+ const { rerender, container } = render(<WeekdayChip weekday="Fri" day="13" detail="12" />);
88
+ expect(container.querySelector("[data-slot='weekday-chip-detail']")).toHaveTextContent("12");
89
+ rerender(<WeekdayChip weekday="Fri" day="13" />);
90
+ expect(container.querySelector("[data-slot='weekday-chip-detail']")).toBeNull();
91
+ });
85
92
  });
86
93
 
87
94
  describe("HoldCountdown", () => {
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { DayRailChip } from "./day-rail-chip";
4
+
5
+ const labels = { noEvents: "no events" };
6
+
7
+ describe("DayRailChip", () => {
8
+ // Without a label a screen reader read "Vin 1312": the date and the count
9
+ // as one number (bearmenu-fe HIG review, 2026-11-10).
10
+ it("speaks its aria-label instead of its text run together", () => {
11
+ render(
12
+ <DayRailChip
13
+ label="Vin"
14
+ dayNumber="13"
15
+ count={12}
16
+ labels={labels}
17
+ onClick={() => {}}
18
+ aria-label="Vineri 13 noiembrie, 12 evenimente"
19
+ />
20
+ );
21
+ expect(
22
+ screen.getByRole("button", { name: "Vineri 13 noiembrie, 12 evenimente" })
23
+ ).toBeInTheDocument();
24
+ });
25
+
26
+ it("names a link chip and an inert one the same way", () => {
27
+ const { rerender } = render(
28
+ <DayRailChip
29
+ label="Sâm"
30
+ dayNumber="14"
31
+ count={3}
32
+ labels={labels}
33
+ href="/ro/cluj-napoca/events?day=2026-11-14"
34
+ aria-label="Sâmbătă 14 noiembrie, 3 evenimente"
35
+ />
36
+ );
37
+ expect(
38
+ screen.getByRole("link", { name: "Sâmbătă 14 noiembrie, 3 evenimente" })
39
+ ).toBeInTheDocument();
40
+ rerender(
41
+ <DayRailChip
42
+ label="Lun"
43
+ dayNumber="16"
44
+ count={0}
45
+ labels={labels}
46
+ aria-label="Luni 16 noiembrie, niciun eveniment"
47
+ />
48
+ );
49
+ expect(
50
+ screen.getByRole("group", { name: "Luni 16 noiembrie, niciun eveniment" })
51
+ ).toBeInTheDocument();
52
+ });
53
+
54
+ it("keeps its text as the name when no label is given", () => {
55
+ render(<DayRailChip label="Azi" dayNumber="10" count={7} labels={labels} onClick={() => {}} />);
56
+ expect(screen.getByRole("button")).toHaveTextContent("Azi 107");
57
+ });
58
+ });
@@ -53,6 +53,7 @@ export type DayRailChipLinkProps = {
53
53
  className?: string;
54
54
  children: React.ReactNode;
55
55
  "aria-current"?: "page";
56
+ "aria-label"?: string;
56
57
  onClick?: () => void;
57
58
  };
58
59
 
@@ -75,6 +76,12 @@ export type DayRailChipProps = {
75
76
  linkComponent?: React.ComponentType<DayRailChipLinkProps>;
76
77
  onClick?: () => void;
77
78
  className?: string;
79
+ /**
80
+ * The day for assistive tech — "Vineri 13 noiembrie, 12 evenimente". Without
81
+ * it a screen reader reads the chip's text run together: "Vin 1312", the
82
+ * date and the count as one number (bearmenu-fe HIG review, 2026-11-10).
83
+ */
84
+ "aria-label"?: string;
78
85
  };
79
86
 
80
87
  export function DayRailChip({
@@ -88,6 +95,7 @@ export function DayRailChip({
88
95
  linkComponent: LinkComponent = DefaultLink,
89
96
  onClick,
90
97
  className,
98
+ "aria-label": ariaLabel,
91
99
  }: DayRailChipProps) {
92
100
  const hasEvents = count > 0;
93
101
 
@@ -160,6 +168,7 @@ export function DayRailChip({
160
168
  href={href}
161
169
  className={rootClasses}
162
170
  aria-current={isActive ? "page" : undefined}
171
+ aria-label={ariaLabel}
163
172
  onClick={onClick}
164
173
  >
165
174
  {body}
@@ -176,6 +185,7 @@ export function DayRailChip({
176
185
  // toggle semantic was right; it now navigates, and this is the current
177
186
  // day in a set of days.
178
187
  aria-current={isActive ? "true" : undefined}
188
+ aria-label={ariaLabel}
179
189
  onClick={onClick}
180
190
  >
181
191
  {body}
@@ -183,7 +193,13 @@ export function DayRailChip({
183
193
  );
184
194
  }
185
195
 
186
- return <div className={rootClasses}>{body}</div>;
196
+ // An inert day has no role to carry a label; `role="group"` gives the name
197
+ // somewhere to live without inventing an interaction.
198
+ return (
199
+ <div className={rootClasses} role={ariaLabel ? "group" : undefined} aria-label={ariaLabel}>
200
+ {body}
201
+ </div>
202
+ );
187
203
  }
188
204
 
189
205
  export type DayRailChipSkeletonProps = {
@@ -22,6 +22,10 @@ type Story = StoryObj<typeof WeekdayChip>;
22
22
  export const Default: Story = { args: { weekday: "Mon", day: "10" } };
23
23
  export const Selected: Story = { args: { weekday: "Fri", day: "14", selected: true } };
24
24
  export const Unavailable: Story = { args: { weekday: "Tue", day: "11", unavailable: true } };
25
+ /** The events map's week picker: the day's event count under the date. */
26
+ export const WithDetail: Story = {
27
+ args: { weekday: "Fri", day: "13", detail: "12", "aria-label": "Friday 13 November, 12 events" },
28
+ };
25
29
 
26
30
  const WEEK = [
27
31
  ["Mon", "10"],
@@ -12,11 +12,19 @@ import { cn } from "../lib/utils";
12
12
  * over the date, no count — and the rail shares the width equally.
13
13
  *
14
14
  * Measured from the booking design (`.dchip`): flexes to an equal share, 1px
15
- * hairline, 11px radius, 7px vertical inset, centred. The weekday at 10px
15
+ * hairline, 11px radius, 7px vertical inset, centred. The weekday at 11px
16
16
  * uppercase, 0.05em tracking, muted; the date at 15px bold, tabular, 2px
17
17
  * below. Selected fills with `--gradient-primary`, white type, the weekday at
18
18
  * 75%. A day that cannot be booked (closed, past the window) fades to 40% and
19
19
  * is disabled.
20
+ *
21
+ * The weekday was 10px, under the HIG's 11pt floor for iPhone text; measured
22
+ * when the events map took this chip for its week picker (bearmenu-fe,
23
+ * 2026-11-10). 11px still holds a three-letter weekday in a 46px seventh of a
24
+ * phone's width.
25
+ *
26
+ * `detail` is an optional third line under the date: the events map prints
27
+ * that day's event count there. A chip without it keeps its booking height.
20
28
  */
21
29
 
22
30
  export type WeekdayChipProps = {
@@ -24,6 +32,9 @@ export type WeekdayChipProps = {
24
32
  weekday: string;
25
33
  /** Day of month as displayed — "14". */
26
34
  day: string;
35
+ /** A short line under the date — "12", that day's events. Give the button
36
+ * an `aria-label` that says it with its unit; a bare number is unclear. */
37
+ detail?: string;
27
38
  selected?: boolean;
28
39
  /** Closed that day or outside the booking window. */
29
40
  unavailable?: boolean;
@@ -36,6 +47,7 @@ export type WeekdayChipProps = {
36
47
  export function WeekdayChip({
37
48
  weekday,
38
49
  day,
50
+ detail,
39
51
  selected,
40
52
  unavailable,
41
53
  onSelect,
@@ -63,13 +75,24 @@ export function WeekdayChip({
63
75
  >
64
76
  <span
65
77
  className={cn(
66
- "block text-[10px] tracking-[0.05em] uppercase",
78
+ "block text-[11px] tracking-[0.05em] uppercase",
67
79
  selected ? "text-white/75" : "text-muted-foreground"
68
80
  )}
69
81
  >
70
82
  {weekday}
71
83
  </span>
72
84
  <span className="mt-0.5 block text-[15px] font-bold tabular-nums">{day}</span>
85
+ {detail !== undefined && (
86
+ <span
87
+ data-slot="weekday-chip-detail"
88
+ className={cn(
89
+ "mt-0.5 block font-mono text-[11px] tabular-nums",
90
+ selected ? "text-white/85" : "text-muted-foreground"
91
+ )}
92
+ >
93
+ {detail}
94
+ </span>
95
+ )}
73
96
  </button>
74
97
  );
75
98
  }