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 */
|
/** Whether the compare button is in loading state */
|
||||||
compareLoading?: boolean;
|
compareLoading?: boolean;
|
||||||
/** Whether this package is already in the comparison basket. When true,
|
/** Whether this package is already in the comparison basket. When true,
|
||||||
* the Compare button swaps to an "In comparison" selected-state (soft
|
* the Compare button swaps its label to "Added" and adds a leading check
|
||||||
* brand tint + check icon) and is inert — removal happens via the
|
* icon. The button remains clickable — the caller is expected to treat
|
||||||
* CompareBar, not this button. */
|
* `onCompare` as a toggle (add when not in cart, remove when in cart). */
|
||||||
inCart?: boolean;
|
inCart?: boolean;
|
||||||
/** Custom label for the arrange CTA button (default: "Make Arrangement") */
|
/** Custom label for the arrange CTA button (default: "Make Arrangement") */
|
||||||
arrangeLabel?: string;
|
arrangeLabel?: string;
|
||||||
@@ -219,42 +219,25 @@ export const PackageDetail = React.forwardRef<HTMLDivElement, PackageDetailProps
|
|||||||
>
|
>
|
||||||
{arrangeLabel}
|
{arrangeLabel}
|
||||||
</Button>
|
</Button>
|
||||||
{onCompare &&
|
{onCompare && (
|
||||||
(inCart ? (
|
// Same soft/secondary chrome in both states; when the package
|
||||||
// Selected-state: soft brand tint + check icon. Inert
|
// is in the basket a leading check icon appears and the label
|
||||||
// (disabled + aria-disabled) — removal happens via the
|
// changes to "Added". Click is a toggle — caller decides to
|
||||||
// CompareBar, not this button. sx override keeps the
|
// add or remove based on the `inCart` it's passing in.
|
||||||
// brand tint instead of the default greyed-disabled look.
|
<Button
|
||||||
<Button
|
variant="soft"
|
||||||
variant="outlined"
|
color="secondary"
|
||||||
color="secondary"
|
size="large"
|
||||||
size="large"
|
loading={compareLoading}
|
||||||
disabled
|
startIcon={inCart ? <CheckRoundedIcon /> : undefined}
|
||||||
startIcon={<CheckRoundedIcon />}
|
onClick={onCompare}
|
||||||
aria-label="Already in comparison"
|
aria-pressed={inCart}
|
||||||
sx={{
|
aria-label={inCart ? 'Remove from comparison' : 'Add to comparison'}
|
||||||
flexShrink: 0,
|
sx={{ flexShrink: 0 }}
|
||||||
'&.Mui-disabled': {
|
>
|
||||||
bgcolor: 'var(--fa-color-brand-50)',
|
{inCart ? 'Added' : 'Compare'}
|
||||||
borderColor: 'var(--fa-color-brand-300)',
|
</Button>
|
||||||
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>
|
|
||||||
))}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ export function PackagesRoute() {
|
|||||||
|
|
||||||
if (!provider || !bundle) return <Navigate to="/" replace />;
|
if (!provider || !bundle) return <Navigate to="/" replace />;
|
||||||
|
|
||||||
// Compare CTA on the PackageDetail panel just adds the selection to the
|
// Compare CTA on the PackageDetail panel toggles the selection in the
|
||||||
// basket. The floating CompareBar (mounted in App.tsx) handles navigation
|
// basket — adds when absent, removes when present. The button's visible
|
||||||
// and removal once the user has 2+ packages selected.
|
// state (Compare / Added + ✓) reflects `isSelectedInCart` below. The
|
||||||
|
// floating CompareBar (mounted in App.tsx) handles navigation once the
|
||||||
|
// user has 2+ packages selected.
|
||||||
const handleCompare = () => {
|
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
|
// When the selected package is already in the basket, PackageDetail swaps
|
||||||
|
|||||||
Reference in New Issue
Block a user