@bearmenu/ui 0.8.49 → 0.8.51

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.
Files changed (56) hide show
  1. package/dist/{chunk-QY66ME5B.js → chunk-CZ2HC7WY.js} +24 -6
  2. package/dist/components/alert.d.ts +2 -2
  3. package/dist/components/badge.d.ts +1 -1
  4. package/dist/components/banner.d.ts +1 -1
  5. package/dist/components/button.d.ts +2 -2
  6. package/dist/components/card.d.ts +2 -2
  7. package/dist/components/command.d.ts +15 -15
  8. package/dist/components/concept-compare.d.ts +1 -1
  9. package/dist/components/doorway-tiles.d.ts +1 -1
  10. package/dist/components/empty-state.d.ts +2 -2
  11. package/dist/components/event-detail-panel.d.ts +12 -2
  12. package/dist/components/event-detail-panel.js +15 -3
  13. package/dist/components/grade-atom.d.ts +1 -1
  14. package/dist/components/kitchen-dossier.d.ts +1 -1
  15. package/dist/components/menu-dead-end.d.ts +1 -1
  16. package/dist/components/menu-family-block.d.ts +2 -2
  17. package/dist/components/menu-family-block.js +3 -1
  18. package/dist/components/menu-family-card.d.ts +1 -1
  19. package/dist/components/menu-filter-chips.d.ts +1 -1
  20. package/dist/components/menu-group-rows.d.ts +1 -1
  21. package/dist/components/menu-index-header.d.ts +1 -1
  22. package/dist/components/menu-index-list.d.ts +1 -1
  23. package/dist/components/menu-intent-hero.d.ts +1 -1
  24. package/dist/components/menu-item.d.ts +1 -1
  25. package/dist/components/menu-kitchen-list.d.ts +9 -2
  26. package/dist/components/menu-kitchen-list.js +3 -1
  27. package/dist/components/menu-mocks.d.ts +1 -1
  28. package/dist/components/menu-types.d.ts +1 -1
  29. package/dist/components/open-now-list.d.ts +1 -1
  30. package/dist/components/place-about-tab.d.ts +1 -1
  31. package/dist/components/place-card.d.ts +1 -1
  32. package/dist/components/place-details-mobile.d.ts +1 -1
  33. package/dist/components/place-schedule-week.d.ts +1 -1
  34. package/dist/components/place-types.d.ts +1 -1
  35. package/dist/components/qa-block.d.ts +1 -1
  36. package/dist/components/sliding-panels.d.ts +6 -0
  37. package/dist/components/sliding-panels.js +13 -3
  38. package/dist/components/spinner.d.ts +2 -2
  39. package/dist/components/tag.d.ts +2 -2
  40. package/dist/components/text.d.ts +2 -2
  41. package/dist/components/top-comparison.d.ts +1 -1
  42. package/dist/components/top-cross-link.d.ts +1 -1
  43. package/dist/components/top-ranking-entry.d.ts +1 -1
  44. package/dist/components/top-ranking-hero.d.ts +1 -1
  45. package/dist/components/top-types.d.ts +1 -1
  46. package/dist/components/venue-grade-dimensions.d.ts +1 -1
  47. package/dist/components/venue-quotes.d.ts +1 -1
  48. package/dist/components/venue-wall.d.ts +1 -1
  49. package/dist/{place-types-DCVqWmk_.d.ts → place-types-Dzy6FH67.d.ts} +1 -1
  50. package/package.json +23 -217
  51. package/src/components/event-detail-panel.test.tsx +35 -0
  52. package/src/components/event-detail-panel.tsx +32 -10
  53. package/src/components/menu-kitchen-list.test.tsx +39 -0
  54. package/src/components/menu-kitchen-list.tsx +36 -7
  55. package/src/components/sliding-panels.test.tsx +26 -4
  56. package/src/components/sliding-panels.tsx +15 -4
@@ -1,5 +1,7 @@
1
1
  import { Check, ChevronRight, Clock, MessageSquare, Star } from "lucide-react";
2
+ import { showsGradeAtom } from "../lib/grade";
2
3
  import { cn } from "../lib/utils";
4
+ import { GradeAtom, type GradeAtomLabels } from "./grade-atom";
3
5
  import { Separator } from "./separator";
4
6
  import { Skeleton } from "./skeleton";
5
7
  import type { MenuKitchenPreview, MenuLinkComponent, MenuLinkProps } from "./menu-types";
@@ -59,6 +61,13 @@ export type MenuKitchenListLabels = {
59
61
  openNow: string;
60
62
  /** Reveals the next page of rows, e.g. "Show 10 more". */
61
63
  showMore?: string;
64
+ /**
65
+ * The grade pill's labels. With them, a graded venue shows its BearMenu
66
+ * grade where the Google star was; the star stays only for a venue with no
67
+ * grade. Optional so existing callers keep compiling — without it every row
68
+ * shows the star, as before.
69
+ */
70
+ grade?: GradeAtomLabels;
62
71
  };
63
72
 
64
73
  /**
@@ -127,6 +136,31 @@ export function MenuKitchenList({
127
136
  );
128
137
  }
129
138
 
139
+ function gradeShown(place: MenuKitchenPreview["place"], labels: MenuKitchenListLabels) {
140
+ return labels.grade !== undefined && showsGradeAtom(place.reviews_digest?.grade, "xs");
141
+ }
142
+
143
+ /** BearMenu's grade when the venue has one; Google's star only without it. */
144
+ function KitchenRating({
145
+ place,
146
+ labels,
147
+ }: {
148
+ place: MenuKitchenPreview["place"];
149
+ labels: MenuKitchenListLabels;
150
+ }) {
151
+ const grade = place.reviews_digest?.grade;
152
+ if (labels.grade && showsGradeAtom(grade, "xs")) {
153
+ return <GradeAtom grade={grade} labels={labels.grade} size="xs" className="shrink-0" />;
154
+ }
155
+ if (!place.rating) return null;
156
+ return (
157
+ <>
158
+ <Star className="h-[11px] w-[11px] shrink-0" aria-hidden />
159
+ {place.rating.rating}
160
+ </>
161
+ );
162
+ }
163
+
130
164
  function KitchenRow({
131
165
  kitchen,
132
166
  labels,
@@ -189,13 +223,8 @@ function KitchenRow({
189
223
  {place.name}
190
224
  </span>
191
225
  <span className="flex items-center gap-1 truncate text-xs text-muted-foreground">
192
- {place.rating && (
193
- <>
194
- <Star className="h-[11px] w-[11px] shrink-0" aria-hidden />
195
- {place.rating.rating}
196
- {kitchen.metaLabel && " · "}
197
- </>
198
- )}
226
+ <KitchenRating place={place} labels={labels} />
227
+ {kitchen.metaLabel && (gradeShown(place, labels) || place.rating) && " · "}
199
228
  {kitchen.metaLabel}
200
229
  </span>
201
230
  {kitchen.hoursLabel && (
@@ -18,10 +18,7 @@ const basePanels: Panel[] = [
18
18
  const panelWrappers = () =>
19
19
  Array.from(document.querySelectorAll<HTMLElement>('[data-slot="sliding-panel"]'));
20
20
 
21
- // The back button is icon-only, so it is the one button in the active panel
22
- // with no text content.
23
- const backButton = () =>
24
- screen.getAllByRole("button").filter((button) => button.textContent === "")[0];
21
+ const backButton = () => screen.getByRole("button", { name: "Back" });
25
22
 
26
23
  function Controlled({ initial = "one" }: { initial?: string }) {
27
24
  const [current, setCurrent] = useState(initial);
@@ -113,4 +110,29 @@ describe("SlidingPanels", () => {
113
110
  await user.click(backButton());
114
111
  expect(onPanelChange).toHaveBeenCalledWith("root");
115
112
  });
113
+
114
+ it("names the icon-only back button with backAriaLabel", () => {
115
+ const panels: Panel[] = [
116
+ { id: "root", content: <button type="button">Root</button> },
117
+ {
118
+ id: "child",
119
+ showBackButton: true,
120
+ backButtonLabel: "Categorii",
121
+ backAriaLabel: "Înapoi",
122
+ content: <button type="button">Child</button>,
123
+ },
124
+ ];
125
+ render(<SlidingPanels panels={panels} currentPanel="child" onPanelChange={vi.fn()} />);
126
+ // The heading beside it is the panel title, not the button's name
127
+ expect(screen.getByRole("button", { name: "Înapoi" })).toBeInTheDocument();
128
+ });
129
+
130
+ it("drops the slide and height transitions under reduced motion", () => {
131
+ render(<Controlled initial="two" />);
132
+ const container = document.querySelector<HTMLElement>('[data-slot="sliding-panels"]');
133
+ expect(container?.className).toContain("motion-reduce:transition-none");
134
+ for (const panel of panelWrappers()) {
135
+ expect(panel.className).toContain("motion-reduce:transition-none");
136
+ }
137
+ });
116
138
  });
@@ -10,7 +10,13 @@ export interface Panel {
10
10
  id: string;
11
11
  content: React.ReactNode;
12
12
  showBackButton?: boolean;
13
+ /** Heading shown next to the back button (the panel's title, not the button's name). */
13
14
  backButtonLabel?: string;
15
+ /**
16
+ * Accessible name of the icon-only back button, localized by the caller.
17
+ * Falls back to "Back"; without a name screen readers announce only "button".
18
+ */
19
+ backAriaLabel?: string;
14
20
  onBack?: () => void;
15
21
  // When set, the panel is treated as a child of `parentId`. Siblings (panels
16
22
  // sharing the same parent) stay off-screen on the right when not active —
@@ -84,7 +90,7 @@ export const SlidingPanels = ({
84
90
  <div
85
91
  data-slot="sliding-panels"
86
92
  className={cn(
87
- "relative overflow-clip transition-[height] duration-500 ease-[cubic-bezier(0.32,0.72,0,1)]",
93
+ "relative overflow-clip transition-[height] duration-500 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none",
88
94
  className
89
95
  )}
90
96
  style={{ height: containerHeight, overflowClipMargin: "8px" }}
@@ -105,7 +111,7 @@ export const SlidingPanels = ({
105
111
  data-slot="sliding-panel"
106
112
  data-state={isActive ? "active" : "inactive"}
107
113
  className={cn(
108
- "absolute top-0 left-0 w-full transition-transform duration-500 ease-[cubic-bezier(0.32,0.72,0,1)]",
114
+ "absolute top-0 left-0 w-full transition-transform duration-500 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none",
109
115
  isActive && "translate-x-0",
110
116
  isLeft && "-translate-x-[calc(100%+8px)]",
111
117
  isRight && "translate-x-[calc(100%+8px)]"
@@ -125,8 +131,13 @@ export const SlidingPanels = ({
125
131
  >
126
132
  {panel.showBackButton && (
127
133
  <div className="flex items-center gap-2 mb-4">
128
- <Button variant="ghost" size="icon" onClick={handleBack}>
129
- <ChevronLeft className="h-4 w-4" />
134
+ <Button
135
+ variant="ghost"
136
+ size="icon"
137
+ onClick={handleBack}
138
+ aria-label={panel.backAriaLabel ?? "Back"}
139
+ >
140
+ <ChevronLeft className="h-4 w-4" aria-hidden />
130
141
  </Button>
131
142
  {panel.backButtonLabel && (
132
143
  <h3 className="font-semibold">{panel.backButtonLabel}</h3>