@multiplatform.one/forms 7.45.0 → 7.46.0
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.native.js +1 -1
- package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
- package/dist/cjs/Button/index.native.js +1 -1
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Chip/index.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js +1 -1
- package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/index.native.js +3 -3
- package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
- package/dist/cjs/ControlGroup/index.native.js +1 -1
- package/dist/cjs/ControlGroup/index.native.js.map +1 -1
- package/dist/cjs/DescriptionList/index.native.js +2 -2
- package/dist/cjs/DescriptionList/index.native.js.map +1 -1
- package/dist/cjs/ErrorSummary.native.js.map +1 -1
- package/dist/cjs/FieldDisplay/editMode.native.js.map +1 -1
- package/dist/cjs/FieldDisplay/formatters.native.js.map +1 -1
- package/dist/cjs/FieldDisplay/index.native.js +1 -1
- package/dist/cjs/FieldDisplay/index.native.js.map +1 -1
- package/dist/cjs/FieldGroup.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/index.cjs +158 -40
- package/dist/cjs/FloatingPanel/nestedAction.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/cjs/Form/index.native.js +1 -1
- package/dist/cjs/Form/index.native.js.map +1 -1
- package/dist/cjs/Form/submissionController.native.js.map +1 -1
- package/dist/cjs/Form/validateOn.native.js.map +1 -1
- package/dist/cjs/FormGrid.native.js.map +1 -1
- package/dist/cjs/FormSection.native.js.map +1 -1
- package/dist/cjs/InputParts/index.native.js.map +1 -1
- package/dist/cjs/Label/index.native.js.map +1 -1
- package/dist/cjs/MediaPicker/base64.native.js.map +1 -1
- package/dist/cjs/MediaPicker/payload.native.js.map +1 -1
- package/dist/cjs/MediaPicker/types.native.js.map +1 -1
- package/dist/cjs/MediaPicker/useMediaPicker.native.js.map +1 -1
- package/dist/cjs/Skeleton/index.native.js.map +1 -1
- package/dist/cjs/Spinner/SpinnerGlyph.native.js.map +1 -1
- package/dist/cjs/Spinner/spinnerSize.native.js.map +1 -1
- package/dist/cjs/Table/index.native.js.map +1 -1
- package/dist/cjs/Wheel/haptic.native.js.map +1 -1
- package/dist/cjs/Wheel/index.native.js.map +1 -1
- package/dist/cjs/Wheel/wheelShared.native.js.map +1 -1
- package/dist/cjs/actionLabel.native.js.map +1 -1
- package/dist/cjs/fieldDefaults.native.js.map +1 -1
- package/dist/cjs/fieldLayout.native.js +1 -1
- package/dist/cjs/fieldLayout.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/BarcodePreview.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js +1 -1
- package/dist/cjs/fields/Barcode/code128.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/index.native.js +2 -2
- package/dist/cjs/fields/Barcode/index.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/qr.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/scannerTypes.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/Checkbox.native.js +1 -1
- package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxBox.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxCard.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/index.native.js.map +1 -1
- package/dist/cjs/fields/ColorPicker/index.native.js +3 -3
- package/dist/cjs/fields/ColorPicker/index.native.js.map +1 -1
- package/dist/cjs/fields/ColorPicker/parts.native.js +2 -2
- package/dist/cjs/fields/ColorPicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/authorOptions.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/index.native.js +6 -6
- package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/Calendar.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +3 -3
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/catalog.native.js +2 -2
- package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/fieldValueSync.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/index.native.js +2 -2
- package/dist/cjs/fields/DatePicker/parts.native.js +2 -2
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/utils.native.js.map +1 -1
- package/dist/cjs/fields/Duration/durationMath.native.js.map +1 -1
- package/dist/cjs/fields/Duration/index.native.js +3 -3
- package/dist/cjs/fields/Duration/index.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/filePickerTypes.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/index.native.js +2 -2
- package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/geoValue.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/geocoder.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/geocoderContext.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/index.native.js +5 -5
- package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/mapMath.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/mapTypes.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/photonGeocoder.native.js.map +1 -1
- package/dist/cjs/fields/Icon/drawnSet.native.js.map +1 -1
- package/dist/cjs/fields/Icon/index.native.js +3 -3
- package/dist/cjs/fields/Icon/index.native.js.map +1 -1
- package/dist/cjs/fields/ImageField/index.native.js +2 -2
- package/dist/cjs/fields/ImageField/index.native.js.map +1 -1
- package/dist/cjs/fields/Input/index.native.js +3 -3
- package/dist/cjs/fields/Input/index.native.js.map +1 -1
- package/dist/cjs/fields/MentionInput/index.native.js +3 -3
- package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
- package/dist/cjs/fields/OTPInput/index.native.js +3 -3
- package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
- package/dist/cjs/fields/OTPInput/otpCellCss.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/dialCodes.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/index.native.js +3 -3
- package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/phone.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/regionNames.native.js.map +1 -1
- package/dist/cjs/fields/Progress/index.native.js +1 -1
- package/dist/cjs/fields/Progress/index.native.js.map +1 -1
- package/dist/cjs/fields/RadioGroup/index.native.js +1 -1
- package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/index.native.js +1 -1
- package/dist/cjs/fields/Rating/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/ratingMath.native.js.map +1 -1
- package/dist/cjs/fields/Rating/types.native.js.map +1 -1
- package/dist/cjs/fields/SearchInput/index.native.js +2 -2
- package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.native.js +4 -4
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Signature/index.native.js +2 -2
- package/dist/cjs/fields/Signature/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/index.native.js +1 -1
- package/dist/cjs/fields/Slider/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/jumpMotion.native.js.map +1 -1
- package/dist/cjs/fields/Stepper/index.native.js +2 -2
- package/dist/cjs/fields/Stepper/index.native.js.map +1 -1
- package/dist/cjs/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
- package/dist/cjs/fields/Switch/footprint.native.js.map +1 -1
- package/dist/cjs/fields/Switch/index.native.js +2 -2
- package/dist/cjs/fields/Switch/index.native.js.map +1 -1
- package/dist/cjs/fields/TextArea/index.native.js +2 -2
- package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/catalog.native.js +3 -3
- package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/index.native.js +2 -2
- package/dist/cjs/fields/TimePicker/parts.native.js +1 -1
- package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
- package/dist/cjs/fields/TimeRangeScrubber/index.native.js +2 -2
- package/dist/cjs/fields/TimeRangeScrubber/index.native.js.map +1 -1
- package/dist/cjs/fields/ToggleGroup/index.native.js +1 -1
- package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/dateTimePickerLoader.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
- package/dist/cjs/fields/nativeSelect.native.js.map +1 -1
- package/dist/cjs/fields/selectedOrder.native.js.map +1 -1
- package/dist/cjs/formSpacing.native.js.map +1 -1
- package/dist/cjs/hooks/useFocusManagement.native.js.map +1 -1
- package/dist/cjs/index.native.js +44 -44
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/keyboard/KeyboardDoneBar.native.js +1 -1
- package/dist/cjs/keyboard/chain.native.js.map +1 -1
- package/dist/cjs/keyboard/index.native.js.map +1 -1
- package/dist/cjs/keyboard/types.native.js.map +1 -1
- package/dist/cjs/requiredMarking.native.js.map +1 -1
- package/dist/cjs/shared/TimeColumn.native.js +1 -1
- package/dist/cjs/shared/TimeColumn.native.js.map +1 -1
- package/dist/cjs/shared/capabilityPreflight.native.js.map +1 -1
- package/dist/cjs/shared/colorRamps.native.js.map +1 -1
- package/dist/cjs/shared/devWarn.native.js.map +1 -1
- package/dist/cjs/shared/floatingList.native.js +1 -1
- package/dist/cjs/shared/floatingList.native.js.map +1 -1
- package/dist/cjs/shared/focusManagement.cjs +3 -3
- package/dist/cjs/shared/focusManagement.native.js +3 -3
- package/dist/cjs/shared/focusManagement.native.js.map +1 -1
- package/dist/cjs/shared/groupContext.native.js.map +1 -1
- package/dist/cjs/shared/i18n.native.js.map +1 -1
- package/dist/cjs/shared/instantMotion.native.js.map +1 -1
- package/dist/cjs/shared/nativeA11y.native.js.map +1 -1
- package/dist/cjs/shared/nativeAutoFocus.native.js.map +1 -1
- package/dist/cjs/shared/panelPortal.native.js.map +1 -1
- package/dist/cjs/shared/pickerTriggerContract.native.js.map +1 -1
- package/dist/cjs/shared/pressSlopProps.native.js.map +1 -1
- package/dist/cjs/shared/t.native.js.map +1 -1
- package/dist/cjs/shared/tableCellContext.native.js.map +1 -1
- package/dist/cjs/shared/useDragScroll.native.js.map +1 -1
- package/dist/cjs/shared/useKeyboardInset.native.js.map +1 -1
- package/dist/cjs/shared/useSheetEscapeDismiss.cjs +5 -4
- package/dist/cjs/shared/useSheetEscapeDismiss.native.js +5 -3
- package/dist/cjs/shared/useSheetEscapeDismiss.native.js.map +1 -1
- package/dist/cjs/shared/useWheelIncrement.native.js.map +1 -1
- package/dist/cjs/shared/utils.native.js +1 -1
- package/dist/cjs/shared/utils.native.js.map +1 -1
- package/dist/cjs/shared/zIndex.native.js.map +1 -1
- package/dist/cjs/themedPrimitives.native.js.map +1 -1
- package/dist/cjs/types.native.js.map +1 -1
- package/dist/cjs/utils/LinkCell.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.mjs +160 -42
- package/dist/esm/FloatingPanel/index.mjs.map +1 -1
- package/dist/esm/shared/focusManagement.mjs +3 -3
- package/dist/esm/shared/focusManagement.mjs.map +1 -1
- package/dist/esm/shared/focusManagement.native.js +3 -3
- package/dist/esm/shared/focusManagement.native.js.map +1 -1
- package/dist/esm/shared/useSheetEscapeDismiss.mjs +5 -4
- package/dist/esm/shared/useSheetEscapeDismiss.mjs.map +1 -1
- package/dist/esm/shared/useSheetEscapeDismiss.native.js +5 -3
- package/dist/esm/shared/useSheetEscapeDismiss.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.mjs +160 -42
- package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/focusManagement.mjs +3 -3
- package/dist/jsx/shared/focusManagement.mjs.map +1 -1
- package/dist/jsx/shared/focusManagement.native.js +3 -3
- package/dist/jsx/shared/focusManagement.native.js.map +1 -1
- package/dist/jsx/shared/useSheetEscapeDismiss.mjs +5 -4
- package/dist/jsx/shared/useSheetEscapeDismiss.mjs.map +1 -1
- package/dist/jsx/shared/useSheetEscapeDismiss.native.js +5 -3
- package/dist/jsx/shared/useSheetEscapeDismiss.native.js.map +1 -1
- package/package.json +8 -8
- package/src/FloatingPanel/FloatingPanel.coverTrigger.spec.tsx +184 -0
- package/src/FloatingPanel/FloatingPanel.spec.tsx +3 -5
- package/src/FloatingPanel/FloatingPanel.stories.tsx +26 -0
- package/src/FloatingPanel/index.tsx +255 -59
- package/src/FloatingPanel/useFloatingPanel.spec.tsx +4 -2
- package/src/shared/focusManagement.spec.tsx +11 -1
- package/src/shared/focusManagement.ts +16 -7
- package/src/shared/interactionHooks.spec.tsx +13 -0
- package/src/shared/useSheetEscapeDismiss.ts +9 -4
- package/types/FloatingPanel/index.d.ts +9 -1
- package/types/FloatingPanel/index.d.ts.map +1 -1
- package/types/shared/focusManagement.d.ts +10 -5
- package/types/shared/focusManagement.d.ts.map +1 -1
- package/types/shared/useSheetEscapeDismiss.d.ts +1 -1
- package/types/shared/useSheetEscapeDismiss.d.ts.map +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { fireEvent, waitFor, within } from "@testing-library/react";
|
|
3
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { Preset, defaultPreset, heroPreset, boldPreset } from "@multiplatform.one/theme";
|
|
6
|
+
import { Button } from "../Button";
|
|
7
|
+
import { FloatingPanel } from "./index";
|
|
8
|
+
|
|
9
|
+
const triggerBounds = {
|
|
10
|
+
x: 80,
|
|
11
|
+
y: 240,
|
|
12
|
+
top: 240,
|
|
13
|
+
bottom: 280,
|
|
14
|
+
left: 80,
|
|
15
|
+
right: 200,
|
|
16
|
+
width: 120,
|
|
17
|
+
height: 40,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function measure(bounds = triggerBounds, panelWidth = 240, panelHeight = 180) {
|
|
21
|
+
const original = HTMLElement.prototype.getBoundingClientRect;
|
|
22
|
+
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(
|
|
23
|
+
function (this: HTMLElement) {
|
|
24
|
+
if (this.hasAttribute("data-fp-reference")) return { ...bounds, toJSON: () => ({}) };
|
|
25
|
+
return original.call(this);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
vi.spyOn(HTMLElement.prototype, "offsetWidth", "get").mockImplementation(
|
|
29
|
+
function (this: HTMLElement) {
|
|
30
|
+
return this.hasAttribute("data-fp-panel") ? panelWidth : 0;
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
vi.spyOn(HTMLElement.prototype, "scrollHeight", "get").mockImplementation(
|
|
34
|
+
function (this: HTMLElement) {
|
|
35
|
+
return this.hasAttribute("data-fp-panel") ? panelHeight : 0;
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function Harness({ scrollable }: { scrollable?: boolean }) {
|
|
41
|
+
const [open, setOpen] = useState(false);
|
|
42
|
+
return (
|
|
43
|
+
<FloatingPanel
|
|
44
|
+
open={open}
|
|
45
|
+
onOpenChange={setOpen}
|
|
46
|
+
anchorMode="cover-trigger"
|
|
47
|
+
scrollable={scrollable}
|
|
48
|
+
trigger={
|
|
49
|
+
<Button aria-label="Timeline filters">
|
|
50
|
+
<span aria-hidden="true">◇</span>Filters
|
|
51
|
+
</Button>
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
<Button>Assigned to me</Button>
|
|
55
|
+
<Button>All entries</Button>
|
|
56
|
+
</FloatingPanel>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
vi.restoreAllMocks();
|
|
62
|
+
document.documentElement.removeAttribute("dir");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("LC-112 exact cover-trigger placement", () => {
|
|
66
|
+
it("keeps an explicitly scrollable panel anchored when a full action row fits", async () => {
|
|
67
|
+
measure(triggerBounds, 240, 2000);
|
|
68
|
+
const result = renderWithProviders(<Harness scrollable />);
|
|
69
|
+
fireEvent.click(result.getByRole("button", { name: "Timeline filters" }), { detail: 0 });
|
|
70
|
+
const panel = await result.findByTestId("floating-panel-viewport");
|
|
71
|
+
expect(result.queryByTestId("floating-panel-sheet")).toBeNull();
|
|
72
|
+
expect(panel.style.top).toBe("240px");
|
|
73
|
+
const echo = within(panel).getByRole("button", { name: "Timeline filters" });
|
|
74
|
+
expect(echo.style.position).toBe("sticky");
|
|
75
|
+
});
|
|
76
|
+
it("keeps a menu floating when its visible trigger sits flush with the viewport", async () => {
|
|
77
|
+
measure({ ...triggerBounds, x: 0, left: 0, right: 120, y: 0, top: 0, bottom: 40 });
|
|
78
|
+
const result = renderWithProviders(<Harness />);
|
|
79
|
+
fireEvent.click(result.getByRole("button", { name: "Timeline filters" }), { detail: 0 });
|
|
80
|
+
const panel = await result.findByTestId("floating-panel-viewport");
|
|
81
|
+
expect(panel.style.top).toBe("0px");
|
|
82
|
+
expect(result.queryByTestId("floating-panel-sheet")).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
for (const [name, preset] of Object.entries({
|
|
85
|
+
Default: defaultPreset,
|
|
86
|
+
Hero: heroPreset,
|
|
87
|
+
Bold: boldPreset,
|
|
88
|
+
})) {
|
|
89
|
+
for (const theme of ["light", "dark"] as const) {
|
|
90
|
+
it(`preserves the measured trigger row in ${name} ${theme}`, async () => {
|
|
91
|
+
measure();
|
|
92
|
+
const result = renderWithProviders(
|
|
93
|
+
<Preset overrides={preset.knobs} theme={theme}>
|
|
94
|
+
<Harness />
|
|
95
|
+
</Preset>,
|
|
96
|
+
);
|
|
97
|
+
fireEvent.click(result.getByRole("button", { name: "Timeline filters" }), { detail: 0 });
|
|
98
|
+
const panel = await result.findByTestId("floating-panel-viewport");
|
|
99
|
+
const echo = within(panel).getByRole("button", { name: "Timeline filters" });
|
|
100
|
+
expect(echo.style.width).toBe("120px");
|
|
101
|
+
expect(echo.style.height).toBe("40px");
|
|
102
|
+
expect(echo.getAttribute("aria-expanded")).toBe("true");
|
|
103
|
+
expect(echo.querySelector("[id]")).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
it("keeps the measured first row over its trigger, exposes its name, and closes on activation", async () => {
|
|
109
|
+
measure();
|
|
110
|
+
const result = renderWithProviders(<Harness />);
|
|
111
|
+
const trigger = result.getByRole("button", { name: "Timeline filters" });
|
|
112
|
+
fireEvent.click(trigger, { detail: 0 });
|
|
113
|
+
const panel = await result.findByTestId("floating-panel-viewport");
|
|
114
|
+
await waitFor(() => expect(panel.style.top).toBe("240px"));
|
|
115
|
+
expect(panel.style.left).toBe("80px");
|
|
116
|
+
const echo = within(panel).getByRole("button", { name: "Timeline filters" });
|
|
117
|
+
expect(echo.style.width).toBe("120px");
|
|
118
|
+
expect(echo.style.height).toBe("40px");
|
|
119
|
+
expect(echo.textContent).toContain("◇");
|
|
120
|
+
await waitFor(() =>
|
|
121
|
+
expect(document.activeElement).toBe(result.getByRole("button", { name: "Assigned to me" })),
|
|
122
|
+
);
|
|
123
|
+
fireEvent.click(echo);
|
|
124
|
+
await waitFor(() => expect(result.queryByTestId("floating-panel-viewport")).toBeNull());
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("uses a bottom sheet at the bottom edge instead of moving the first row upward", async () => {
|
|
128
|
+
measure({ ...triggerBounds, y: 730, top: 730, bottom: 770 });
|
|
129
|
+
const result = renderWithProviders(<Harness />);
|
|
130
|
+
const trigger = result.getByRole("button", { name: "Timeline filters" });
|
|
131
|
+
trigger.focus();
|
|
132
|
+
const parentChildren = trigger.closest("[data-fp-reference]")?.parentElement?.children.length;
|
|
133
|
+
fireEvent.click(trigger, { detail: 0 });
|
|
134
|
+
await result.findByTestId("floating-panel-sheet");
|
|
135
|
+
expect(result.queryByTestId("floating-panel-viewport")).toBeNull();
|
|
136
|
+
expect(result.getByRole("button", { name: "Assigned to me" })).toBeTruthy();
|
|
137
|
+
expect(result.getByRole("button", { name: "Timeline filters" })).toBe(trigger);
|
|
138
|
+
expect(trigger.closest("[data-fp-reference]")?.parentElement?.children.length).toBe(
|
|
139
|
+
parentChildren,
|
|
140
|
+
);
|
|
141
|
+
fireEvent.keyDown(document, { key: "Escape" });
|
|
142
|
+
await waitFor(() => expect(document.activeElement).toBe(trigger));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("closes the collision sheet on Escape while the trigger's tooltip is open", async () => {
|
|
146
|
+
measure({ ...triggerBounds, y: 730, top: 730, bottom: 770 });
|
|
147
|
+
const tooltipEscape = (event: KeyboardEvent) => {
|
|
148
|
+
if (event.key === "Escape") event.stopPropagation();
|
|
149
|
+
};
|
|
150
|
+
document.addEventListener("keydown", tooltipEscape);
|
|
151
|
+
try {
|
|
152
|
+
const result = renderWithProviders(<Harness />);
|
|
153
|
+
const trigger = result.getByRole("button", { name: "Timeline filters" });
|
|
154
|
+
fireEvent.click(trigger, { detail: 0 });
|
|
155
|
+
await result.findByTestId("floating-panel-sheet");
|
|
156
|
+
fireEvent.keyDown(trigger, { key: "Escape" });
|
|
157
|
+
await waitFor(() => expect(result.queryByTestId("floating-panel-sheet")).toBeNull());
|
|
158
|
+
} finally {
|
|
159
|
+
document.removeEventListener("keydown", tooltipEscape);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("uses a bottom sheet for horizontal collisions instead of shifting the trigger row", async () => {
|
|
164
|
+
measure({ ...triggerBounds, x: 900, left: 900, right: 1020 });
|
|
165
|
+
const result = renderWithProviders(<Harness />);
|
|
166
|
+
fireEvent.click(result.getByRole("button", { name: "Timeline filters" }), { detail: 0 });
|
|
167
|
+
await result.findByTestId("floating-panel-sheet");
|
|
168
|
+
expect(result.queryByTestId("floating-panel-viewport")).toBeNull();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("aligns the trigger row at the logical start edge in RTL", async () => {
|
|
172
|
+
document.documentElement.dir = "rtl";
|
|
173
|
+
measure({ ...triggerBounds, x: 400, left: 400, right: 520 });
|
|
174
|
+
const result = renderWithProviders(<Harness />);
|
|
175
|
+
(result.container.querySelector("[data-fp-reference]") as HTMLElement).style.direction = "rtl";
|
|
176
|
+
fireEvent.click(result.getByRole("button", { name: "Timeline filters" }), { detail: 0 });
|
|
177
|
+
const panel = await result.findByTestId("floating-panel-viewport");
|
|
178
|
+
await waitFor(() => expect(panel.style.left).toBe("280px"));
|
|
179
|
+
expect(panel.style.transformOrigin).toBe("top right");
|
|
180
|
+
const echo = within(panel).getByRole("button", { name: "Timeline filters" });
|
|
181
|
+
expect(echo.style.alignSelf).toBe("flex-start");
|
|
182
|
+
expect(panel.dir).toBe("rtl");
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -4,7 +4,7 @@ import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
|
4
4
|
import { useState, type ReactNode } from "react";
|
|
5
5
|
import { Text } from "tamagui";
|
|
6
6
|
import { Button } from "../Button";
|
|
7
|
-
import { FloatingPanel
|
|
7
|
+
import { FloatingPanel } from "./index";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The trigger contract, and why every case fires a REAL press rather than
|
|
@@ -436,9 +436,7 @@ describe("FloatingPanel nesting (MPO-221)", () => {
|
|
|
436
436
|
await waitFor(() => expect(floatingViewport()).not.toBeNull());
|
|
437
437
|
const viewport = floatingViewport() as HTMLElement;
|
|
438
438
|
expect(scroller.contains(viewport)).toBe(false);
|
|
439
|
-
|
|
440
|
-
// viewport padding, now in viewport coordinates with no scroller offset.
|
|
441
|
-
await waitFor(() => expect(viewport.style.top).toBe(`${panelViewportPadding}px`));
|
|
439
|
+
await waitFor(() => expect(viewport.style.top).toBe("0px"));
|
|
442
440
|
});
|
|
443
441
|
|
|
444
442
|
it("offsets its top by a transformed dialog it stays inside", async () => {
|
|
@@ -459,7 +457,7 @@ describe("FloatingPanel nesting (MPO-221)", () => {
|
|
|
459
457
|
expect(dialog.contains(viewport)).toBe(true);
|
|
460
458
|
// Laid out against the dialog, whose content is scrolled 40px, its css
|
|
461
459
|
// top must be 40px lower to land at the same viewport y.
|
|
462
|
-
await waitFor(() => expect(viewport.style.top).toBe(
|
|
460
|
+
await waitFor(() => expect(viewport.style.top).toBe("40px"));
|
|
463
461
|
});
|
|
464
462
|
});
|
|
465
463
|
|
|
@@ -52,6 +52,32 @@ export const Basic: Story = {
|
|
|
52
52
|
},
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
export const CoverTriggerEdges: Story = {
|
|
56
|
+
render: () => {
|
|
57
|
+
const [open, setOpen] = useState(false);
|
|
58
|
+
return (
|
|
59
|
+
<YStack
|
|
60
|
+
height="100%"
|
|
61
|
+
minHeight="$20"
|
|
62
|
+
padding="$4"
|
|
63
|
+
justifyContent="flex-end"
|
|
64
|
+
alignItems="flex-end"
|
|
65
|
+
>
|
|
66
|
+
<FloatingPanel
|
|
67
|
+
open={open}
|
|
68
|
+
onOpenChange={setOpen}
|
|
69
|
+
anchorMode="cover-trigger"
|
|
70
|
+
triggerSizing="content"
|
|
71
|
+
trigger={<Button>Edge menu</Button>}
|
|
72
|
+
>
|
|
73
|
+
<Button onPress={() => setOpen(false)}>First action</Button>
|
|
74
|
+
<Button onPress={() => setOpen(false)}>Second action</Button>
|
|
75
|
+
</FloatingPanel>
|
|
76
|
+
</YStack>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
55
81
|
export const ScrollableContent: Story = {
|
|
56
82
|
render: () => {
|
|
57
83
|
const [open, setOpen] = useState(false);
|