@multiplatform.one/forms 7.80.0 → 7.81.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/AdaptivePopup/index.cjs +2 -1
- package/dist/cjs/AdaptivePopup/index.native.js +2 -1
- package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
- package/dist/cjs/Button/index.cjs +12 -5
- package/dist/cjs/Button/index.native.js +14 -6
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/PanelHeader.cjs +3 -8
- package/dist/cjs/FloatingPanel/PanelHeader.native.js +3 -8
- package/dist/cjs/FloatingPanel/PanelHeader.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/coverRect.cjs +0 -8
- package/dist/cjs/FloatingPanel/coverRect.native.js +0 -8
- package/dist/cjs/FloatingPanel/coverRect.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/echoContext.cjs +5 -27
- package/dist/cjs/FloatingPanel/echoContext.native.js +5 -28
- package/dist/cjs/FloatingPanel/echoContext.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/index.cjs +49 -131
- package/dist/cjs/FloatingPanel/index.native.js +216 -135
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/index.cjs +3 -10
- package/dist/cjs/fields/Combobox/index.native.js +3 -10
- package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/InlineChoices.cjs +4 -8
- package/dist/cjs/fields/Select/InlineChoices.native.js +4 -8
- package/dist/cjs/fields/Select/InlineChoices.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.cjs +65 -201
- package/dist/cjs/fields/Select/index.native.js +74 -209
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +2 -2
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +2 -2
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/cjs/index.cjs +4 -0
- package/dist/cjs/index.native.js +4 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/CheckGlyph.cjs +40 -0
- package/dist/cjs/shared/CheckGlyph.native.js +44 -0
- package/dist/cjs/shared/CheckGlyph.native.js.map +1 -0
- package/dist/cjs/shared/checkedOnField.cjs +104 -0
- package/dist/cjs/shared/checkedOnField.native.js +117 -0
- package/dist/cjs/shared/checkedOnField.native.js.map +1 -0
- package/dist/cjs/shared/floatingList.cjs +12 -5
- package/dist/cjs/shared/floatingList.native.js +15 -7
- package/dist/cjs/shared/floatingList.native.js.map +1 -1
- package/dist/esm/AdaptivePopup/index.mjs +3 -2
- package/dist/esm/AdaptivePopup/index.mjs.map +1 -1
- package/dist/esm/AdaptivePopup/index.native.js +3 -2
- package/dist/esm/AdaptivePopup/index.native.js.map +1 -1
- package/dist/esm/Button/index.mjs +12 -5
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +14 -6
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/PanelHeader.mjs +3 -8
- package/dist/esm/FloatingPanel/PanelHeader.mjs.map +1 -1
- package/dist/esm/FloatingPanel/PanelHeader.native.js +3 -8
- package/dist/esm/FloatingPanel/PanelHeader.native.js.map +1 -1
- package/dist/esm/FloatingPanel/coverRect.mjs +1 -8
- package/dist/esm/FloatingPanel/coverRect.mjs.map +1 -1
- package/dist/esm/FloatingPanel/coverRect.native.js +1 -8
- package/dist/esm/FloatingPanel/coverRect.native.js.map +1 -1
- package/dist/esm/FloatingPanel/echoContext.mjs +4 -24
- package/dist/esm/FloatingPanel/echoContext.mjs.map +1 -1
- package/dist/esm/FloatingPanel/echoContext.native.js +4 -25
- package/dist/esm/FloatingPanel/echoContext.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.mjs +53 -135
- package/dist/esm/FloatingPanel/index.mjs.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +180 -99
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/fields/Combobox/index.mjs +4 -11
- package/dist/esm/fields/Combobox/index.mjs.map +1 -1
- package/dist/esm/fields/Combobox/index.native.js +4 -11
- package/dist/esm/fields/Combobox/index.native.js.map +1 -1
- package/dist/esm/fields/Select/InlineChoices.mjs +2 -6
- package/dist/esm/fields/Select/InlineChoices.mjs.map +1 -1
- package/dist/esm/fields/Select/InlineChoices.native.js +2 -6
- package/dist/esm/fields/Select/InlineChoices.native.js.map +1 -1
- package/dist/esm/fields/Select/index.mjs +68 -204
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +77 -212
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +3 -3
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +3 -3
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/esm/index.mjs +3 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +3 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/CheckGlyph.mjs +15 -0
- package/dist/esm/shared/CheckGlyph.mjs.map +1 -0
- package/dist/esm/shared/CheckGlyph.native.js +16 -0
- package/dist/esm/shared/CheckGlyph.native.js.map +1 -0
- package/dist/esm/shared/checkedOnField.mjs +76 -0
- package/dist/esm/shared/checkedOnField.mjs.map +1 -0
- package/dist/esm/shared/checkedOnField.native.js +86 -0
- package/dist/esm/shared/checkedOnField.native.js.map +1 -0
- package/dist/esm/shared/floatingList.mjs +9 -5
- package/dist/esm/shared/floatingList.mjs.map +1 -1
- package/dist/esm/shared/floatingList.native.js +12 -7
- package/dist/esm/shared/floatingList.native.js.map +1 -1
- package/dist/jsx/AdaptivePopup/index.mjs +3 -2
- package/dist/jsx/AdaptivePopup/index.mjs.map +1 -1
- package/dist/jsx/AdaptivePopup/index.native.js +2 -1
- package/dist/jsx/AdaptivePopup/index.native.js.map +1 -1
- package/dist/jsx/Button/index.mjs +12 -5
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +14 -6
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/PanelHeader.mjs +3 -8
- package/dist/jsx/FloatingPanel/PanelHeader.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/PanelHeader.native.js +3 -8
- package/dist/jsx/FloatingPanel/PanelHeader.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/coverRect.mjs +1 -8
- package/dist/jsx/FloatingPanel/coverRect.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/coverRect.native.js +0 -8
- package/dist/jsx/FloatingPanel/coverRect.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/echoContext.mjs +4 -24
- package/dist/jsx/FloatingPanel/echoContext.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/echoContext.native.js +5 -28
- package/dist/jsx/FloatingPanel/echoContext.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.mjs +53 -135
- package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +216 -135
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/fields/Combobox/index.mjs +4 -11
- package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
- package/dist/jsx/fields/Combobox/index.native.js +3 -10
- package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/InlineChoices.mjs +2 -6
- package/dist/jsx/fields/Select/InlineChoices.mjs.map +1 -1
- package/dist/jsx/fields/Select/InlineChoices.native.js +4 -8
- package/dist/jsx/fields/Select/InlineChoices.native.js.map +1 -1
- package/dist/jsx/fields/Select/index.mjs +68 -204
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +74 -209
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +3 -3
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +2 -2
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/jsx/index.js +3 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +3 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +4 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/CheckGlyph.mjs +15 -0
- package/dist/jsx/shared/CheckGlyph.mjs.map +1 -0
- package/dist/jsx/shared/CheckGlyph.native.js +44 -0
- package/dist/jsx/shared/CheckGlyph.native.js.map +1 -0
- package/dist/jsx/shared/checkedOnField.mjs +76 -0
- package/dist/jsx/shared/checkedOnField.mjs.map +1 -0
- package/dist/jsx/shared/checkedOnField.native.js +117 -0
- package/dist/jsx/shared/checkedOnField.native.js.map +1 -0
- package/dist/jsx/shared/floatingList.mjs +9 -5
- package/dist/jsx/shared/floatingList.mjs.map +1 -1
- package/dist/jsx/shared/floatingList.native.js +15 -7
- package/dist/jsx/shared/floatingList.native.js.map +1 -1
- package/package.json +8 -8
- package/src/AdaptivePopup/index.tsx +6 -3
- package/src/Button/Button.iconButton.spec.tsx +39 -0
- package/src/Button/index.tsx +20 -8
- package/src/FloatingPanel/FloatingPanel.iconCover.spec.tsx +279 -0
- package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +78 -69
- package/src/FloatingPanel/FloatingPanel.seamlessCover.spec.tsx +12 -0
- package/src/FloatingPanel/PanelHeader.tsx +4 -12
- package/src/FloatingPanel/coverRect.ts +2 -15
- package/src/FloatingPanel/echoContext.ts +6 -40
- package/src/FloatingPanel/index.native.tsx +209 -144
- package/src/FloatingPanel/index.tsx +71 -187
- package/src/FloatingPanel/pointAnchor.spec.ts +1 -1
- package/src/FloatingPanel/pointAnchor.ts +4 -4
- package/src/FloatingPanel/sheetGrabber.spec.tsx +53 -0
- package/src/fields/Combobox/index.tsx +4 -9
- package/src/fields/Select/InlineChoices.tsx +2 -2
- package/src/fields/Select/Select.containingBlock.spec.tsx +131 -130
- package/src/fields/Select/Select.popUp.native.spec.tsx +276 -0
- package/src/fields/Select/Select.stories.tsx +26 -0
- package/src/fields/Select/index.tsx +84 -255
- package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +3 -3
- package/src/index.ts +2 -1
- package/src/shared/CheckGlyph.tsx +12 -0
- package/src/shared/checkedOnField.ts +116 -0
- package/src/shared/floatingList.spec.tsx +154 -1
- package/src/shared/floatingList.tsx +18 -9
- package/types/AdaptivePopup/index.d.ts.map +1 -1
- package/types/Button/index.d.ts.map +1 -1
- package/types/FloatingPanel/PanelHeader.d.ts +1 -6
- package/types/FloatingPanel/PanelHeader.d.ts.map +1 -1
- package/types/FloatingPanel/coverRect.d.ts +2 -11
- package/types/FloatingPanel/coverRect.d.ts.map +1 -1
- package/types/FloatingPanel/echoContext.d.ts +5 -22
- package/types/FloatingPanel/echoContext.d.ts.map +1 -1
- package/types/FloatingPanel/index.d.ts +22 -9
- package/types/FloatingPanel/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.native.d.ts +8 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/FloatingPanel/pointAnchor.d.ts +4 -4
- package/types/fields/Combobox/index.d.ts.map +1 -1
- package/types/fields/Select/index.d.ts.map +1 -1
- package/types/index.d.ts +2 -1
- package/types/index.d.ts.map +1 -1
- package/types/shared/CheckGlyph.d.ts +10 -0
- package/types/shared/CheckGlyph.d.ts.map +1 -0
- package/types/shared/checkedOnField.d.ts +54 -0
- package/types/shared/checkedOnField.d.ts.map +1 -0
- package/types/shared/floatingList.d.ts +5 -2
- package/types/shared/floatingList.d.ts.map +1 -1
- package/dist/cjs/FloatingPanel/iconEcho.cjs +0 -106
- package/dist/cjs/FloatingPanel/iconEcho.native.js +0 -116
- package/dist/cjs/FloatingPanel/iconEcho.native.js.map +0 -1
- package/dist/esm/FloatingPanel/iconEcho.mjs +0 -79
- package/dist/esm/FloatingPanel/iconEcho.mjs.map +0 -1
- package/dist/esm/FloatingPanel/iconEcho.native.js +0 -86
- package/dist/esm/FloatingPanel/iconEcho.native.js.map +0 -1
- package/dist/jsx/FloatingPanel/iconEcho.mjs +0 -79
- package/dist/jsx/FloatingPanel/iconEcho.mjs.map +0 -1
- package/dist/jsx/FloatingPanel/iconEcho.native.js +0 -116
- package/dist/jsx/FloatingPanel/iconEcho.native.js.map +0 -1
- package/src/FloatingPanel/FloatingPanel.iconEcho.spec.tsx +0 -357
- package/src/FloatingPanel/iconEcho.tsx +0 -124
- package/types/FloatingPanel/iconEcho.d.ts +0 -29
- package/types/FloatingPanel/iconEcho.d.ts.map +0 -1
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* so only inside another overlay, so a sheet never stacks on a sheet.
|
|
5
5
|
*/
|
|
6
6
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
7
|
-
import { act
|
|
8
|
-
import { PlusIcon
|
|
7
|
+
import { act } from "@testing-library/react";
|
|
8
|
+
import { PlusIcon } from "@phosphor-icons/react";
|
|
9
9
|
import { useState, type ReactNode } from "react";
|
|
10
10
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
11
11
|
import { Text, XStack, YStack, getTokens, getVariableValue } from "tamagui";
|
|
@@ -462,7 +462,7 @@ describe("FloatingPanel native menu face panelWidth (LC-112, FZ-8438 r3)", () =>
|
|
|
462
462
|
});
|
|
463
463
|
});
|
|
464
464
|
|
|
465
|
-
function
|
|
465
|
+
function CoverMenu() {
|
|
466
466
|
const [open, setOpen] = useState(false);
|
|
467
467
|
return (
|
|
468
468
|
<FloatingPanel
|
|
@@ -471,7 +471,7 @@ function EchoMenu({ kind }: { kind: "close" | "menu" }) {
|
|
|
471
471
|
compactFace="menu"
|
|
472
472
|
anchorMode="cover-trigger"
|
|
473
473
|
triggerEcho={false}
|
|
474
|
-
|
|
474
|
+
iconCover
|
|
475
475
|
coverAlign="auto"
|
|
476
476
|
triggerSizing="content"
|
|
477
477
|
contentPadding="none"
|
|
@@ -483,67 +483,92 @@ function EchoMenu({ kind }: { kind: "close" | "menu" }) {
|
|
|
483
483
|
);
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
const
|
|
487
|
-
document.querySelector<HTMLElement>('[data-testid="floating-panel-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
486
|
+
const clip = () =>
|
|
487
|
+
document.querySelector<HTMLElement>('[data-testid="floating-panel-menu-clip"]') as HTMLElement;
|
|
488
|
+
|
|
489
|
+
/** The live prop a component took: RN-web drops the ones it has no DOM use for. */
|
|
490
|
+
function propOf<T>(node: Element, name: string): T | undefined {
|
|
491
|
+
const key = Object.keys(node).find((candidate) => candidate.startsWith("__reactFiber$"));
|
|
492
|
+
let fiber = (node as unknown as Record<string, FiberLike | undefined>)[key ?? ""];
|
|
493
|
+
while (fiber) {
|
|
494
|
+
const value = fiber.memoizedProps?.[name];
|
|
495
|
+
if (value !== undefined) return value as T;
|
|
496
|
+
fiber = fiber.return;
|
|
497
|
+
}
|
|
498
|
+
return undefined;
|
|
499
|
+
}
|
|
500
|
+
type FiberLike = { memoizedProps?: Record<string, unknown>; return?: FiberLike };
|
|
501
|
+
|
|
502
|
+
function settleMenu() {
|
|
503
|
+
layout(menu() as HTMLElement, MENU_MIN_WIDTH, 180);
|
|
504
|
+
act(() => {
|
|
505
|
+
vi.advanceTimersByTime(1000);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
491
508
|
|
|
492
|
-
describe("LC-112 icon trigger
|
|
493
|
-
it("
|
|
509
|
+
describe("LC-112 icon trigger cover on native (FZ-8665, reversing Round 37's X)", () => {
|
|
510
|
+
it("covers the + from the button's top trailing corner and repeats nothing", () => {
|
|
494
511
|
trigger = { x: 330, y: 60, width: 44, height: 44 };
|
|
495
|
-
const result = renderWithProviders(<
|
|
512
|
+
const result = renderWithProviders(<CoverMenu />);
|
|
496
513
|
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
497
514
|
const panel = menu() as HTMLElement;
|
|
498
515
|
layout(panel, MENU_MIN_WIDTH, 180);
|
|
499
516
|
expect(panel.style.top).toBe(`${trigger.y}px`);
|
|
500
517
|
expect(panel.style.left).toBe(`${trigger.x + trigger.width - MENU_MIN_WIDTH}px`);
|
|
501
|
-
const band = iconBand() as HTMLElement;
|
|
502
|
-
expect(band.style.height).toBe("44px");
|
|
503
|
-
const box = band.firstElementChild as HTMLElement;
|
|
504
|
-
expect(box.style.width).toBe("44px");
|
|
505
|
-
expect(box.style.height).toBe("44px");
|
|
506
|
-
// The box sits where the trigger is: its start offset is the trigger's x
|
|
507
|
-
// less the menu's left edge.
|
|
508
|
-
expect(box.style.marginInlineStart || box.style.marginLeft).toBe(
|
|
509
|
-
`${trigger.x - (trigger.x + trigger.width - MENU_MIN_WIDTH)}px`,
|
|
510
|
-
);
|
|
511
|
-
const echoButton = iconEcho() as HTMLElement;
|
|
512
|
-
expect(echoButton.getAttribute("aria-label")).toBe("Close");
|
|
513
|
-
const x = render(<XIcon weight="bold" />);
|
|
514
|
-
expect(glyphPath(echoButton)).toBe(glyphPath(x.container));
|
|
515
|
-
x.unmount();
|
|
516
518
|
expect(echo()).toBeNull();
|
|
519
|
+
expect(document.querySelector('[data-testid="floating-panel-icon-echo"]')).toBeNull();
|
|
520
|
+
expect(document.querySelector('[data-testid="floating-panel-icon-echo-band"]')).toBeNull();
|
|
517
521
|
});
|
|
518
522
|
|
|
519
|
-
it("
|
|
523
|
+
it("starts the rows at the panel's top edge: the scroll view is the panel's only child", () => {
|
|
520
524
|
trigger = { x: 330, y: 60, width: 44, height: 44 };
|
|
521
|
-
const result = renderWithProviders(<
|
|
525
|
+
const result = renderWithProviders(<CoverMenu />);
|
|
522
526
|
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
523
527
|
layout(menu() as HTMLElement, MENU_MIN_WIDTH, 180);
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
+
expect(clip().children).toHaveLength(1);
|
|
529
|
+
expect(clip().textContent).toBe("Add a node");
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("closes from the backdrop, which a screen reader reads as Close", () => {
|
|
533
|
+
trigger = { x: 330, y: 60, width: 44, height: 44 };
|
|
534
|
+
const result = renderWithProviders(<CoverMenu />);
|
|
535
|
+
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
536
|
+
settleMenu();
|
|
537
|
+
tap(result.getByRole("button", { name: "Close" }));
|
|
538
|
+
expect(menu()).toBeNull();
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("closes from VoiceOver's escape gesture", () => {
|
|
542
|
+
trigger = { x: 330, y: 60, width: 44, height: 44 };
|
|
543
|
+
const result = renderWithProviders(<CoverMenu />);
|
|
544
|
+
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
545
|
+
settleMenu();
|
|
546
|
+
const escape = propOf<() => void>(frame() as HTMLElement, "onAccessibilityEscape");
|
|
547
|
+
expect(escape).toBeTypeOf("function");
|
|
548
|
+
act(() => escape?.());
|
|
528
549
|
expect(menu()).toBeNull();
|
|
529
550
|
});
|
|
530
551
|
|
|
531
|
-
it("
|
|
552
|
+
it("is modal to VoiceOver, holding only its backdrop Close and the panel", () => {
|
|
553
|
+
trigger = { x: 330, y: 60, width: 44, height: 44 };
|
|
554
|
+
const result = renderWithProviders(<CoverMenu />);
|
|
555
|
+
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
556
|
+
settleMenu();
|
|
557
|
+
const layer = document.querySelector<HTMLElement>('[data-testid="floating-panel-menu-layer"]');
|
|
558
|
+
expect(layer).not.toBeNull();
|
|
559
|
+
expect(propOf<boolean>(layer as HTMLElement, "accessibilityViewIsModal")).toBe(true);
|
|
560
|
+
expect(layer?.contains(result.getByRole("button", { name: "Close" }))).toBe(true);
|
|
561
|
+
expect(layer?.contains(menu())).toBe(true);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("still covers its button when it opens up from the bottom edge", () => {
|
|
532
565
|
trigger = { x: 20, y: 760, width: 44, height: 44 };
|
|
533
|
-
const result = renderWithProviders(<
|
|
534
|
-
|
|
535
|
-
tap(live);
|
|
566
|
+
const result = renderWithProviders(<CoverMenu />);
|
|
567
|
+
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
536
568
|
const panel = menu() as HTMLElement;
|
|
537
569
|
layout(panel, MENU_MIN_WIDTH, 300);
|
|
538
570
|
expect(parseFloat(panel.style.top) + 300).toBe(trigger.y + trigger.height);
|
|
539
|
-
|
|
540
|
-
const band = iconBand() as HTMLElement;
|
|
541
|
-
const clip = surface.querySelector('[data-testid="floating-panel-menu-clip"]') as HTMLElement;
|
|
542
|
-
expect(clip.lastElementChild).toBe(band);
|
|
543
|
-
const x = render(<XIcon weight="bold" />);
|
|
544
|
-
expect(glyphPath(iconEcho())).toBe(glyphPath(x.container));
|
|
545
|
-
x.unmount();
|
|
546
|
-
expect(glyphPath(iconEcho())).not.toBe(glyphPath(live));
|
|
571
|
+
expect(clip().children).toHaveLength(1);
|
|
547
572
|
});
|
|
548
573
|
});
|
|
549
574
|
|
|
@@ -554,11 +579,11 @@ const capsuleSurface = (plate: IconButtonGroupPlate): IconButtonGroupSurface =>
|
|
|
554
579
|
describe("LC-112 grouped glass capsule on native (FZ-8550, Round 37 second pass)", () => {
|
|
555
580
|
const plate = { x: 200, y: 50, width: 174, height: 44, radius: 22 };
|
|
556
581
|
|
|
557
|
-
it("covers the whole capsule from context at its radius
|
|
582
|
+
it("covers the whole capsule from context at its radius, showing none of its buttons", () => {
|
|
558
583
|
trigger = { x: 330, y: 50, width: 44, height: 44 };
|
|
559
584
|
const result = renderWithProviders(
|
|
560
585
|
<IconButtonGroupSurfaceContext.Provider value={capsuleSurface(plate)}>
|
|
561
|
-
<
|
|
586
|
+
<CoverMenu />
|
|
562
587
|
</IconButtonGroupSurfaceContext.Provider>,
|
|
563
588
|
);
|
|
564
589
|
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
@@ -572,18 +597,8 @@ describe("LC-112 grouped glass capsule on native (FZ-8550, Round 37 second pass)
|
|
|
572
597
|
expect(left + MENU_MIN_WIDTH).toBeGreaterThanOrEqual(plate.x + plate.width);
|
|
573
598
|
expect(left).toBe(plate.x + plate.width - MENU_MIN_WIDTH);
|
|
574
599
|
expect(corners(frame() as HTMLElement)).toEqual(radiusAtoms(plate.radius));
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
expect(band.style.height).toBe(`${plate.height}px`);
|
|
578
|
-
const box = band.firstElementChild as HTMLElement;
|
|
579
|
-
expect(box.style.width).toBe(`${trigger.width}px`);
|
|
580
|
-
expect(box.style.marginInlineStart || box.style.marginLeft).toBe(`${trigger.x - left}px`);
|
|
581
|
-
// Only the pressed trigger shows, as an X; nothing else of the group.
|
|
582
|
-
const echoes = document.querySelectorAll('[data-testid="floating-panel-icon-echo"]');
|
|
583
|
-
expect(echoes).toHaveLength(1);
|
|
584
|
-
const x = render(<XIcon weight="bold" />);
|
|
585
|
-
expect(glyphPath(echoes[0] as Element)).toBe(glyphPath(x.container));
|
|
586
|
-
x.unmount();
|
|
600
|
+
expect(clip().children).toHaveLength(1);
|
|
601
|
+
expect(clip().textContent).toBe("Add a node");
|
|
587
602
|
});
|
|
588
603
|
|
|
589
604
|
it("takes the capsule's width as its floor when the capsule is wider than the menu", () => {
|
|
@@ -591,7 +606,7 @@ describe("LC-112 grouped glass capsule on native (FZ-8550, Round 37 second pass)
|
|
|
591
606
|
trigger = { x: 330, y: 50, width: 44, height: 44 };
|
|
592
607
|
const result = renderWithProviders(
|
|
593
608
|
<IconButtonGroupSurfaceContext.Provider value={capsuleSurface(wide)}>
|
|
594
|
-
<
|
|
609
|
+
<CoverMenu />
|
|
595
610
|
</IconButtonGroupSurfaceContext.Provider>,
|
|
596
611
|
);
|
|
597
612
|
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
@@ -612,7 +627,7 @@ describe("LC-112 grouped glass capsule on native (FZ-8550, Round 37 second pass)
|
|
|
612
627
|
},
|
|
613
628
|
}}
|
|
614
629
|
>
|
|
615
|
-
<
|
|
630
|
+
<CoverMenu />
|
|
616
631
|
</IconButtonGroupSurfaceContext.Provider>,
|
|
617
632
|
);
|
|
618
633
|
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
@@ -626,20 +641,14 @@ describe("LC-112 grouped glass capsule on native (FZ-8550, Round 37 second pass)
|
|
|
626
641
|
|
|
627
642
|
it("covers a 32pt avatar at its 44pt target, so its centre is the row-icon column (FZ-8480)", () => {
|
|
628
643
|
trigger = { x: 100, y: 56, width: 32, height: 32 };
|
|
629
|
-
const result = renderWithProviders(<
|
|
644
|
+
const result = renderWithProviders(<CoverMenu />);
|
|
630
645
|
tap(result.getAllByRole("button", { name: "Add node" })[0]!);
|
|
631
646
|
const panel = menu() as HTMLElement;
|
|
632
647
|
layout(panel, MENU_MIN_WIDTH, 180);
|
|
633
648
|
const inset = (MIN_PRESS_TARGET - trigger.width) / 2;
|
|
634
649
|
expect(panel.style.left).toBe(`${trigger.x - inset}px`);
|
|
635
650
|
expect(panel.style.top).toBe(`${trigger.y - inset}px`);
|
|
636
|
-
|
|
637
|
-
expect(band.style.height).toBe(`${MIN_PRESS_TARGET}px`);
|
|
638
|
-
const box = band.firstElementChild as HTMLElement;
|
|
639
|
-
expect(box.style.width).toBe(`${trigger.width}px`);
|
|
640
|
-
expect(box.style.marginTop).toBe(`${inset}px`);
|
|
641
|
-
expect(box.style.marginInlineStart || box.style.marginLeft).toBe(`${inset}px`);
|
|
642
|
-
// The X's centre sits half a press target in: where a row's leading
|
|
651
|
+
// The avatar's centre sits half a press target in: where a row's leading
|
|
643
652
|
// glyph centres (row inset plus half its 18pt slot, on the frame border).
|
|
644
653
|
expect(inset + trigger.width / 2).toBe(MIN_PRESS_TARGET / 2);
|
|
645
654
|
});
|
|
@@ -212,6 +212,18 @@ describe("LC-112 icon trigger: the menu opens on its button (rv_d86cc201997dffd4
|
|
|
212
212
|
expect(within(panel).queryByRole("button", { name: "Appearance" })).toBeNull();
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
it("lands on a whole pixel when its button sits on a half pixel, so rows reach the far edge (FZ-8557)", async () => {
|
|
216
|
+
const button = { x: 16.5, y: 12, width: 40, height: 40 };
|
|
217
|
+
measure(button, 124.6);
|
|
218
|
+
const result = renderWithProviders(<IconPanel />);
|
|
219
|
+
fireEvent.click(result.getByRole("button", { name: "Appearance" }), { detail: 0 });
|
|
220
|
+
const panel = (await result.findByTestId("floating-panel-viewport")) as HTMLElement;
|
|
221
|
+
await waitFor(() => expect(panel.style.top).toBe(`${button.y}px`));
|
|
222
|
+
const left = parseFloat(panel.style.left);
|
|
223
|
+
expect(Number.isInteger(left)).toBe(true);
|
|
224
|
+
expect(Math.abs(left - button.x)).toBeLessThanOrEqual(0.5);
|
|
225
|
+
});
|
|
226
|
+
|
|
215
227
|
it("slides up over its button near the bottom edge instead of opening a sheet", async () => {
|
|
216
228
|
const button = { x: 16, y: window.innerHeight - 60, width: 44, height: 44 };
|
|
217
229
|
measure(button, 200);
|
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import { XStack, type XStackProps } from "tamagui";
|
|
2
|
-
import { usePanelIconEchoBox } from "./echoContext";
|
|
3
2
|
|
|
4
|
-
/**
|
|
5
|
-
* A floating panel's header row (LC-112, FZ-8480 Round 37). Over an
|
|
6
|
-
* icon-trigger panel whose X shares the header's row, it takes the X's
|
|
7
|
-
* height and centres its content on the X's line, keeping clear of the X;
|
|
8
|
-
* anywhere else it is the panel's padded top row.
|
|
9
|
-
*/
|
|
3
|
+
/** A floating panel's title row: the panel's padded top row (LC-112). */
|
|
10
4
|
export function PanelHeader({ children, ...rest }: XStackProps) {
|
|
11
|
-
const echo = usePanelIconEchoBox();
|
|
12
5
|
return (
|
|
13
6
|
<XStack
|
|
14
7
|
alignItems="center"
|
|
15
8
|
gap="$2"
|
|
16
|
-
paddingLeft=
|
|
17
|
-
paddingRight=
|
|
18
|
-
paddingTop=
|
|
19
|
-
{...(echo ? { minHeight: echo.height } : undefined)}
|
|
9
|
+
paddingLeft="$4"
|
|
10
|
+
paddingRight="$4"
|
|
11
|
+
paddingTop="$4"
|
|
20
12
|
data-fp-panel-header=""
|
|
21
13
|
{...rest}
|
|
22
14
|
>
|
|
@@ -5,8 +5,8 @@ import type { TriggerRect } from "./pointAnchor";
|
|
|
5
5
|
* The 44pt box an icon trigger is pressed in, centred on the trigger
|
|
6
6
|
* (LC-176). A trigger drawn smaller than its target (a 32pt avatar) is
|
|
7
7
|
* covered at its target, so the panel's row-icon column, which centres on
|
|
8
|
-
* a 44pt start inset, runs through the trigger's centre
|
|
9
|
-
*
|
|
8
|
+
* a 44pt start inset, runs through the trigger's centre (FZ-8480 GAP). A
|
|
9
|
+
* trigger at least that big is its own box.
|
|
10
10
|
*/
|
|
11
11
|
export function pressTargetRect(trigger: TriggerRect, min = MIN_PRESS_TARGET): TriggerRect {
|
|
12
12
|
const width = Math.max(trigger.width, min);
|
|
@@ -27,16 +27,3 @@ export function pressTargetRect(trigger: TriggerRect, min = MIN_PRESS_TARGET): T
|
|
|
27
27
|
export function iconCoverRect(trigger: TriggerRect, group: TriggerRect | null): TriggerRect {
|
|
28
28
|
return group ?? pressTargetRect(trigger);
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Where the trigger sits inside the surface its panel covers: the X box's
|
|
33
|
-
* offset from the cover's top, and the band's height. Null when the cover is
|
|
34
|
-
* the trigger itself.
|
|
35
|
-
*/
|
|
36
|
-
export function echoInCover(
|
|
37
|
-
trigger: TriggerRect,
|
|
38
|
-
cover: TriggerRect,
|
|
39
|
-
): { top: number; height: number } | null {
|
|
40
|
-
if (cover.y === trigger.y && cover.height === trigger.height) return null;
|
|
41
|
-
return { top: trigger.y - cover.y, height: cover.height };
|
|
42
|
-
}
|
|
@@ -22,46 +22,12 @@ export function usePanelTriggerRadiusReporter(): ((radius: number) => void) | nu
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* True inside a panel's sheet face (a phone's bottom sheet, web or native). A
|
|
26
|
+
* sheet isn't laid over its trigger, so LC-112's echo row has nothing to
|
|
27
|
+
* cover: a picker titles the sheet instead, per HIG (FZ-8557, Round 40).
|
|
28
28
|
*/
|
|
29
|
-
export
|
|
30
|
-
height: number;
|
|
31
|
-
left: number;
|
|
32
|
-
right: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const PanelIconEchoBoxContext = createContext<PanelIconEchoBox | null>(null);
|
|
36
|
-
|
|
37
|
-
export function usePanelIconEchoBox(): PanelIconEchoBox | null {
|
|
38
|
-
return useContext(PanelIconEchoBoxContext);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The header's clearance around an X `offset` in from the panel's start edge:
|
|
43
|
-
* the X's side keeps clear of it, the other side keeps none of its own.
|
|
44
|
-
*/
|
|
45
|
-
export function echoHeaderBox({
|
|
46
|
-
offset,
|
|
47
|
-
width,
|
|
48
|
-
height,
|
|
49
|
-
panelWidth,
|
|
50
|
-
rtl = false,
|
|
51
|
-
}: {
|
|
52
|
-
offset: number;
|
|
53
|
-
width: number;
|
|
54
|
-
height: number;
|
|
55
|
-
panelWidth: number;
|
|
56
|
-
rtl?: boolean;
|
|
57
|
-
}): PanelIconEchoBox {
|
|
58
|
-
const atStart = offset + width / 2 < panelWidth / 2;
|
|
59
|
-
const start = atStart ? Math.max(0, offset + width) : 0;
|
|
60
|
-
const end = atStart ? 0 : Math.max(0, panelWidth - offset);
|
|
61
|
-
return { height, left: rtl ? end : start, right: rtl ? start : end };
|
|
62
|
-
}
|
|
29
|
+
export const PanelSheetContext = createContext(false);
|
|
63
30
|
|
|
64
|
-
export function
|
|
65
|
-
|
|
66
|
-
return a.height === b.height && a.left === b.left && a.right === b.right;
|
|
31
|
+
export function useInPanelSheet(): boolean {
|
|
32
|
+
return useContext(PanelSheetContext);
|
|
67
33
|
}
|