From c457ee8b0dfc59b6ee2b1ac2545534a3aee23f02 Mon Sep 17 00:00:00 2001 From: Richie Date: Mon, 6 Apr 2026 20:55:36 +1000 Subject: [PATCH] Add price-only MapPin variant (no name) Name is now optional. When omitted, renders a compact single-line pill with just "From $X" using the bolder name styling. Useful for denser map views or when provider identity isn't needed at pin level. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../atoms/MapPin/MapPin.stories.tsx | 24 ++++++++ src/components/atoms/MapPin/MapPin.tsx | 57 +++++++++++-------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/components/atoms/MapPin/MapPin.stories.tsx b/src/components/atoms/MapPin/MapPin.stories.tsx index bb7736b..0f6a28a 100644 --- a/src/components/atoms/MapPin/MapPin.stories.tsx +++ b/src/components/atoms/MapPin/MapPin.stories.tsx @@ -74,6 +74,30 @@ export const NameOnlyUnverified: Story = { }, }; +/** Price-only pill — no name, verified */ +export const PriceOnly: Story = { + args: { + price: 900, + verified: true, + }, +}; + +/** Price-only pill — unverified */ +export const PriceOnlyUnverified: Story = { + args: { + price: 1200, + }, +}; + +/** Price-only pill — active */ +export const PriceOnlyActive: Story = { + args: { + price: 900, + verified: true, + active: true, + }, +}; + /** Custom price label */ export const CustomPriceLabel: Story = { args: { diff --git a/src/components/atoms/MapPin/MapPin.tsx b/src/components/atoms/MapPin/MapPin.tsx index 6c3df3f..a7f6e08 100644 --- a/src/components/atoms/MapPin/MapPin.tsx +++ b/src/components/atoms/MapPin/MapPin.tsx @@ -6,8 +6,8 @@ import type { SxProps, Theme } from '@mui/material/styles'; /** Props for the FA MapPin atom */ export interface MapPinProps { - /** Provider or venue name — always shown on the pin */ - name: string; + /** Provider or venue name — omit for a price-only pill */ + name?: string; /** Starting package price in dollars — shown as "From $X" */ price?: number; /** Custom price label (e.g. "POA") — overrides formatted price */ @@ -81,7 +81,8 @@ const colours = { * Usage: * ```tsx * - * {/* No price, unverified *\/} + * {/* Name only, unverified *\/} + * {/* Price-only pill, no name *\/} * * ``` */ @@ -105,7 +106,7 @@ export const MapPin = React.forwardRef( ref={ref} role="button" tabIndex={0} - aria-label={`${name}${hasPrice ? `, packages from $${price?.toLocaleString('en-AU') ?? priceLabel}` : ''}${verified ? ', verified' : ''}${active ? ' (selected)' : ''}`} + aria-label={`${name ?? (verified ? 'Verified' : 'Unverified') + ' provider'}${hasPrice ? `, packages from $${price?.toLocaleString('en-AU') ?? priceLabel}` : ''}${verified ? ', verified' : ''}${active ? ' (selected)' : ''}`} onClick={onClick} onKeyDown={handleKeyDown} sx={[ @@ -150,34 +151,42 @@ export const MapPin = React.forwardRef( }} > {/* Name */} - t.typography.fontFamily, - lineHeight: 1.3, - color: active ? palette.activeName : palette.name, - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - maxWidth: '100%', - transition: 'color 150ms ease-in-out', - }} - > - {name} - + {name && ( + t.typography.fontFamily, + lineHeight: 1.3, + color: active ? palette.activeName : palette.name, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + maxWidth: '100%', + transition: 'color 150ms ease-in-out', + }} + > + {name} + + )} {/* Price line */} {hasPrice && ( t.typography.fontFamily, lineHeight: 1.2, - color: active ? palette.activePrice : palette.price, + color: !name + ? active + ? palette.activeName + : palette.name + : active + ? palette.activePrice + : palette.price, whiteSpace: 'nowrap', transition: 'color 150ms ease-in-out', }}