mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +00:00
Open the capture sheet from the dashboard's add button on phones
On the phone shell the header's 'Add' opened a dropdown while the plus in the bottom nav, one thumb below, opened the capture sheet. The header button now opens the same sheet there; the desktop rail keeps the dropdown. The onboarding tour anchors on whichever of the two is laid out.
This commit is contained in:
1 parent
b53782810f
commit
6ca33882f1
3 files changed
+51
-14
No files matched your search
@@ -141,7 +141,10 @@ test.describe("v1.4.33 F2 — onboarding tour passes clicks through to the page"
|
||||
// so a naive "dim everything outside the spotlight rect" overlay
|
||||
// covers it. The fix makes the entire tour layer pointer-events:
|
||||
// none — every dim panel and the spotlight ring are visual only.
|
||||
const quickAddButton = page.locator('[data-tour-id="dashboard-quick-add"]');
|
||||
// One add button per shell carries the tour id; the other is hidden.
|
||||
const quickAddButton = page
|
||||
.locator('[data-tour-id="dashboard-quick-add"]')
|
||||
.filter({ visible: true });
|
||||
await expect(quickAddButton).toBeVisible();
|
||||
|
||||
// Forensic guard — at the quick-add button's centre, every element
|
||||
@@ -153,9 +156,11 @@ test.describe("v1.4.33 F2 — onboarding tour passes clicks through to the page"
|
||||
// the stack is blocking, instead of just observing the symptom
|
||||
// ("button click did not toggle aria-expanded").
|
||||
const tourBlockers = await page.evaluate(() => {
|
||||
const btn = document.querySelector<HTMLElement>(
|
||||
'[data-tour-id="dashboard-quick-add"]',
|
||||
);
|
||||
const btn = [
|
||||
...document.querySelectorAll<HTMLElement>(
|
||||
'[data-tour-id="dashboard-quick-add"]',
|
||||
),
|
||||
].find((el) => el.getClientRects().length > 0);
|
||||
if (!btn) return ["no-button"];
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const x = rect.left + rect.width / 2;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* Extracted from the dashboard page; the page owns the open-state the
|
||||
* dropdown items set via `onQuickEntry`.
|
||||
*/
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Activity,
|
||||
@@ -37,6 +37,7 @@ import { useRecordCapabilities } from "@/hooks/use-record-capabilities";
|
||||
import { hourInTz } from "@/lib/tz/format";
|
||||
import {
|
||||
CAPTURE_KIND_ORDER,
|
||||
CapturePicker,
|
||||
visibleCaptureKinds,
|
||||
} from "@/components/layout/capture-picker";
|
||||
import type { QuickEntryDialog } from "@/components/dashboard/quick-entry-sheets";
|
||||
@@ -49,6 +50,7 @@ export function DashboardHeader({
|
||||
const { t } = useTranslations();
|
||||
const { user } = useAuth();
|
||||
const mounted = useMounted();
|
||||
const [captureOpen, setCaptureOpen] = useState(false);
|
||||
// The quick-add offers four kinds, and each one is asked about its own
|
||||
// section: a reading under `measurements`, a dose under `medications`, a
|
||||
// mood entry under `mind`, a workout in one's own record only. The coarse `canAdd` this used to gate the whole
|
||||
@@ -149,6 +151,26 @@ export function DashboardHeader({
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{/* On the phone shell the add button opens the same capture
|
||||
picker sheet the bottom nav's plus opens: one way to add a
|
||||
thing, not a dropdown here and a sheet one thumb below. */}
|
||||
{canAddAnything && (
|
||||
<Button
|
||||
size="default"
|
||||
className="shell-desktop:hidden min-h-11"
|
||||
onClick={() => setCaptureOpen(true)}
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={captureOpen}
|
||||
data-slot="dashboard-quick-add-capture"
|
||||
data-tour-id="dashboard-quick-add"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
{t("common.add")}
|
||||
</Button>
|
||||
)}
|
||||
{canAddAnything && (
|
||||
<CapturePicker open={captureOpen} onOpenChange={setCaptureOpen} />
|
||||
)}
|
||||
{canAddAnything && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -166,7 +188,7 @@ export function DashboardHeader({
|
||||
+ label keep the same visual contract. */}
|
||||
<Button
|
||||
size="default"
|
||||
className="min-h-11 sm:min-h-9"
|
||||
className="shell-mobile:hidden min-h-11 sm:min-h-9"
|
||||
data-tour-id="dashboard-quick-add"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
|
||||
@@ -102,12 +102,26 @@ interface SpotlightRect {
|
||||
* not in the DOM or has zero dimensions — caller renders a centred
|
||||
* tooltip in that case.
|
||||
*/
|
||||
/**
|
||||
* The rendered element carrying a tour id. A control can exist twice with the
|
||||
* same id, one per shell (the dashboard's add button is a dropdown on the
|
||||
* desktop rail and a sheet trigger on the phone shell), with the other one
|
||||
* hidden; the stop anchors on the one that is actually laid out.
|
||||
*/
|
||||
function findTourTarget(targetId: string): HTMLElement | null {
|
||||
const all = document.querySelectorAll<HTMLElement>(
|
||||
`[data-tour-id="${CSS.escape(targetId)}"]`,
|
||||
);
|
||||
for (const el of all) {
|
||||
if (el.getClientRects().length > 0) return el;
|
||||
}
|
||||
return all[0] ?? null;
|
||||
}
|
||||
|
||||
function measureTarget(targetId: string | null): SpotlightRect | null {
|
||||
if (!targetId) return null;
|
||||
if (typeof document === "undefined") return null;
|
||||
const el = document.querySelector<HTMLElement>(
|
||||
`[data-tour-id="${CSS.escape(targetId)}"]`,
|
||||
);
|
||||
const el = findTourTarget(targetId);
|
||||
if (!el) return null;
|
||||
const rect = el.getBoundingClientRect();
|
||||
if (rect.width === 0 || rect.height === 0) return null;
|
||||
@@ -356,11 +370,7 @@ export function OnboardingTour({
|
||||
const MAX_ATTEMPTS = 40;
|
||||
const settleAndMeasure = () => {
|
||||
if (cancelled) return;
|
||||
const el = stop.targetId
|
||||
? document.querySelector<HTMLElement>(
|
||||
`[data-tour-id="${CSS.escape(stop.targetId)}"]`,
|
||||
)
|
||||
: null;
|
||||
const el = stop.targetId ? findTourTarget(stop.targetId) : null;
|
||||
if (el) {
|
||||
// Bring the anchor into view BEFORE measuring — anchors below the
|
||||
// fold positioned the popover under the viewport bottom otherwise.
|
||||
|
||||
Reference in new issue
Block a user