PackageDetail: toggle pattern for Compare button inCart state
Replaces the earlier inert selected-state treatment. Now: - Button keeps its default soft/secondary chrome in both states — no separate brand-tinted visual. - When `inCart=true`, a leading CheckRoundedIcon is added and the label swaps from "Compare" to "Added". - Button remains clickable; `onCompare` is invoked in both states. Caller treats it as a toggle — add when absent, remove when present. - aria-pressed reflects the state for SR users; aria-label spells "Add to comparison" / "Remove from comparison" explicitly. Demo route swaps `basket.add()` for `basket.toggle()` on the handler so a second click removes the package from the comparison basket. Simpler visual (less space, one chrome to maintain) and a clearer interaction — the user can undo directly from the detail panel rather than hunting for CompareBar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,9 +55,9 @@ export interface PackageDetailProps {
|
||||
/** Whether the compare button is in loading state */
|
||||
compareLoading?: boolean;
|
||||
/** Whether this package is already in the comparison basket. When true,
|
||||
* the Compare button swaps to an "In comparison" selected-state (soft
|
||||
* brand tint + check icon) and is inert — removal happens via the
|
||||
* CompareBar, not this button. */
|
||||
* the Compare button swaps its label to "Added" and adds a leading check
|
||||
* icon. The button remains clickable — the caller is expected to treat
|
||||
* `onCompare` as a toggle (add when not in cart, remove when in cart). */
|
||||
inCart?: boolean;
|
||||
/** Custom label for the arrange CTA button (default: "Make Arrangement") */
|
||||
arrangeLabel?: string;
|
||||
@@ -219,42 +219,25 @@ export const PackageDetail = React.forwardRef<HTMLDivElement, PackageDetailProps
|
||||
>
|
||||
{arrangeLabel}
|
||||
</Button>
|
||||
{onCompare &&
|
||||
(inCart ? (
|
||||
// Selected-state: soft brand tint + check icon. Inert
|
||||
// (disabled + aria-disabled) — removal happens via the
|
||||
// CompareBar, not this button. sx override keeps the
|
||||
// brand tint instead of the default greyed-disabled look.
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="large"
|
||||
disabled
|
||||
startIcon={<CheckRoundedIcon />}
|
||||
aria-label="Already in comparison"
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
'&.Mui-disabled': {
|
||||
bgcolor: 'var(--fa-color-brand-50)',
|
||||
borderColor: 'var(--fa-color-brand-300)',
|
||||
color: 'var(--fa-color-text-brand)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
In comparison
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="soft"
|
||||
color="secondary"
|
||||
size="large"
|
||||
loading={compareLoading}
|
||||
onClick={onCompare}
|
||||
sx={{ flexShrink: 0 }}
|
||||
>
|
||||
Compare
|
||||
</Button>
|
||||
))}
|
||||
{onCompare && (
|
||||
// Same soft/secondary chrome in both states; when the package
|
||||
// is in the basket a leading check icon appears and the label
|
||||
// changes to "Added". Click is a toggle — caller decides to
|
||||
// add or remove based on the `inCart` it's passing in.
|
||||
<Button
|
||||
variant="soft"
|
||||
color="secondary"
|
||||
size="large"
|
||||
loading={compareLoading}
|
||||
startIcon={inCart ? <CheckRoundedIcon /> : undefined}
|
||||
onClick={onCompare}
|
||||
aria-pressed={inCart}
|
||||
aria-label={inCart ? 'Remove from comparison' : 'Add to comparison'}
|
||||
sx={{ flexShrink: 0 }}
|
||||
>
|
||||
{inCart ? 'Added' : 'Compare'}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
|
||||
@@ -21,11 +21,13 @@ export function PackagesRoute() {
|
||||
|
||||
if (!provider || !bundle) return <Navigate to="/" replace />;
|
||||
|
||||
// Compare CTA on the PackageDetail panel just adds the selection to the
|
||||
// basket. The floating CompareBar (mounted in App.tsx) handles navigation
|
||||
// and removal once the user has 2+ packages selected.
|
||||
// Compare CTA on the PackageDetail panel toggles the selection in the
|
||||
// basket — adds when absent, removes when present. The button's visible
|
||||
// state (Compare / Added + ✓) reflects `isSelectedInCart` below. The
|
||||
// floating CompareBar (mounted in App.tsx) handles navigation once the
|
||||
// user has 2+ packages selected.
|
||||
const handleCompare = () => {
|
||||
if (selectedId) basket.add(makeBasketKey(provider.id, selectedId));
|
||||
if (selectedId) basket.toggle(makeBasketKey(provider.id, selectedId));
|
||||
};
|
||||
|
||||
// When the selected package is already in the basket, PackageDetail swaps
|
||||
|
||||
Reference in New Issue
Block a user