@multiplatform.one/forms 7.70.0 → 7.72.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/Button/index.cjs +16 -0
- package/dist/cjs/Button/index.native.js +16 -0
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/echoContext.cjs +39 -0
- package/dist/cjs/FloatingPanel/echoContext.native.js +42 -0
- package/dist/cjs/FloatingPanel/echoContext.native.js.map +1 -0
- package/dist/cjs/FloatingPanel/index.cjs +212 -80
- package/dist/cjs/FloatingPanel/index.native.js +92 -17
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/panelWidth.cjs +92 -0
- package/dist/cjs/FloatingPanel/panelWidth.native.js +99 -0
- package/dist/cjs/FloatingPanel/panelWidth.native.js.map +1 -0
- package/dist/cjs/FloatingPanel/pointAnchor.cjs +15 -5
- package/dist/cjs/FloatingPanel/pointAnchor.native.js +15 -5
- package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/useFloatingPanel.cjs +58 -4
- package/dist/cjs/FloatingPanel/useFloatingPanel.native.js +69 -4
- package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/cjs/InputParts/index.cjs +20 -1
- package/dist/cjs/InputParts/index.native.js +22 -2
- package/dist/cjs/InputParts/index.native.js.map +1 -1
- package/dist/cjs/Wheel/NativeWheel.native.js +1 -1
- package/dist/cjs/Wheel/NativeWheel.native.js.map +1 -1
- package/dist/cjs/Wheel/index.cjs +2 -1
- package/dist/cjs/Wheel/index.native.js +4 -1
- package/dist/cjs/Wheel/index.native.js.map +1 -1
- package/dist/cjs/Wheel/wheelShared.cjs +6 -1
- package/dist/cjs/Wheel/wheelShared.native.js +7 -1
- package/dist/cjs/Wheel/wheelShared.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/index.cjs +1 -0
- package/dist/cjs/fields/Combobox/index.native.js +1 -0
- package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +6 -30
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/catalog.cjs +2 -28
- package/dist/cjs/fields/DatePicker/catalog.native.js +2 -30
- package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/parts.cjs +11 -20
- package/dist/cjs/fields/DatePicker/parts.native.js +13 -22
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/SearchInput/index.cjs +23 -1
- package/dist/cjs/fields/SearchInput/index.native.js +23 -1
- package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.cjs +65 -44
- package/dist/cjs/fields/Select/index.native.js +69 -47
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/shouldInlineChoices.cjs +3 -1
- package/dist/cjs/fields/Select/shouldInlineChoices.native.js +3 -1
- package/dist/cjs/fields/Select/shouldInlineChoices.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/catalog.cjs +4 -25
- package/dist/cjs/fields/TimePicker/catalog.native.js +7 -29
- package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/parts.cjs +8 -21
- package/dist/cjs/fields/TimePicker/parts.native.js +8 -21
- package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/floatingList.cjs +13 -4
- package/dist/cjs/shared/floatingList.native.js +15 -4
- package/dist/cjs/shared/floatingList.native.js.map +1 -1
- package/dist/esm/Button/index.mjs +17 -1
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +17 -1
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/echoContext.mjs +11 -0
- package/dist/esm/FloatingPanel/echoContext.mjs.map +1 -0
- package/dist/esm/FloatingPanel/echoContext.native.js +11 -0
- package/dist/esm/FloatingPanel/echoContext.native.js.map +1 -0
- package/dist/esm/FloatingPanel/index.mjs +213 -83
- package/dist/esm/FloatingPanel/index.mjs.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +92 -19
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/panelWidth.mjs +63 -0
- package/dist/esm/FloatingPanel/panelWidth.mjs.map +1 -0
- package/dist/esm/FloatingPanel/panelWidth.native.js +67 -0
- package/dist/esm/FloatingPanel/panelWidth.native.js.map +1 -0
- package/dist/esm/FloatingPanel/pointAnchor.mjs +15 -5
- package/dist/esm/FloatingPanel/pointAnchor.mjs.map +1 -1
- package/dist/esm/FloatingPanel/pointAnchor.native.js +15 -5
- package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
- package/dist/esm/FloatingPanel/useFloatingPanel.mjs +58 -5
- package/dist/esm/FloatingPanel/useFloatingPanel.mjs.map +1 -1
- package/dist/esm/FloatingPanel/useFloatingPanel.native.js +69 -5
- package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/esm/InputParts/index.mjs +21 -2
- package/dist/esm/InputParts/index.mjs.map +1 -1
- package/dist/esm/InputParts/index.native.js +23 -3
- package/dist/esm/InputParts/index.native.js.map +1 -1
- package/dist/esm/Wheel/NativeWheel.native.js +1 -1
- package/dist/esm/Wheel/NativeWheel.native.js.map +1 -1
- package/dist/esm/Wheel/index.mjs +2 -1
- package/dist/esm/Wheel/index.mjs.map +1 -1
- package/dist/esm/Wheel/index.native.js +4 -1
- package/dist/esm/Wheel/index.native.js.map +1 -1
- package/dist/esm/Wheel/wheelShared.mjs +6 -1
- package/dist/esm/Wheel/wheelShared.mjs.map +1 -1
- package/dist/esm/Wheel/wheelShared.native.js +7 -1
- package/dist/esm/Wheel/wheelShared.native.js.map +1 -1
- package/dist/esm/fields/Combobox/index.mjs +1 -0
- package/dist/esm/fields/Combobox/index.mjs.map +1 -1
- package/dist/esm/fields/Combobox/index.native.js +1 -0
- package/dist/esm/fields/Combobox/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/catalog.mjs +2 -28
- package/dist/esm/fields/DatePicker/catalog.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/catalog.native.js +2 -30
- package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/parts.mjs +11 -20
- package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/parts.native.js +13 -22
- package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/SearchInput/index.mjs +25 -3
- package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
- package/dist/esm/fields/SearchInput/index.native.js +25 -3
- package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
- package/dist/esm/fields/Select/index.mjs +66 -45
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +70 -48
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/Select/shouldInlineChoices.mjs +3 -1
- package/dist/esm/fields/Select/shouldInlineChoices.mjs.map +1 -1
- package/dist/esm/fields/Select/shouldInlineChoices.native.js +3 -1
- package/dist/esm/fields/Select/shouldInlineChoices.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/catalog.mjs +4 -25
- package/dist/esm/fields/TimePicker/catalog.mjs.map +1 -1
- package/dist/esm/fields/TimePicker/catalog.native.js +7 -29
- package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/parts.mjs +8 -21
- package/dist/esm/fields/TimePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/TimePicker/parts.native.js +8 -21
- package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/floatingList.mjs +14 -5
- package/dist/esm/shared/floatingList.mjs.map +1 -1
- package/dist/esm/shared/floatingList.native.js +16 -5
- package/dist/esm/shared/floatingList.native.js.map +1 -1
- package/dist/jsx/Button/index.mjs +17 -1
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +16 -0
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/echoContext.mjs +11 -0
- package/dist/jsx/FloatingPanel/echoContext.mjs.map +1 -0
- package/dist/jsx/FloatingPanel/echoContext.native.js +42 -0
- package/dist/jsx/FloatingPanel/echoContext.native.js.map +1 -0
- package/dist/jsx/FloatingPanel/index.mjs +213 -83
- package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +92 -17
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/panelWidth.mjs +63 -0
- package/dist/jsx/FloatingPanel/panelWidth.mjs.map +1 -0
- package/dist/jsx/FloatingPanel/panelWidth.native.js +99 -0
- package/dist/jsx/FloatingPanel/panelWidth.native.js.map +1 -0
- package/dist/jsx/FloatingPanel/pointAnchor.mjs +15 -5
- package/dist/jsx/FloatingPanel/pointAnchor.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/pointAnchor.native.js +15 -5
- package/dist/jsx/FloatingPanel/pointAnchor.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/useFloatingPanel.mjs +58 -5
- package/dist/jsx/FloatingPanel/useFloatingPanel.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/useFloatingPanel.native.js +69 -4
- package/dist/jsx/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/jsx/InputParts/index.mjs +21 -2
- package/dist/jsx/InputParts/index.mjs.map +1 -1
- package/dist/jsx/InputParts/index.native.js +22 -2
- package/dist/jsx/InputParts/index.native.js.map +1 -1
- package/dist/jsx/Wheel/NativeWheel.native.js +1 -1
- package/dist/jsx/Wheel/NativeWheel.native.js.map +1 -1
- package/dist/jsx/Wheel/index.mjs +2 -1
- package/dist/jsx/Wheel/index.mjs.map +1 -1
- package/dist/jsx/Wheel/index.native.js +4 -1
- package/dist/jsx/Wheel/index.native.js.map +1 -1
- package/dist/jsx/Wheel/wheelShared.mjs +6 -1
- package/dist/jsx/Wheel/wheelShared.mjs.map +1 -1
- package/dist/jsx/Wheel/wheelShared.native.js +7 -1
- package/dist/jsx/Wheel/wheelShared.native.js.map +1 -1
- package/dist/jsx/fields/Combobox/index.mjs +1 -0
- package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
- package/dist/jsx/fields/Combobox/index.native.js +1 -0
- package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +6 -30
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +11 -34
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/catalog.mjs +2 -28
- package/dist/jsx/fields/DatePicker/catalog.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/catalog.native.js +2 -30
- package/dist/jsx/fields/DatePicker/catalog.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.mjs +11 -20
- package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.native.js +13 -22
- package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/jsx/fields/SearchInput/index.mjs +25 -3
- package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
- package/dist/jsx/fields/SearchInput/index.native.js +23 -1
- package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/index.mjs +66 -45
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +69 -47
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/shouldInlineChoices.mjs +3 -1
- package/dist/jsx/fields/Select/shouldInlineChoices.mjs.map +1 -1
- package/dist/jsx/fields/Select/shouldInlineChoices.native.js +3 -1
- package/dist/jsx/fields/Select/shouldInlineChoices.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/catalog.mjs +4 -25
- package/dist/jsx/fields/TimePicker/catalog.mjs.map +1 -1
- package/dist/jsx/fields/TimePicker/catalog.native.js +7 -29
- package/dist/jsx/fields/TimePicker/catalog.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/parts.mjs +8 -21
- package/dist/jsx/fields/TimePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/TimePicker/parts.native.js +8 -21
- package/dist/jsx/fields/TimePicker/parts.native.js.map +1 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/floatingList.mjs +14 -5
- package/dist/jsx/shared/floatingList.mjs.map +1 -1
- package/dist/jsx/shared/floatingList.native.js +15 -4
- package/dist/jsx/shared/floatingList.native.js.map +1 -1
- package/package.json +8 -8
- package/src/Button/index.tsx +22 -0
- package/src/FloatingPanel/FloatingPanel.echoStretch.spec.tsx +67 -0
- package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +193 -2
- package/src/FloatingPanel/FloatingPanel.seamlessCover.spec.tsx +237 -0
- package/src/FloatingPanel/echoContext.ts +22 -0
- package/src/FloatingPanel/index.native.tsx +106 -9
- package/src/FloatingPanel/index.tsx +310 -102
- package/src/FloatingPanel/panelWidth.spec.ts +158 -0
- package/src/FloatingPanel/panelWidth.ts +122 -0
- package/src/FloatingPanel/panelWidthContract.spec.ts +10 -10
- package/src/FloatingPanel/pointAnchor.spec.ts +64 -2
- package/src/FloatingPanel/pointAnchor.ts +28 -3
- package/src/FloatingPanel/useFloatingPanel.spec.tsx +124 -4
- package/src/FloatingPanel/useFloatingPanel.ts +106 -33
- package/src/InputParts/index.tsx +30 -1
- package/src/Wheel/NativeWheel.native.tsx +1 -1
- package/src/Wheel/Wheel.spec.tsx +9 -0
- package/src/Wheel/index.tsx +12 -3
- package/src/Wheel/wheelShared.ts +6 -2
- package/src/fields/Combobox/index.tsx +1 -0
- package/src/fields/DatePicker/DatePicker.stories.tsx +3 -3
- package/src/fields/DatePicker/DatetimePicker.catalog.tsx +17 -45
- package/src/fields/DatePicker/DatetimePicker.native.tsx +3 -0
- package/src/fields/DatePicker/catalog.tsx +8 -39
- package/src/fields/DatePicker/index.native.tsx +3 -0
- package/src/fields/DatePicker/parts.tsx +13 -23
- package/src/fields/DatePicker/seamlessCover.spec.tsx +180 -0
- package/src/fields/DatePicker/touchWeb.spec.tsx +109 -167
- package/src/fields/SearchInput/SearchInput.spec.tsx +40 -0
- package/src/fields/SearchInput/index.tsx +47 -2
- package/src/fields/Select/Select.containingBlock.spec.tsx +191 -0
- package/src/fields/Select/Select.stories.tsx +43 -0
- package/src/fields/Select/index.tsx +109 -45
- package/src/fields/Select/shouldInlineChoices.spec.ts +13 -0
- package/src/fields/Select/shouldInlineChoices.ts +6 -0
- package/src/fields/TimePicker/catalog.tsx +12 -33
- package/src/fields/TimePicker/index.native.tsx +2 -0
- package/src/fields/TimePicker/parts.tsx +6 -20
- package/src/index.ts +1 -0
- package/src/shared/floatingList.tsx +21 -5
- package/types/Button/index.d.ts.map +1 -1
- package/types/FloatingPanel/echoContext.d.ts +14 -0
- package/types/FloatingPanel/echoContext.d.ts.map +1 -0
- package/types/FloatingPanel/index.d.ts +59 -0
- package/types/FloatingPanel/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.native.d.ts +9 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/FloatingPanel/panelWidth.d.ts +71 -0
- package/types/FloatingPanel/panelWidth.d.ts.map +1 -0
- package/types/FloatingPanel/pointAnchor.d.ts +8 -1
- package/types/FloatingPanel/pointAnchor.d.ts.map +1 -1
- package/types/FloatingPanel/useFloatingPanel.d.ts +33 -2
- package/types/FloatingPanel/useFloatingPanel.d.ts.map +1 -1
- package/types/InputParts/index.d.ts +5 -1
- package/types/InputParts/index.d.ts.map +1 -1
- package/types/Wheel/index.d.ts.map +1 -1
- package/types/Wheel/wheelShared.d.ts +2 -1
- package/types/Wheel/wheelShared.d.ts.map +1 -1
- package/types/fields/Combobox/index.d.ts.map +1 -1
- package/types/fields/DatePicker/DatetimePicker.catalog.d.ts +6 -0
- package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -1
- package/types/fields/DatePicker/DatetimePicker.native.d.ts +3 -0
- package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -1
- package/types/fields/DatePicker/catalog.d.ts +6 -0
- package/types/fields/DatePicker/catalog.d.ts.map +1 -1
- package/types/fields/DatePicker/index.native.d.ts +3 -0
- package/types/fields/DatePicker/index.native.d.ts.map +1 -1
- package/types/fields/DatePicker/parts.d.ts +5 -7
- package/types/fields/DatePicker/parts.d.ts.map +1 -1
- package/types/fields/Input/index.d.ts +3 -1
- package/types/fields/Input/index.d.ts.map +1 -1
- package/types/fields/SearchInput/index.d.ts +11 -0
- package/types/fields/SearchInput/index.d.ts.map +1 -1
- package/types/fields/Select/index.d.ts +8 -1
- package/types/fields/Select/index.d.ts.map +1 -1
- package/types/fields/Select/shouldInlineChoices.d.ts +5 -1
- package/types/fields/Select/shouldInlineChoices.d.ts.map +1 -1
- package/types/fields/TimePicker/catalog.d.ts +7 -1
- package/types/fields/TimePicker/catalog.d.ts.map +1 -1
- package/types/fields/TimePicker/index.native.d.ts +2 -0
- package/types/fields/TimePicker/index.native.d.ts.map +1 -1
- package/types/fields/TimePicker/parts.d.ts +1 -3
- package/types/fields/TimePicker/parts.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/shared/floatingList.d.ts +5 -1
- package/types/shared/floatingList.d.ts.map +1 -1
- package/dist/cjs/shared/touchWebPicker.cjs +0 -136
- package/dist/cjs/shared/touchWebPicker.native.js +0 -154
- package/dist/cjs/shared/touchWebPicker.native.js.map +0 -1
- package/dist/esm/shared/touchWebPicker.mjs +0 -105
- package/dist/esm/shared/touchWebPicker.mjs.map +0 -1
- package/dist/esm/shared/touchWebPicker.native.js +0 -120
- package/dist/esm/shared/touchWebPicker.native.js.map +0 -1
- package/dist/jsx/shared/touchWebPicker.mjs +0 -105
- package/dist/jsx/shared/touchWebPicker.mjs.map +0 -1
- package/dist/jsx/shared/touchWebPicker.native.js +0 -154
- package/dist/jsx/shared/touchWebPicker.native.js.map +0 -1
- package/src/shared/touchWebPicker.spec.ts +0 -98
- package/src/shared/touchWebPicker.tsx +0 -158
- package/types/shared/touchWebPicker.d.ts +0 -40
- package/types/shared/touchWebPicker.d.ts.map +0 -1
|
@@ -27,6 +27,7 @@ import { AnimatePresence, SizableText, Text, View, isWeb } from "tamagui";
|
|
|
27
27
|
|
|
28
28
|
import { FloatingPanel, floatingPanelActionProps, isNestedPanelAction } from "../../FloatingPanel";
|
|
29
29
|
import type { PanelActionEvent } from "../../FloatingPanel/nestedAction";
|
|
30
|
+
import type { PanelWidth } from "../../FloatingPanel/panelWidth";
|
|
30
31
|
import { Input as InputParts } from "../../InputParts";
|
|
31
32
|
import { formCommonColors } from "../../shared/colorRamps";
|
|
32
33
|
import { t } from "../../shared/t";
|
|
@@ -861,11 +862,6 @@ interface DatePickerInputTriggerProps {
|
|
|
861
862
|
"aria-describedby"?: string;
|
|
862
863
|
"aria-required"?: boolean;
|
|
863
864
|
"aria-invalid"?: boolean;
|
|
864
|
-
/**
|
|
865
|
-
* Touch web (LC-266): the browser's own picker input, laid over the box.
|
|
866
|
-
* The input then owns the tap, the focus and the field's a11y name.
|
|
867
|
-
*/
|
|
868
|
-
touchInput?: ReactNode;
|
|
869
865
|
}
|
|
870
866
|
|
|
871
867
|
export function DatePickerClearButton({
|
|
@@ -905,7 +901,6 @@ export function DatePickerInputTrigger({
|
|
|
905
901
|
"aria-describedby": ariaDescribedBy,
|
|
906
902
|
"aria-required": ariaRequired,
|
|
907
903
|
"aria-invalid": ariaInvalid,
|
|
908
|
-
touchInput,
|
|
909
904
|
}: DatePickerInputTriggerProps) {
|
|
910
905
|
const handleTriggerKey = (
|
|
911
906
|
e: PanelActionEvent & { key?: string; code?: string; preventDefault?: () => void },
|
|
@@ -948,13 +943,10 @@ export function DatePickerInputTrigger({
|
|
|
948
943
|
// on the closed text-field trigger.
|
|
949
944
|
elevation={undefined}
|
|
950
945
|
data-testid="date-picker-trigger"
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
position={touchInput ? "relative" : undefined}
|
|
954
|
-
id={touchInput ? undefined : id}
|
|
955
|
-
onPress={isWeb && !disabled && !touchInput ? onButtonPress : undefined}
|
|
946
|
+
id={id}
|
|
947
|
+
onPress={isWeb && !disabled ? onButtonPress : undefined}
|
|
956
948
|
{...(disabled ? { opacity: 0.5, pointerEvents: "none" } : undefined)}
|
|
957
|
-
{...(isWeb
|
|
949
|
+
{...(isWeb
|
|
958
950
|
? {
|
|
959
951
|
role: "combobox" as const,
|
|
960
952
|
"aria-haspopup": "dialog",
|
|
@@ -969,7 +961,6 @@ export function DatePickerInputTrigger({
|
|
|
969
961
|
}
|
|
970
962
|
: undefined)}
|
|
971
963
|
>
|
|
972
|
-
{touchInput}
|
|
973
964
|
<InputParts.Section data-testid="date-picker-glyph">
|
|
974
965
|
<InputParts.Icon adornment="leading" color={formCommonColors.muted}>
|
|
975
966
|
<Glyph />
|
|
@@ -980,7 +971,6 @@ export function DatePickerInputTrigger({
|
|
|
980
971
|
readOnly
|
|
981
972
|
pointerEvents="none"
|
|
982
973
|
tabIndex={-1}
|
|
983
|
-
aria-hidden={touchInput ? true : undefined}
|
|
984
974
|
value={value}
|
|
985
975
|
placeholder={placeholder}
|
|
986
976
|
disabled={disabled}
|
|
@@ -990,14 +980,7 @@ export function DatePickerInputTrigger({
|
|
|
990
980
|
</InputParts.Section>
|
|
991
981
|
{value && isWeb ? (
|
|
992
982
|
<InputParts.Section>
|
|
993
|
-
{
|
|
994
|
-
// Above the touch input, so Clear stays reachable on the box.
|
|
995
|
-
<View zIndex={2}>
|
|
996
|
-
<DatePickerClearButton onReset={onReset} disabled={disabled} />
|
|
997
|
-
</View>
|
|
998
|
-
) : (
|
|
999
|
-
<DatePickerClearButton onReset={onReset} disabled={disabled} />
|
|
1000
|
-
)}
|
|
983
|
+
<DatePickerClearButton onReset={onReset} disabled={disabled} />
|
|
1001
984
|
</InputParts.Section>
|
|
1002
985
|
) : null}
|
|
1003
986
|
</InputParts.Box>
|
|
@@ -1078,6 +1061,8 @@ interface DatePickerPopoverShellProps {
|
|
|
1078
1061
|
/** When set, native hosts render Clear outside the pointerEvents-none trigger. */
|
|
1079
1062
|
clearValue?: string;
|
|
1080
1063
|
onClear?: () => void;
|
|
1064
|
+
/** How wide the open picker is (LC-112, FZ-8438 r3). Unset: at least the field. */
|
|
1065
|
+
panelWidth?: PanelWidth;
|
|
1081
1066
|
}
|
|
1082
1067
|
|
|
1083
1068
|
export function DatePickerPopoverShell({
|
|
@@ -1090,6 +1075,7 @@ export function DatePickerPopoverShell({
|
|
|
1090
1075
|
triggerA11y,
|
|
1091
1076
|
clearValue,
|
|
1092
1077
|
onClear,
|
|
1078
|
+
panelWidth,
|
|
1093
1079
|
}: DatePickerPopoverShellProps) {
|
|
1094
1080
|
const nativeClear =
|
|
1095
1081
|
!isWeb && clearValue && onClear ? (
|
|
@@ -1104,7 +1090,11 @@ export function DatePickerPopoverShell({
|
|
|
1104
1090
|
disabled={disabled}
|
|
1105
1091
|
sizing="grow"
|
|
1106
1092
|
contentPadding="none"
|
|
1107
|
-
|
|
1093
|
+
// LC-112 seamless cover (FZ-8438): the panel is the field grown open,
|
|
1094
|
+
// at least its width, on every web width and pointer.
|
|
1095
|
+
widthMode="at-least-trigger"
|
|
1096
|
+
panelWidth={panelWidth}
|
|
1097
|
+
compactFace={isWeb ? "menu" : undefined}
|
|
1108
1098
|
triggerA11y={triggerA11y}
|
|
1109
1099
|
>
|
|
1110
1100
|
{children}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LC-112 seamless cover for the DatePicker family (FZ-8438). Clay rated deck
|
|
3
|
+
* rv_a07d4308a6c445b6 0 good: the date, time and date-and-time panels must
|
|
4
|
+
* open as one surface exactly over their field, at desktop and phone width,
|
|
5
|
+
* on a fine pointer and on touch alike.
|
|
6
|
+
*
|
|
7
|
+
* The browser half is emulated where happy-dom has no layout: the field's
|
|
8
|
+
* rect, and the arc its Box draws (read off the Box's own radius atom, the
|
|
9
|
+
* value a browser would compute from it).
|
|
10
|
+
*/
|
|
11
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
12
|
+
import { act, fireEvent, waitFor } from "@testing-library/react";
|
|
13
|
+
import { getTokens, getVariableValue } from "tamagui";
|
|
14
|
+
import type { ReactElement } from "react";
|
|
15
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
16
|
+
import { TimePicker } from "../TimePicker";
|
|
17
|
+
import { DatePicker, DatetimePicker } from "./index";
|
|
18
|
+
|
|
19
|
+
type Rect = { x: number; y: number; width: number; height: number };
|
|
20
|
+
|
|
21
|
+
const viewports: Record<string, { width: number; height: number; field: Rect }> = {
|
|
22
|
+
"1440": { width: 1440, height: 900, field: { x: 16, y: 42, width: 400, height: 40 } },
|
|
23
|
+
"390": { width: 390, height: 844, field: { x: 16, y: 42, width: 358, height: 44 } },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const pickers: [string, () => ReactElement][] = [
|
|
27
|
+
["DatePicker", () => <DatePicker label="Date" name="date" value={new Date(2026, 2, 5)} />],
|
|
28
|
+
["TimePicker", () => <TimePicker label="Time" name="time" value="14:30" />],
|
|
29
|
+
[
|
|
30
|
+
"DatetimePicker",
|
|
31
|
+
() => (
|
|
32
|
+
<DatetimePicker label="Date and time" name="starts" value={new Date(2026, 2, 5, 14, 30)} />
|
|
33
|
+
),
|
|
34
|
+
],
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const originalWidth = window.innerWidth;
|
|
38
|
+
const originalHeight = window.innerHeight;
|
|
39
|
+
|
|
40
|
+
function setViewport(width: number, height: number) {
|
|
41
|
+
Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
|
|
42
|
+
Object.defineProperty(window, "innerHeight", { configurable: true, value: height });
|
|
43
|
+
act(() => {
|
|
44
|
+
window.dispatchEvent(new Event("resize"));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The painted Box of each picker's trigger (the field the user sees). */
|
|
49
|
+
function isFieldBox(el: Element): boolean {
|
|
50
|
+
return (
|
|
51
|
+
el.matches('[data-testid="date-picker-trigger"]') ||
|
|
52
|
+
!!el.parentElement?.matches('[data-testid="time-picker-trigger"]')
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function atomRadius(el: Element): number {
|
|
57
|
+
for (const c of Array.from(el.classList)) {
|
|
58
|
+
const px = c.match(/^_btlr-(\d+(?:\.\d+)?)px$/);
|
|
59
|
+
if (px) return Number(px[1]);
|
|
60
|
+
const token = c.match(/^_btlr-t-radius-(.+)$/);
|
|
61
|
+
if (token) {
|
|
62
|
+
const value = (getTokens().radius as Record<string, unknown>)[`$${token[1]}`];
|
|
63
|
+
return Number(getVariableValue(value));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function emulateBrowser(field: Rect) {
|
|
70
|
+
const rect = {
|
|
71
|
+
...field,
|
|
72
|
+
top: field.y,
|
|
73
|
+
left: field.x,
|
|
74
|
+
right: field.x + field.width,
|
|
75
|
+
bottom: field.y + field.height,
|
|
76
|
+
toJSON: () => ({}),
|
|
77
|
+
};
|
|
78
|
+
const originalRect = HTMLElement.prototype.getBoundingClientRect;
|
|
79
|
+
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(
|
|
80
|
+
function (this: HTMLElement) {
|
|
81
|
+
if (this.hasAttribute("data-fp-reference") || isFieldBox(this)) return rect;
|
|
82
|
+
return originalRect.call(this);
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
const original = globalThis.getComputedStyle.bind(globalThis);
|
|
86
|
+
const computed = (el: Element, pseudo?: string | null) => {
|
|
87
|
+
const style = original(el, pseudo);
|
|
88
|
+
if (!isFieldBox(el)) return style;
|
|
89
|
+
const radius = `${atomRadius(el)}px`;
|
|
90
|
+
return new Proxy(style, {
|
|
91
|
+
get(target, key) {
|
|
92
|
+
if (key === "borderTopLeftRadius") return radius;
|
|
93
|
+
const value = Reflect.get(target, key, target);
|
|
94
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
vi.spyOn(globalThis, "getComputedStyle").mockImplementation(computed as typeof getComputedStyle);
|
|
99
|
+
if ((window as unknown) !== globalThis) {
|
|
100
|
+
vi.spyOn(window, "getComputedStyle").mockImplementation(computed as typeof getComputedStyle);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function fieldBox(root: ParentNode): HTMLElement {
|
|
105
|
+
const box = Array.from(root.querySelectorAll<HTMLElement>("*")).find(isFieldBox);
|
|
106
|
+
expect(box).toBeTruthy();
|
|
107
|
+
return box as HTMLElement;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const corners = (el: Element) =>
|
|
111
|
+
Array.from(el.classList)
|
|
112
|
+
.filter((c) => /^_b[tb][lr]r-/.test(c))
|
|
113
|
+
.sort();
|
|
114
|
+
|
|
115
|
+
beforeEach(() => {
|
|
116
|
+
setViewport(1440, 900);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
afterEach(() => {
|
|
120
|
+
vi.restoreAllMocks();
|
|
121
|
+
setViewport(originalWidth, originalHeight);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("DatePicker family: one surface over the field (LC-112, FZ-8438)", () => {
|
|
125
|
+
for (const [size, viewport] of Object.entries(viewports)) {
|
|
126
|
+
for (const [name, render] of pickers) {
|
|
127
|
+
it(`${name} at ${size}: top, start, width and all four arcs are the field's`, async () => {
|
|
128
|
+
setViewport(viewport.width, viewport.height);
|
|
129
|
+
emulateBrowser(viewport.field);
|
|
130
|
+
const result = renderWithProviders(render());
|
|
131
|
+
const box = fieldBox(result.container);
|
|
132
|
+
const drawn = Math.min(atomRadius(box), viewport.field.height / 2);
|
|
133
|
+
expect(drawn).toBeGreaterThan(0);
|
|
134
|
+
|
|
135
|
+
fireEvent.click(box, { detail: 0 });
|
|
136
|
+
const panel = (await result.findByTestId("floating-panel-viewport")) as HTMLElement;
|
|
137
|
+
expect(result.queryByTestId("floating-panel-sheet")).toBeNull();
|
|
138
|
+
expect(document.querySelector('input[type="date"], input[type="time"]')).toBeNull();
|
|
139
|
+
|
|
140
|
+
await waitFor(() => expect(panel.style.top).toBe(`${viewport.field.y}px`));
|
|
141
|
+
expect(panel.style.left).toBe(`${viewport.field.x}px`);
|
|
142
|
+
await waitFor(() =>
|
|
143
|
+
expect(parseFloat(panel.style.minWidth)).toBeGreaterThanOrEqual(viewport.field.width),
|
|
144
|
+
);
|
|
145
|
+
await waitFor(() =>
|
|
146
|
+
expect(corners(panel)).toEqual(
|
|
147
|
+
[`_bblr-${drawn}px`, `_bbrr-${drawn}px`, `_btlr-${drawn}px`, `_btrr-${drawn}px`].sort(),
|
|
148
|
+
),
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it(`${name} at ${size}: the echo draws no border or divider and sits above the field`, async () => {
|
|
153
|
+
setViewport(viewport.width, viewport.height);
|
|
154
|
+
emulateBrowser(viewport.field);
|
|
155
|
+
const result = renderWithProviders(render());
|
|
156
|
+
const live = fieldBox(result.container);
|
|
157
|
+
fireEvent.click(live, { detail: 0 });
|
|
158
|
+
const panel = (await result.findByTestId("floating-panel-viewport")) as HTMLElement;
|
|
159
|
+
const frame = await waitFor(() => {
|
|
160
|
+
const found = panel.querySelector<HTMLElement>("[data-fp-echo-frame]");
|
|
161
|
+
expect(found).not.toBeNull();
|
|
162
|
+
return found as HTMLElement;
|
|
163
|
+
});
|
|
164
|
+
for (const property of ["border-color", "background-color"]) {
|
|
165
|
+
expect(frame.style.getPropertyValue(property)).toBe("transparent");
|
|
166
|
+
expect(frame.style.getPropertyPriority(property)).toBe("important");
|
|
167
|
+
}
|
|
168
|
+
expect(frame.style.getPropertyValue("box-shadow")).toBe("none");
|
|
169
|
+
const row = panel.querySelector<HTMLElement>("[data-fp-trigger-row]") as HTMLElement;
|
|
170
|
+
expect(row.style.width).toBe(`${viewport.field.width}px`);
|
|
171
|
+
expect(row.style.height).toBe(`${viewport.field.height}px`);
|
|
172
|
+
expect(row.style.backgroundColor).toBe(getComputedStyle(panel).backgroundColor);
|
|
173
|
+
expect(panel.style.position).toBe("fixed");
|
|
174
|
+
expect(Number(panel.style.zIndex)).toBeGreaterThan(0);
|
|
175
|
+
expect(live.compareDocumentPosition(panel) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
176
|
+
expect(live.contains(panel)).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Date, Time and Datetime on touch web. FZ-8114 handed these to the browser's
|
|
3
|
+
* own popover; FZ-8438 replaces it with MPO's cover panel, because Safari's
|
|
4
|
+
* popover cannot lie over the field, match its arc or repeat its content
|
|
5
|
+
* (LC-112 seamless cover, Clay's rv_a07d4308a6c445b6 notes).
|
|
6
|
+
*
|
|
7
|
+
* FZ-8114's invariant still holds: a blank tap opens nothing, only the tapped
|
|
8
|
+
* field opens, and its clear ✕ clears without opening.
|
|
5
9
|
*/
|
|
6
10
|
import { formatCalendarDate } from "@multiplatform.one/theme";
|
|
7
11
|
import { useForm } from "@tanstack/react-form";
|
|
@@ -13,8 +17,15 @@ import { Form } from "../../Form";
|
|
|
13
17
|
import { TimePicker } from "../TimePicker";
|
|
14
18
|
import { DatePicker, DatetimePicker } from "./index";
|
|
15
19
|
|
|
16
|
-
let coarse = false;
|
|
17
20
|
const originalMatchMedia = window.matchMedia;
|
|
21
|
+
const originalWidth = window.innerWidth;
|
|
22
|
+
|
|
23
|
+
function setWidth(width: number) {
|
|
24
|
+
Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
|
|
25
|
+
act(() => {
|
|
26
|
+
window.dispatchEvent(new Event("resize"));
|
|
27
|
+
});
|
|
28
|
+
}
|
|
18
29
|
|
|
19
30
|
beforeEach(() => {
|
|
20
31
|
Object.defineProperty(window, "matchMedia", {
|
|
@@ -22,7 +33,7 @@ beforeEach(() => {
|
|
|
22
33
|
writable: true,
|
|
23
34
|
value: (media: string) => ({
|
|
24
35
|
media,
|
|
25
|
-
matches: media === "(pointer: coarse)"
|
|
36
|
+
matches: media === "(pointer: coarse)" || media === "(hover: none)",
|
|
26
37
|
onchange: null,
|
|
27
38
|
addEventListener: () => {},
|
|
28
39
|
removeEventListener: () => {},
|
|
@@ -31,23 +42,30 @@ beforeEach(() => {
|
|
|
31
42
|
dispatchEvent: () => false,
|
|
32
43
|
}),
|
|
33
44
|
});
|
|
45
|
+
setWidth(390);
|
|
34
46
|
});
|
|
35
47
|
|
|
36
48
|
afterEach(() => {
|
|
37
|
-
coarse = false;
|
|
38
49
|
Object.defineProperty(window, "matchMedia", {
|
|
39
50
|
configurable: true,
|
|
40
51
|
writable: true,
|
|
41
52
|
value: originalMatchMedia,
|
|
42
53
|
});
|
|
54
|
+
setWidth(originalWidth);
|
|
43
55
|
});
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
/** A finger tap as mobile Safari delivers it: pointer, compat mouse, click. */
|
|
58
|
+
function tap(el: Element) {
|
|
59
|
+
fireEvent.pointerDown(el, { pointerType: "touch", button: 0 });
|
|
60
|
+
fireEvent.mouseDown(el, { button: 0, detail: 1 });
|
|
61
|
+
fireEvent.pointerUp(el, { pointerType: "touch", button: 0 });
|
|
62
|
+
fireEvent.mouseUp(el, { button: 0, detail: 1 });
|
|
63
|
+
fireEvent.click(el, { button: 0, detail: 1 });
|
|
49
64
|
}
|
|
50
65
|
|
|
66
|
+
const panels = () => document.querySelectorAll('[data-testid="floating-panel-viewport"]');
|
|
67
|
+
const sheets = () => document.querySelectorAll('[data-testid="floating-panel-sheet"]');
|
|
68
|
+
|
|
51
69
|
function dayButton(root: ParentNode, day: string): HTMLElement {
|
|
52
70
|
const match = Array.from(root.querySelectorAll('button, [role="button"]')).find(
|
|
53
71
|
(el) => el.textContent?.trim() === day && el.getAttribute("aria-disabled") !== "true",
|
|
@@ -56,100 +74,74 @@ function dayButton(root: ParentNode, day: string): HTMLElement {
|
|
|
56
74
|
return match as HTMLElement;
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
function Page() {
|
|
78
|
+
return (
|
|
79
|
+
<>
|
|
80
|
+
<div data-testid="blank" style={{ height: 200 }} />
|
|
81
|
+
<DatePicker label="Date" name="date" value={new Date(2026, 2, 5)} />
|
|
82
|
+
<TimePicker label="Time" name="time" value="14:30" />
|
|
83
|
+
<DatetimePicker label="Date and time" name="starts" />
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
68
87
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
describe("touch web opens MPO's cover panel, never the browser's popover (FZ-8438)", () => {
|
|
89
|
+
it.each([
|
|
90
|
+
["date", "date-picker-trigger", <DatePicker key="d" label="Due" name="due" />],
|
|
91
|
+
["time", "time-picker-trigger", <TimePicker key="t" label="At" name="at" />],
|
|
92
|
+
[
|
|
93
|
+
"date and time",
|
|
94
|
+
"date-picker-trigger",
|
|
95
|
+
<DatetimePicker key="dt" label="Starts" name="starts" />,
|
|
96
|
+
],
|
|
97
|
+
] as const)("a tap on the %s field opens the MPO panel over it", async (_, testId, field) => {
|
|
98
|
+
const result = renderWithProviders(field);
|
|
99
|
+
expect(result.container.querySelector("input[type=date], input[type=time]")).toBeNull();
|
|
100
|
+
expect(result.container.querySelector('input[type="datetime-local"]')).toBeNull();
|
|
101
|
+
tap(result.getByTestId(testId));
|
|
102
|
+
await waitFor(() => expect(panels().length).toBe(1));
|
|
103
|
+
expect(sheets().length).toBe(0);
|
|
104
|
+
expect(document.querySelector("[data-fp-trigger-row]")).not.toBeNull();
|
|
73
105
|
});
|
|
74
106
|
|
|
75
|
-
it("
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
107
|
+
it("a blank tap opens nothing", async () => {
|
|
108
|
+
const result = renderWithProviders(<Page />);
|
|
109
|
+
tap(result.getByTestId("blank"));
|
|
110
|
+
tap(document.body);
|
|
79
111
|
await act(async () => {
|
|
80
|
-
|
|
112
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
81
113
|
});
|
|
82
|
-
expect(
|
|
83
|
-
|
|
84
|
-
expect(picked.getTime()).toBe(new Date(2026, 2, 5).getTime());
|
|
85
|
-
expect(result.container.querySelector('input[value="Mar 5, 2026"]')).not.toBeNull();
|
|
86
|
-
expect(touchInput(result.container, "date").value).toBe("2026-03-05");
|
|
114
|
+
expect(panels().length).toBe(0);
|
|
115
|
+
expect(sheets().length).toBe(0);
|
|
87
116
|
});
|
|
88
117
|
|
|
89
|
-
it("
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
await waitFor(() => dayButton(document.body, "5"));
|
|
98
|
-
await act(async () => {
|
|
99
|
-
fireEvent.click(dayButton(document.body, "5"));
|
|
100
|
-
});
|
|
101
|
-
desktop.unmount();
|
|
102
|
-
|
|
103
|
-
coarse = true;
|
|
104
|
-
const touch = vi.fn();
|
|
105
|
-
const phone = renderWithProviders(
|
|
106
|
-
<DatePicker label="Due" name="due" value="2026-03-01" onChange={touch} />,
|
|
107
|
-
);
|
|
108
|
-
expect(touchInput(phone.container, "date").value).toBe("2026-03-01");
|
|
109
|
-
await act(async () => {
|
|
110
|
-
fireEvent.change(touchInput(phone.container, "date"), { target: { value: "2026-03-05" } });
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
expect(fine).toHaveBeenCalledTimes(1);
|
|
114
|
-
expect(touch).toHaveBeenCalledTimes(1);
|
|
115
|
-
expect((touch.mock.calls[0][0] as Date).getTime()).toBe(
|
|
116
|
-
(fine.mock.calls[0][0] as Date).getTime(),
|
|
117
|
-
);
|
|
118
|
+
it("only the tapped field opens", async () => {
|
|
119
|
+
const result = renderWithProviders(<Page />);
|
|
120
|
+
tap(result.getByTestId("time-picker-trigger"));
|
|
121
|
+
await waitFor(() => expect(panels().length).toBe(1));
|
|
122
|
+
const panel = panels()[0] as HTMLElement;
|
|
123
|
+
expect(panel.querySelector('[data-testid="time-picker-panel"]')).not.toBeNull();
|
|
124
|
+
expect(panel.textContent).not.toContain("March");
|
|
118
125
|
});
|
|
119
126
|
|
|
120
|
-
it("
|
|
121
|
-
coarse = true;
|
|
127
|
+
it("the clear ✕ clears without opening", async () => {
|
|
122
128
|
const onChange = vi.fn();
|
|
123
129
|
const result = renderWithProviders(
|
|
124
|
-
<DatePicker label="Due" name="due" value=
|
|
130
|
+
<DatePicker label="Due" name="due" value={new Date(2026, 2, 5)} onChange={onChange} />,
|
|
125
131
|
);
|
|
132
|
+
tap(result.getByTestId("date-picker-clear"));
|
|
126
133
|
await act(async () => {
|
|
127
|
-
|
|
134
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
128
135
|
});
|
|
129
136
|
expect(onChange).toHaveBeenLastCalledWith(null);
|
|
137
|
+
expect(panels().length).toBe(0);
|
|
130
138
|
});
|
|
131
139
|
|
|
132
|
-
it("
|
|
133
|
-
coarse = true;
|
|
134
|
-
const result = renderWithProviders(
|
|
135
|
-
<DatePicker
|
|
136
|
-
label="Due"
|
|
137
|
-
name="due"
|
|
138
|
-
minDate={new Date(2026, 0, 2)}
|
|
139
|
-
maxDate={new Date(2026, 11, 30)}
|
|
140
|
-
/>,
|
|
141
|
-
);
|
|
142
|
-
const input = touchInput(result.container, "date");
|
|
143
|
-
expect(input.min).toBe("2026-01-02");
|
|
144
|
-
expect(input.max).toBe("2026-12-30");
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it("commits into form state", async () => {
|
|
148
|
-
coarse = true;
|
|
140
|
+
it("a day picked in the panel commits the calendar's Date into form state", async () => {
|
|
149
141
|
const onSubmit = vi.fn();
|
|
150
142
|
const Fixture = () => {
|
|
151
143
|
const form = useForm({
|
|
152
|
-
defaultValues: { due:
|
|
144
|
+
defaultValues: { due: new Date(2026, 2, 1) as Date | null },
|
|
153
145
|
onSubmit: async ({ value }) => onSubmit(value),
|
|
154
146
|
});
|
|
155
147
|
return (
|
|
@@ -160,99 +152,49 @@ describe("Date on touch web (FZ-8114)", () => {
|
|
|
160
152
|
);
|
|
161
153
|
};
|
|
162
154
|
const result = renderWithProviders(<Fixture />);
|
|
155
|
+
tap(result.getByTestId("date-picker-trigger"));
|
|
156
|
+
await waitFor(() => expect(panels().length).toBe(1));
|
|
163
157
|
await act(async () => {
|
|
164
|
-
fireEvent.
|
|
158
|
+
fireEvent.click(dayButton(panels()[0] as HTMLElement, "15"));
|
|
165
159
|
});
|
|
160
|
+
await waitFor(() => expect(panels().length).toBe(0));
|
|
166
161
|
await act(async () => {
|
|
167
162
|
fireEvent.click(result.getSubmitButton() as HTMLElement);
|
|
168
163
|
});
|
|
169
164
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
170
|
-
expect(formatCalendarDate(onSubmit.mock.calls[0][0].due, "date")).toBe("2026-
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe("Datetime on touch web (FZ-8114)", () => {
|
|
175
|
-
it("a coarse pointer renders datetime-local and emits the local wall clock", async () => {
|
|
176
|
-
coarse = true;
|
|
177
|
-
const onChange = vi.fn();
|
|
178
|
-
const result = renderWithProviders(
|
|
179
|
-
<DatetimePicker label="Starts" name="starts" onChange={onChange} />,
|
|
180
|
-
);
|
|
181
|
-
await act(async () => {
|
|
182
|
-
fireEvent.change(touchInput(result.container, "datetime-local"), {
|
|
183
|
-
target: { value: "2026-03-05T14:30" },
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
const picked = onChange.mock.calls[0][0] as Date;
|
|
187
|
-
expect(formatCalendarDate(picked, "datetime")).toBe("2026-03-05 14:30:00");
|
|
188
|
-
expect(touchInput(result.container, "datetime-local").value).toBe("2026-03-05T14:30");
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("seconds widen the input's step and survive the round trip", () => {
|
|
192
|
-
coarse = true;
|
|
193
|
-
const result = renderWithProviders(
|
|
194
|
-
<DatetimePicker
|
|
195
|
-
label="Starts"
|
|
196
|
-
name="starts"
|
|
197
|
-
showSeconds
|
|
198
|
-
value={new Date(2026, 2, 5, 14, 30, 15)}
|
|
199
|
-
/>,
|
|
200
|
-
);
|
|
201
|
-
const input = touchInput(result.container, "datetime-local");
|
|
202
|
-
expect(input.step).toBe("1");
|
|
203
|
-
expect(input.value).toBe("2026-03-05T14:30:15");
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("a fine pointer keeps the MPO panel trigger", () => {
|
|
207
|
-
const result = renderWithProviders(<DatetimePicker label="Starts" name="starts" />);
|
|
208
|
-
expect(result.container.querySelector('input[type="datetime-local"]')).toBeNull();
|
|
209
|
-
expect(result.container.querySelector('[role="combobox"]')).not.toBeNull();
|
|
165
|
+
expect(formatCalendarDate(onSubmit.mock.calls[0][0].due, "date")).toBe("2026-03-15");
|
|
210
166
|
});
|
|
211
167
|
});
|
|
212
168
|
|
|
213
|
-
describe("
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
169
|
+
describe("a field too low for the panel (LC-105 collision)", () => {
|
|
170
|
+
afterEach(() => {
|
|
171
|
+
vi.restoreAllMocks();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("takes MPO's bottom sheet, still never a native input", async () => {
|
|
175
|
+
const field = { x: 16, y: window.innerHeight - 60, width: 358, height: 44 };
|
|
176
|
+
const original = HTMLElement.prototype.getBoundingClientRect;
|
|
177
|
+
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(
|
|
178
|
+
function (this: HTMLElement) {
|
|
179
|
+
if (!this.hasAttribute("data-fp-reference")) return original.call(this);
|
|
180
|
+
return {
|
|
181
|
+
...field,
|
|
182
|
+
top: field.y,
|
|
183
|
+
left: field.x,
|
|
184
|
+
right: field.x + field.width,
|
|
185
|
+
bottom: field.y + field.height,
|
|
186
|
+
toJSON: () => ({}),
|
|
187
|
+
} as DOMRect;
|
|
188
|
+
},
|
|
219
189
|
);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
});
|
|
225
|
-
expect(onChange).toHaveBeenLastCalledWith("14:30");
|
|
226
|
-
expect(result.container.querySelector('input[value="2:30 PM"]')).not.toBeNull();
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it("showSeconds emits HH:mm:ss even when the browser drops :00", async () => {
|
|
230
|
-
coarse = true;
|
|
231
|
-
const onChange = vi.fn();
|
|
232
|
-
const result = renderWithProviders(
|
|
233
|
-
<TimePicker label="At" name="at" showSeconds onChange={onChange} />,
|
|
190
|
+
vi.spyOn(HTMLElement.prototype, "scrollHeight", "get").mockImplementation(
|
|
191
|
+
function (this: HTMLElement) {
|
|
192
|
+
return this.hasAttribute("data-fp-panel") ? 350 : 0;
|
|
193
|
+
},
|
|
234
194
|
);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
expect(
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it("a fine pointer keeps the MPO wheels trigger", () => {
|
|
242
|
-
const result = renderWithProviders(<TimePicker label="At" name="at" />);
|
|
243
|
-
expect(result.container.querySelector('input[type="time"]')).toBeNull();
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe("the touch input stays inside its box (FZ-8114)", () => {
|
|
248
|
-
it.each([
|
|
249
|
-
["date", <DatePicker key="d" label="Due" name="due" />],
|
|
250
|
-
["datetime-local", <DatetimePicker key="dt" label="Starts" name="starts" />],
|
|
251
|
-
["time", <TimePicker key="t" label="At" name="at" />],
|
|
252
|
-
] as const)("the %s input's box is its positioning context", (type, field) => {
|
|
253
|
-
coarse = true;
|
|
254
|
-
const result = renderWithProviders(field);
|
|
255
|
-
const box = touchInput(result.container, type).parentElement as HTMLElement;
|
|
256
|
-
expect(getComputedStyle(box).position).toBe("relative");
|
|
195
|
+
const result = renderWithProviders(<DatePicker label="Due" name="due" />);
|
|
196
|
+
tap(result.getByTestId("date-picker-trigger"));
|
|
197
|
+
await waitFor(() => expect(sheets().length).toBe(1));
|
|
198
|
+
expect(document.querySelector("input[type=date], input[type=time]")).toBeNull();
|
|
257
199
|
});
|
|
258
200
|
});
|