@bearmenu/ui 0.8.48 → 0.8.50
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-QY66ME5B.js → chunk-CZ2HC7WY.js} +24 -6
- package/dist/components/alert.d.ts +2 -2
- package/dist/components/avatar.d.ts +2 -2
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/banner.d.ts +1 -1
- package/dist/components/button.d.ts +2 -2
- package/dist/components/card.d.ts +2 -2
- package/dist/components/command.d.ts +3 -3
- package/dist/components/concept-compare.d.ts +1 -1
- package/dist/components/doorway-tiles.d.ts +1 -1
- package/dist/components/empty-state.d.ts +2 -2
- package/dist/components/event-detail-panel.d.ts +12 -2
- package/dist/components/event-detail-panel.js +15 -3
- package/dist/components/grade-atom.d.ts +1 -1
- package/dist/components/item.d.ts +2 -2
- package/dist/components/kitchen-dossier.d.ts +1 -1
- package/dist/components/menu-dead-end.d.ts +1 -1
- package/dist/components/menu-family-block.d.ts +2 -2
- package/dist/components/menu-family-block.js +3 -1
- package/dist/components/menu-family-card.d.ts +1 -1
- package/dist/components/menu-filter-chips.d.ts +1 -1
- package/dist/components/menu-group-rows.d.ts +1 -1
- package/dist/components/menu-index-header.d.ts +1 -1
- package/dist/components/menu-index-list.d.ts +1 -1
- package/dist/components/menu-intent-hero.d.ts +1 -1
- package/dist/components/menu-item.d.ts +1 -1
- package/dist/components/menu-kitchen-list.d.ts +9 -2
- package/dist/components/menu-kitchen-list.js +3 -1
- package/dist/components/menu-mocks.d.ts +1 -1
- package/dist/components/menu-types.d.ts +1 -1
- package/dist/components/open-now-list.d.ts +1 -1
- package/dist/components/place-about-tab.d.ts +1 -1
- package/dist/components/place-card.d.ts +1 -1
- package/dist/components/place-card.js +13 -5
- package/dist/components/place-details-mobile.d.ts +1 -1
- package/dist/components/place-schedule-week.d.ts +1 -1
- package/dist/components/place-types.d.ts +1 -1
- package/dist/components/qa-block.d.ts +1 -1
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/tag.d.ts +2 -2
- package/dist/components/text.d.ts +3 -3
- package/dist/components/top-comparison.d.ts +1 -1
- package/dist/components/top-cross-link.d.ts +1 -1
- package/dist/components/top-ranking-entry.d.ts +1 -1
- package/dist/components/top-ranking-hero.d.ts +1 -1
- package/dist/components/top-types.d.ts +1 -1
- package/dist/components/venue-grade-dimensions.d.ts +1 -1
- package/dist/components/venue-quotes.d.ts +1 -1
- package/dist/components/venue-wall.d.ts +1 -1
- package/dist/{place-types-C4rR3LTs.d.ts → place-types-Dzy6FH67.d.ts} +12 -1
- package/package.json +217 -23
- package/src/components/event-detail-panel.test.tsx +35 -0
- package/src/components/event-detail-panel.tsx +32 -10
- package/src/components/menu-kitchen-list.test.tsx +39 -0
- package/src/components/menu-kitchen-list.tsx +36 -7
- package/src/components/place-card.stories.tsx +36 -0
- package/src/components/place-card.test.tsx +29 -0
- package/src/components/place-card.tsx +24 -5
- package/src/components/place-types.ts +11 -0
|
@@ -10,6 +10,7 @@ const labels: PlaceCardLabels = {
|
|
|
10
10
|
closed: "Closed",
|
|
11
11
|
viewDetails: (name) => `View details for ${name}`,
|
|
12
12
|
priceLevel: (level) => `Price level ${level} of 4`,
|
|
13
|
+
established: (year) => String(year),
|
|
13
14
|
grade: {
|
|
14
15
|
soft: "Worth exploring",
|
|
15
16
|
bands: {
|
|
@@ -35,6 +36,7 @@ const basePlace: PlacePreview = {
|
|
|
35
36
|
price_dishes: { price_range_low: 35, price_range_high: 90, price_score: 2 },
|
|
36
37
|
reviews_digest: { grade: { overall: 8.4 } },
|
|
37
38
|
cover_url: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=800&q=80",
|
|
39
|
+
launched_at: "2020-02",
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
const meta = {
|
|
@@ -101,6 +103,40 @@ export const WithFooter: Story = {
|
|
|
101
103
|
},
|
|
102
104
|
};
|
|
103
105
|
|
|
106
|
+
/**
|
|
107
|
+
* The launch year closes the meta line, bare — fe's copy; `labels.established`
|
|
108
|
+
* may spell it out ("Est. 2020") where there is room. It is the backend's
|
|
109
|
+
* earliest-review estimate (`launched_at`, `YYYY-MM`). No year without the
|
|
110
|
+
* label or without a well-formed `launched_at`.
|
|
111
|
+
*/
|
|
112
|
+
export const Established: Story = {
|
|
113
|
+
args: { place: { ...basePlace, launched_at: "2012-09" } },
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** 220px rail with a long cuisine and full price: the cuisine gives way, price and year never do. */
|
|
117
|
+
export const EstablishedNarrowRail: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
place: { ...basePlace, cuisines: ["mediterranean"], price_dishes: { price_score: 4 } },
|
|
120
|
+
},
|
|
121
|
+
decorators: [
|
|
122
|
+
(Story) => (
|
|
123
|
+
<div className="w-[220px]">
|
|
124
|
+
<Story />
|
|
125
|
+
</div>
|
|
126
|
+
),
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** Spelled out by the label, for a consumer with room for the word. */
|
|
131
|
+
export const EstablishedWithLabel: Story = {
|
|
132
|
+
args: { labels: { ...labels, established: (year) => `Est. ${year}` } },
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** The year alone still gets a meta line: no cuisine, no price signal. */
|
|
136
|
+
export const EstablishedOnly: Story = {
|
|
137
|
+
args: { place: { ...basePlace, cuisines: undefined, price_dishes: undefined } },
|
|
138
|
+
};
|
|
139
|
+
|
|
104
140
|
export const Minimal: Story = {
|
|
105
141
|
args: { place: { id: "2", slug: "tiny", name: "The Tiny Spot" } },
|
|
106
142
|
};
|
|
@@ -9,6 +9,7 @@ const LABELS: PlaceCardLabels = {
|
|
|
9
9
|
closed: "Closed",
|
|
10
10
|
viewDetails: (name) => `View details for ${name}`,
|
|
11
11
|
priceLevel: (level) => `Price level ${level} of 4`,
|
|
12
|
+
established: (year) => `Est. ${year}`,
|
|
12
13
|
grade: {
|
|
13
14
|
soft: "Worth exploring",
|
|
14
15
|
bands: {
|
|
@@ -104,6 +105,34 @@ describe("PlaceCard", () => {
|
|
|
104
105
|
expect(screen.getByLabelText("Price level 2 of 4")).toBeInTheDocument();
|
|
105
106
|
});
|
|
106
107
|
|
|
108
|
+
it("closes the meta line with the launch year", () => {
|
|
109
|
+
render(
|
|
110
|
+
<PlaceCard
|
|
111
|
+
place={makePlace({ cuisines: ["italian"], launched_at: "2020-02" })}
|
|
112
|
+
labels={LABELS}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
expect(screen.getByText("Est. 2020")).toBeInTheDocument();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("shows the launch year on its own when there is no cuisine or price", () => {
|
|
119
|
+
const { container } = render(
|
|
120
|
+
<PlaceCard place={makePlace({ launched_at: "2012-09" })} labels={LABELS} />
|
|
121
|
+
);
|
|
122
|
+
expect(screen.getByText("Est. 2012")).toBeInTheDocument();
|
|
123
|
+
expect(container).not.toHaveTextContent("·");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("shows no year without the label or a well-formed launched_at", () => {
|
|
127
|
+
const { established: _omit, ...withoutEstablished } = LABELS;
|
|
128
|
+
const { rerender } = render(
|
|
129
|
+
<PlaceCard place={makePlace({ launched_at: "2020-02" })} labels={withoutEstablished} />
|
|
130
|
+
);
|
|
131
|
+
expect(screen.queryByText(/2020/)).not.toBeInTheDocument();
|
|
132
|
+
rerender(<PlaceCard place={makePlace({ launched_at: "2020" })} labels={LABELS} />);
|
|
133
|
+
expect(screen.queryByText(/Est\./)).not.toBeInTheDocument();
|
|
134
|
+
});
|
|
135
|
+
|
|
107
136
|
it("clamps an out-of-range price level instead of throwing", () => {
|
|
108
137
|
render(<PlaceCard place={makePlace({ price_drinks: { price_score: 7 } })} labels={LABELS} />);
|
|
109
138
|
expect(screen.getByLabelText("Price level 7 of 4")).toHaveTextContent("$$$$");
|
|
@@ -169,6 +169,12 @@ function PriceLevel({ level, label }: { level: number; label: string }) {
|
|
|
169
169
|
);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/** The year out of the schema's `YYYY-MM`; anything else shows no year. */
|
|
173
|
+
function launchYear(launchedAt: string | undefined): number | undefined {
|
|
174
|
+
const match = launchedAt ? /^(\d{4})-\d{2}$/.exec(launchedAt) : null;
|
|
175
|
+
return match ? Number(match[1]) : undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
172
178
|
const STATIC_ROOT_CLASSES = `group relative flex w-full select-none flex-col ${HOVER_MOTION} ${HOVER_LIFT_GRID_GROUP}`;
|
|
173
179
|
const ROOT_CLASSES = `${STATIC_ROOT_CLASSES} cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:rounded-xl`;
|
|
174
180
|
|
|
@@ -202,6 +208,8 @@ export const PlaceCard = memo(function PlaceCard({
|
|
|
202
208
|
const neighbourhood = place.areas?.[0]?.name;
|
|
203
209
|
const priceLevel = place.price_dishes?.price_score ?? place.price_drinks?.price_score;
|
|
204
210
|
const showPrice = priceLevel !== undefined && priceLevel > 0;
|
|
211
|
+
const year = labels.established ? launchYear(place.launched_at) : undefined;
|
|
212
|
+
const established = year !== undefined ? labels.established?.(year) : undefined;
|
|
205
213
|
const ariaLabel = labels.viewDetails(place.name);
|
|
206
214
|
|
|
207
215
|
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
|
@@ -277,11 +285,22 @@ export const PlaceCard = memo(function PlaceCard({
|
|
|
277
285
|
</div>
|
|
278
286
|
|
|
279
287
|
<div className="flex flex-col gap-0.5">
|
|
280
|
-
{
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
288
|
+
{/* Cuisine · price · launch year. At rail width the three can
|
|
289
|
+
outrun the card, so the cuisine is the part that gives way: the
|
|
290
|
+
price glyphs and the year are short and fixed. */}
|
|
291
|
+
{(cuisine || showPrice || established) && (
|
|
292
|
+
<div className="flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground">
|
|
293
|
+
{cuisine && <span className="min-w-0 truncate capitalize">{cuisine}</span>}
|
|
294
|
+
{cuisine && showPrice && <span className="shrink-0 text-border">·</span>}
|
|
295
|
+
{showPrice && (
|
|
296
|
+
<span className="shrink-0">
|
|
297
|
+
<PriceLevel level={priceLevel} label={labels.priceLevel(priceLevel)} />
|
|
298
|
+
</span>
|
|
299
|
+
)}
|
|
300
|
+
{established && (cuisine || showPrice) && (
|
|
301
|
+
<span className="shrink-0 text-border">·</span>
|
|
302
|
+
)}
|
|
303
|
+
{established && <span className="shrink-0 whitespace-nowrap">{established}</span>}
|
|
285
304
|
</div>
|
|
286
305
|
)}
|
|
287
306
|
|
|
@@ -58,6 +58,12 @@ export type PlacePreview = {
|
|
|
58
58
|
* bare storage key straight to `next/image`'s loader).
|
|
59
59
|
*/
|
|
60
60
|
cover_url?: string
|
|
61
|
+
/**
|
|
62
|
+
* Year and month the place launched (`YYYY-MM`), as the schema ships it.
|
|
63
|
+
* The backend approximates it from the earliest review it holds, so it is a
|
|
64
|
+
* floor rather than a founding date. `<PlaceCard>` shows the year only.
|
|
65
|
+
*/
|
|
66
|
+
launched_at?: string
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
export type PlaceHourBlock = { opens_at: number; closes_at: number }
|
|
@@ -193,6 +199,11 @@ export type PlaceCardLabels = {
|
|
|
193
199
|
viewDetails: (name: string) => string
|
|
194
200
|
/** Accessible name of the `$` price glyphs, e.g. "Price level 2 of 4". */
|
|
195
201
|
priceLevel: (level: number) => string
|
|
202
|
+
/**
|
|
203
|
+
* The launch year on the meta line, e.g. "Est. 2020" / "Din 2020". Optional:
|
|
204
|
+
* without it the card shows no year even when `launched_at` is set.
|
|
205
|
+
*/
|
|
206
|
+
established?: (year: number) => string
|
|
196
207
|
grade: GradeAtomLabels
|
|
197
208
|
}
|
|
198
209
|
|