fix(mobile): the trip rail's More wears the dock's ring while its sheet is open

This commit is contained in:
Maurice
2026-11-08 03:07:24 +00:00
parent bfb1b04f8d
commit c66b69b510
4 changed files with 35 additions and 4 deletions
+12 -2
View File
@@ -97,6 +97,11 @@ export function MRailSpacer() {
interface MRailButtonProps {
label: string;
active: boolean;
/**
* Opens a sheet rather than a section (More): while that sheet is open the button wears
* the ring the phone dock's More wears, so the rail says where the sheet came from.
*/
expanded?: boolean;
onClick: () => void;
/** The icon, drawn at 20px. */
children: ReactNode;
@@ -108,19 +113,24 @@ interface MRailButtonProps {
* and never shrunk: it wraps into a second, balanced line where its language
* needs one, and only a word longer than the rail is hyphenated.
*/
export function MRailButton({ label, active, onClick, children }: MRailButtonProps) {
export function MRailButton({ label, active, expanded, onClick, children }: MRailButtonProps) {
return (
<button
type="button"
onClick={onClick}
title={label}
aria-current={active ? 'page' : undefined}
aria-expanded={expanded}
className="group flex w-full flex-none flex-col items-center gap-[3px] rounded-[18px] py-[5px] outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--m-act)]"
>
<span
aria-hidden="true"
className={`flex h-10 w-10 items-center justify-center rounded-full transition-colors duration-150 ${
active ? 'bg-m-act text-m-actfg' : 'text-m-muted group-hover:bg-[color:var(--m-ic)] group-hover:text-m-ink'
active
? 'bg-m-act text-m-actfg'
: expanded
? 'bg-[color:var(--m-ic)] text-m-ink ring-[1.5px] ring-inset ring-[color:var(--m-act)]'
: 'text-m-muted group-hover:bg-[color:var(--m-ic)] group-hover:text-m-ink'
}`}
>
{children}
+4 -2
View File
@@ -16,6 +16,8 @@ interface MTripRailProps {
onSelect: (id: string) => void;
/** Opens the More sheet: trip settings, export, the assistant. */
onMore: () => void;
/** Whether the More sheet is open, which the More button shows with a ring. */
moreOpen?: boolean;
labelOf: (id: string) => string;
}
@@ -27,7 +29,7 @@ const DOCK_ICONS = new Map(DOCK_PRIORITY.map((tab) => [tab.id, tab.icon]));
* collaboration and plugin pages sit in it too, each with its name, and More
* keeps only the trip's actions.
*/
export default function MTripRail({ tabs, active, onSelect, onMore, labelOf }: MTripRailProps) {
export default function MTripRail({ tabs, active, onSelect, onMore, moreOpen = false, labelOf }: MTripRailProps) {
const { t } = useTranslation();
return (
<MRail label={t('mobileTrip.sections')}>
@@ -41,7 +43,7 @@ export default function MTripRail({ tabs, active, onSelect, onMore, labelOf }: M
);
})}
<MRailSpacer />
<MRailButton label={t('mobileTrip.more')} active={false} onClick={onMore}>
<MRailButton label={t('mobileTrip.more')} active={false} expanded={moreOpen} onClick={onMore}>
<MoreHorizontal size={20} strokeWidth={2.2} />
</MRailButton>
</MRail>
@@ -901,6 +901,7 @@ export default function MTripShell({
active={trTab}
onSelect={setTrTab}
onMore={() => openSheet('mehr')}
moreOpen={sheet?.id === 'mehr'}
labelOf={tabLabel}
/>
)}
@@ -190,6 +190,24 @@ describe('MTripRail', () => {
fireEvent.click(within(nav).getByRole('button', { name: 'More' }));
expect(onMore).toHaveBeenCalledOnce();
});
it('FE-MOB-RAIL-014: More wears a ring while its sheet is open, and says it is expanded', () => {
const props = {
tabs,
active: 'plan',
onSelect: vi.fn(),
onMore: vi.fn(),
labelOf: (id: string) => tabs.find((x) => x.id === id)?.label ?? id,
};
const { rerender } = render(<MTripRail {...props} />);
const more = () => screen.getByRole('button', { name: 'More' });
expect(more()).toHaveAttribute('aria-expanded', 'false');
expect(more().firstElementChild?.className).not.toContain('ring-');
rerender(<MTripRail {...props} moreOpen />);
expect(more()).toHaveAttribute('aria-expanded', 'true');
expect(more().firstElementChild?.className).toContain('ring-[color:var(--m-act)]');
expect(more()).not.toHaveAttribute('aria-current');
});
});
describe('MobileShell on a wide window', () => {