@multiplatform.one/forms 6.4.3 → 6.6.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 +23 -8
- package/dist/cjs/Button/index.native.js +23 -8
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Skeleton/index.cjs +1 -0
- package/dist/cjs/Skeleton/index.native.js +4 -0
- package/dist/cjs/Skeleton/index.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/Checkbox.cjs +22 -15
- package/dist/cjs/fields/Checkbox/Checkbox.native.js +11 -6
- package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/index.cjs +9 -3
- package/dist/cjs/fields/Checkbox/index.native.js +9 -3
- package/dist/cjs/fields/Checkbox/index.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/index.cjs +9 -4
- package/dist/cjs/fields/Combobox/index.native.js +9 -4
- package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DateRangePicker.cjs +30 -20
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +36 -21
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.cjs +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MonthPicker.cjs +24 -14
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js +36 -15
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MultiDatePicker.cjs +27 -18
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +32 -19
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/fieldValueSync.cjs +11 -1
- package/dist/cjs/fields/DatePicker/fieldValueSync.native.js +12 -1
- package/dist/cjs/fields/DatePicker/fieldValueSync.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/GeolocationMap.native.js +2 -2
- package/dist/cjs/fields/RadioGroup/index.cjs +2 -2
- package/dist/cjs/fields/RadioGroup/index.native.js +2 -2
- package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/index.cjs +17 -2
- package/dist/cjs/fields/Rating/index.native.js +25 -2
- package/dist/cjs/fields/Rating/index.native.js.map +1 -1
- package/dist/cjs/fields/SearchInput/index.cjs +5 -1
- package/dist/cjs/fields/SearchInput/index.native.js +5 -1
- package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.cjs +6 -10
- package/dist/cjs/fields/Select/index.native.js +12 -10
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Signature/index.cjs +1 -1
- package/dist/cjs/fields/Signature/index.native.js +1 -1
- package/dist/cjs/fields/Signature/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/index.cjs +1 -0
- package/dist/cjs/fields/Slider/index.native.js +4 -0
- package/dist/cjs/fields/Slider/index.native.js.map +1 -1
- package/dist/cjs/fields/TextArea/index.cjs +3 -2
- package/dist/cjs/fields/TextArea/index.native.js +5 -2
- package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/index.cjs +1 -1
- package/dist/cjs/fields/TimePicker/index.native.js +1 -1
- package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/ToggleGroup/index.cjs +6 -22
- package/dist/cjs/fields/ToggleGroup/index.native.js +11 -28
- package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/cjs/shared/TimeColumn.cjs +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/focusManagement.cjs +7 -1
- package/dist/cjs/shared/focusManagement.native.js +5 -1
- package/dist/cjs/shared/focusManagement.native.js.map +1 -1
- package/dist/cjs/shared/t.cjs +5 -0
- package/dist/cjs/shared/t.native.js +5 -0
- package/dist/cjs/shared/t.native.js.map +1 -1
- package/dist/esm/Button/index.mjs +24 -9
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +24 -9
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/Skeleton/index.mjs +2 -1
- package/dist/esm/Skeleton/index.mjs.map +1 -1
- package/dist/esm/Skeleton/index.native.js +5 -1
- package/dist/esm/Skeleton/index.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/Checkbox.mjs +24 -17
- package/dist/esm/fields/Checkbox/Checkbox.mjs.map +1 -1
- package/dist/esm/fields/Checkbox/Checkbox.native.js +13 -8
- package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/index.mjs +11 -5
- package/dist/esm/fields/Checkbox/index.mjs.map +1 -1
- package/dist/esm/fields/Checkbox/index.native.js +11 -5
- package/dist/esm/fields/Checkbox/index.native.js.map +1 -1
- package/dist/esm/fields/Combobox/index.mjs +9 -4
- package/dist/esm/fields/Combobox/index.mjs.map +1 -1
- package/dist/esm/fields/Combobox/index.native.js +9 -4
- package/dist/esm/fields/Combobox/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DateRangePicker.mjs +31 -21
- package/dist/esm/fields/DatePicker/DateRangePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DateRangePicker.native.js +37 -22
- package/dist/esm/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.mjs +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MonthPicker.mjs +25 -15
- package/dist/esm/fields/DatePicker/MonthPicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/MonthPicker.native.js +37 -16
- package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MultiDatePicker.mjs +28 -19
- package/dist/esm/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/MultiDatePicker.native.js +33 -20
- package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/fieldValueSync.mjs +10 -1
- package/dist/esm/fields/DatePicker/fieldValueSync.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/fieldValueSync.native.js +11 -1
- package/dist/esm/fields/DatePicker/fieldValueSync.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/GeolocationMap.native.js +2 -2
- package/dist/esm/fields/RadioGroup/index.mjs +2 -2
- package/dist/esm/fields/RadioGroup/index.mjs.map +1 -1
- package/dist/esm/fields/RadioGroup/index.native.js +2 -2
- package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/esm/fields/Rating/index.mjs +17 -2
- package/dist/esm/fields/Rating/index.mjs.map +1 -1
- package/dist/esm/fields/Rating/index.native.js +25 -2
- package/dist/esm/fields/Rating/index.native.js.map +1 -1
- package/dist/esm/fields/SearchInput/index.mjs +6 -2
- package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
- package/dist/esm/fields/SearchInput/index.native.js +6 -2
- package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
- package/dist/esm/fields/Select/index.mjs +6 -10
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +12 -10
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/Signature/index.mjs +1 -1
- package/dist/esm/fields/Signature/index.mjs.map +1 -1
- package/dist/esm/fields/Signature/index.native.js +1 -1
- package/dist/esm/fields/Signature/index.native.js.map +1 -1
- package/dist/esm/fields/Slider/index.mjs +1 -0
- package/dist/esm/fields/Slider/index.mjs.map +1 -1
- package/dist/esm/fields/Slider/index.native.js +4 -0
- package/dist/esm/fields/Slider/index.native.js.map +1 -1
- package/dist/esm/fields/TextArea/index.mjs +3 -2
- package/dist/esm/fields/TextArea/index.mjs.map +1 -1
- package/dist/esm/fields/TextArea/index.native.js +5 -2
- package/dist/esm/fields/TextArea/index.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/index.mjs +1 -1
- package/dist/esm/fields/TimePicker/index.mjs.map +1 -1
- package/dist/esm/fields/TimePicker/index.native.js +1 -1
- package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.mjs +7 -23
- package/dist/esm/fields/ToggleGroup/index.mjs.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.native.js +12 -29
- package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/esm/shared/TimeColumn.mjs +1 -1
- package/dist/esm/shared/TimeColumn.mjs.map +1 -1
- package/dist/esm/shared/TimeColumn.native.js +1 -1
- package/dist/esm/shared/TimeColumn.native.js.map +1 -1
- package/dist/esm/shared/focusManagement.mjs +7 -1
- package/dist/esm/shared/focusManagement.mjs.map +1 -1
- package/dist/esm/shared/focusManagement.native.js +5 -1
- package/dist/esm/shared/focusManagement.native.js.map +1 -1
- package/dist/esm/shared/t.mjs +5 -1
- package/dist/esm/shared/t.mjs.map +1 -1
- package/dist/esm/shared/t.native.js +5 -1
- package/dist/esm/shared/t.native.js.map +1 -1
- package/dist/jsx/Button/index.mjs +24 -9
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +23 -8
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/Skeleton/index.mjs +2 -1
- package/dist/jsx/Skeleton/index.mjs.map +1 -1
- package/dist/jsx/Skeleton/index.native.js +4 -0
- package/dist/jsx/Skeleton/index.native.js.map +1 -1
- package/dist/jsx/fields/Checkbox/Checkbox.mjs +24 -17
- package/dist/jsx/fields/Checkbox/Checkbox.mjs.map +1 -1
- package/dist/jsx/fields/Checkbox/Checkbox.native.js +11 -6
- package/dist/jsx/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/jsx/fields/Checkbox/index.mjs +11 -5
- package/dist/jsx/fields/Checkbox/index.mjs.map +1 -1
- package/dist/jsx/fields/Checkbox/index.native.js +9 -3
- package/dist/jsx/fields/Checkbox/index.native.js.map +1 -1
- package/dist/jsx/fields/Combobox/index.mjs +9 -4
- package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
- package/dist/jsx/fields/Combobox/index.native.js +9 -4
- package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DateRangePicker.mjs +31 -21
- package/dist/jsx/fields/DatePicker/DateRangePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DateRangePicker.native.js +36 -21
- package/dist/jsx/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.mjs +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/MonthPicker.mjs +25 -15
- package/dist/jsx/fields/DatePicker/MonthPicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/MonthPicker.native.js +36 -15
- package/dist/jsx/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs +28 -19
- package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js +32 -19
- package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/fieldValueSync.mjs +10 -1
- package/dist/jsx/fields/DatePicker/fieldValueSync.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/fieldValueSync.native.js +12 -1
- package/dist/jsx/fields/DatePicker/fieldValueSync.native.js.map +1 -1
- package/dist/jsx/fields/Geolocation/GeolocationMap.native.js +2 -2
- package/dist/jsx/fields/RadioGroup/index.mjs +2 -2
- package/dist/jsx/fields/RadioGroup/index.mjs.map +1 -1
- package/dist/jsx/fields/RadioGroup/index.native.js +2 -2
- package/dist/jsx/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/jsx/fields/Rating/index.mjs +17 -2
- package/dist/jsx/fields/Rating/index.mjs.map +1 -1
- package/dist/jsx/fields/Rating/index.native.js +25 -2
- package/dist/jsx/fields/Rating/index.native.js.map +1 -1
- package/dist/jsx/fields/SearchInput/index.mjs +6 -2
- package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
- package/dist/jsx/fields/SearchInput/index.native.js +5 -1
- package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/index.mjs +6 -10
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +12 -10
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/dist/jsx/fields/Signature/index.mjs +1 -1
- package/dist/jsx/fields/Signature/index.mjs.map +1 -1
- package/dist/jsx/fields/Signature/index.native.js +1 -1
- package/dist/jsx/fields/Signature/index.native.js.map +1 -1
- package/dist/jsx/fields/Slider/index.mjs +1 -0
- package/dist/jsx/fields/Slider/index.mjs.map +1 -1
- package/dist/jsx/fields/Slider/index.native.js +4 -0
- package/dist/jsx/fields/Slider/index.native.js.map +1 -1
- package/dist/jsx/fields/TextArea/index.mjs +3 -2
- package/dist/jsx/fields/TextArea/index.mjs.map +1 -1
- package/dist/jsx/fields/TextArea/index.native.js +5 -2
- package/dist/jsx/fields/TextArea/index.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/index.mjs +1 -1
- package/dist/jsx/fields/TimePicker/index.mjs.map +1 -1
- package/dist/jsx/fields/TimePicker/index.native.js +1 -1
- package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/ToggleGroup/index.mjs +7 -23
- package/dist/jsx/fields/ToggleGroup/index.mjs.map +1 -1
- package/dist/jsx/fields/ToggleGroup/index.native.js +11 -28
- package/dist/jsx/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/jsx/shared/TimeColumn.mjs +1 -1
- package/dist/jsx/shared/TimeColumn.mjs.map +1 -1
- package/dist/jsx/shared/TimeColumn.native.js +1 -1
- package/dist/jsx/shared/TimeColumn.native.js.map +1 -1
- package/dist/jsx/shared/focusManagement.mjs +7 -1
- package/dist/jsx/shared/focusManagement.mjs.map +1 -1
- package/dist/jsx/shared/focusManagement.native.js +5 -1
- package/dist/jsx/shared/focusManagement.native.js.map +1 -1
- package/dist/jsx/shared/t.mjs +5 -1
- package/dist/jsx/shared/t.mjs.map +1 -1
- package/dist/jsx/shared/t.native.js +5 -0
- package/dist/jsx/shared/t.native.js.map +1 -1
- package/package.json +8 -7
- package/src/Button/Button.stories.tsx +19 -0
- package/src/Button/index.tsx +45 -12
- package/src/ControlGroup/ControlGroup.stories.tsx +12 -0
- package/src/FieldLayout.stories.tsx +3 -2
- package/src/Form/Form.stories.tsx +13 -0
- package/src/Skeleton/index.tsx +5 -1
- package/src/fields/Checkbox/Checkbox.spec.tsx +50 -1
- package/src/fields/Checkbox/Checkbox.tsx +47 -19
- package/src/fields/Checkbox/index.tsx +23 -5
- package/src/fields/ColorPicker/ColorPicker.stories.tsx +2 -1
- package/src/fields/Combobox/index.tsx +8 -3
- package/src/fields/DatePicker/DatePicker.spec.tsx +93 -0
- package/src/fields/DatePicker/DatePicker.stories.tsx +2 -1
- package/src/fields/DatePicker/DateRangePicker.spec.tsx +72 -0
- package/src/fields/DatePicker/DateRangePicker.tsx +44 -30
- package/src/fields/DatePicker/DatetimePicker.spec.tsx +39 -0
- package/src/fields/DatePicker/DatetimePicker.tsx +1 -1
- package/src/fields/DatePicker/MonthPicker.spec.tsx +61 -0
- package/src/fields/DatePicker/MonthPicker.tsx +47 -21
- package/src/fields/DatePicker/MultiDatePicker.spec.tsx +34 -0
- package/src/fields/DatePicker/MultiDatePicker.tsx +39 -27
- package/src/fields/DatePicker/fieldValueSync.tsx +23 -0
- package/src/fields/FileUpload/FileUpload.stories.tsx +11 -1
- package/src/fields/Geolocation/Geolocation.stories.tsx +9 -0
- package/src/fields/Geolocation/GeolocationMap.native.tsx +2 -2
- package/src/fields/ImageField/ImageField.stories.tsx +2 -1
- package/src/fields/Input/Input.stories.tsx +19 -0
- package/src/fields/Progress/Progress.stories.tsx +19 -0
- package/src/fields/RadioGroup/RadioGroup.spec.tsx +139 -2
- package/src/fields/RadioGroup/index.tsx +2 -2
- package/src/fields/Rating/Rating.spec.tsx +43 -1
- package/src/fields/Rating/index.tsx +43 -4
- package/src/fields/SearchInput/SearchInput.spec.tsx +11 -0
- package/src/fields/SearchInput/index.tsx +6 -2
- package/src/fields/Select/Select.stories.tsx +19 -0
- package/src/fields/Select/index.tsx +12 -5
- package/src/fields/Signature/index.tsx +1 -1
- package/src/fields/Slider/Slider.spec.tsx +89 -4
- package/src/fields/Slider/Slider.stories.tsx +19 -0
- package/src/fields/Slider/index.tsx +4 -0
- package/src/fields/Stepper/Stepper.stories.tsx +12 -0
- package/src/fields/Switch/Switch.spec.tsx +50 -2
- package/src/fields/TextArea/TextArea.spec.tsx +57 -0
- package/src/fields/TextArea/TextArea.stories.tsx +19 -0
- package/src/fields/TextArea/index.tsx +10 -2
- package/src/fields/TimePicker/index.tsx +1 -1
- package/src/fields/ToggleGroup/ToggleGroup.spec.tsx +64 -1
- package/src/fields/ToggleGroup/ToggleGroup.stories.tsx +19 -0
- package/src/fields/ToggleGroup/index.tsx +31 -29
- package/src/shared/FloatingListScrollArrows.stories.tsx +4 -2
- package/src/shared/TimeColumn.tsx +1 -1
- package/src/shared/focusManagement.ts +12 -1
- package/src/shared/t.spec.ts +39 -0
- package/src/shared/t.ts +20 -0
- package/types/Button/index.d.ts +9 -1
- package/types/Button/index.d.ts.map +1 -1
- package/types/Skeleton/index.d.ts.map +1 -1
- package/types/fields/Checkbox/Checkbox.d.ts.map +1 -1
- package/types/fields/Checkbox/index.d.ts.map +1 -1
- package/types/fields/Combobox/index.d.ts.map +1 -1
- package/types/fields/DatePicker/DateRangePicker.d.ts.map +1 -1
- package/types/fields/DatePicker/MonthPicker.d.ts.map +1 -1
- package/types/fields/DatePicker/MultiDatePicker.d.ts.map +1 -1
- package/types/fields/DatePicker/fieldValueSync.d.ts +14 -0
- package/types/fields/DatePicker/fieldValueSync.d.ts.map +1 -1
- package/types/fields/Rating/index.d.ts.map +1 -1
- package/types/fields/SearchInput/index.d.ts.map +1 -1
- package/types/fields/Select/index.d.ts.map +1 -1
- package/types/fields/Slider/index.d.ts.map +1 -1
- package/types/fields/TextArea/index.d.ts.map +1 -1
- package/types/fields/ToggleGroup/index.d.ts.map +1 -1
- package/types/shared/focusManagement.d.ts.map +1 -1
- package/types/shared/t.d.ts +16 -0
- package/types/shared/t.d.ts.map +1 -1
|
@@ -333,7 +333,7 @@ const ComboboxItem = memo(function ComboboxItem({
|
|
|
333
333
|
? // Two-line rows need to grow past the size-variant height.
|
|
334
334
|
{ height: "auto", paddingVertical: "$2" }
|
|
335
335
|
: undefined)}
|
|
336
|
-
{...(ctx.knobProps.transition ? {
|
|
336
|
+
{...(ctx.knobProps.transition ? { transition: ctx.knobProps.transition } : undefined)}
|
|
337
337
|
>
|
|
338
338
|
{option.description ? (
|
|
339
339
|
<YStack flex={1} minWidth={0}>
|
|
@@ -1258,6 +1258,11 @@ function FloatingComboboxCore({
|
|
|
1258
1258
|
: {
|
|
1259
1259
|
borderRadius: knobProps.borderRadius.borderRadius,
|
|
1260
1260
|
borderWidth: knobProps.borderRadius.borderWidth,
|
|
1261
|
+
// LC-54 PRESS-FLOOR: compact/small size tokens render a sub-44
|
|
1262
|
+
// trigger (the 36px "Allocated To" quick filter breaking the
|
|
1263
|
+
// list's 44px control row) — same floor the house Select holds
|
|
1264
|
+
// on both its trigger variants.
|
|
1265
|
+
minHeight: 44,
|
|
1261
1266
|
})}
|
|
1262
1267
|
>
|
|
1263
1268
|
<SyncFocusToOpen open={open} />
|
|
@@ -1497,7 +1502,7 @@ function FloatingComboboxCore({
|
|
|
1497
1502
|
: undefined
|
|
1498
1503
|
}
|
|
1499
1504
|
onPress={handleCreate}
|
|
1500
|
-
{...(knobProps.transition ? {
|
|
1505
|
+
{...(knobProps.transition ? { transition: knobProps.transition } : undefined)}
|
|
1501
1506
|
>
|
|
1502
1507
|
<PlusIcon size={14} color={knobProps.textAccentColor} />
|
|
1503
1508
|
<Text
|
|
@@ -1538,7 +1543,7 @@ function FloatingComboboxCore({
|
|
|
1538
1543
|
: "transparent"
|
|
1539
1544
|
}
|
|
1540
1545
|
pressStyle={{ backgroundColor: formInputColors.background.focus }}
|
|
1541
|
-
{...(knobProps.transition ? {
|
|
1546
|
+
{...(knobProps.transition ? { transition: knobProps.transition } : undefined)}
|
|
1542
1547
|
>
|
|
1543
1548
|
<PlusIcon size={14} color={knobProps.textAccentColor} />
|
|
1544
1549
|
<Text
|
|
@@ -223,6 +223,99 @@ describe("DatePicker", () => {
|
|
|
223
223
|
});
|
|
224
224
|
});
|
|
225
225
|
|
|
226
|
+
describe("late-arriving value display (create-mode seeding)", () => {
|
|
227
|
+
// The form branch only ever displayed the mount-time value (`selectedDate`
|
|
228
|
+
// was never re-synced from field state), so a value applied AFTER first
|
|
229
|
+
// paint — create-mode engine seeding, form.setFieldValue, live server
|
|
230
|
+
// merge — never rendered. Adopting a programmatic value must not emit
|
|
231
|
+
// onChange (LC-14: a seeded value is not a user edit).
|
|
232
|
+
const getDisplayInput = (container: ParentNode) =>
|
|
233
|
+
container.querySelector("input") as HTMLInputElement | null;
|
|
234
|
+
|
|
235
|
+
it("standalone controlled: a value arriving after mount displays, without onChange", async () => {
|
|
236
|
+
const DatePicker = await loadDatePicker();
|
|
237
|
+
const onChange = vi.fn();
|
|
238
|
+
const onValueChange = vi.fn();
|
|
239
|
+
const result = renderWithProviders(
|
|
240
|
+
<DatePicker
|
|
241
|
+
label="Due Date"
|
|
242
|
+
value={null}
|
|
243
|
+
onChange={onChange}
|
|
244
|
+
onValueChange={onValueChange}
|
|
245
|
+
/>,
|
|
246
|
+
);
|
|
247
|
+
expect(getDisplayInput(result.container)?.value ?? "").toBe("");
|
|
248
|
+
|
|
249
|
+
// Late seed: the upstream doc value (frappe wire string) arrives after
|
|
250
|
+
// first paint.
|
|
251
|
+
result.rerender(
|
|
252
|
+
<DatePicker
|
|
253
|
+
label="Due Date"
|
|
254
|
+
value="2026-03-05"
|
|
255
|
+
onChange={onChange}
|
|
256
|
+
onValueChange={onValueChange}
|
|
257
|
+
/>,
|
|
258
|
+
);
|
|
259
|
+
await waitFor(() => {
|
|
260
|
+
expect(getDisplayInput(result.container)?.value).toBe("Mar 5, 2026");
|
|
261
|
+
});
|
|
262
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
263
|
+
expect(onValueChange).not.toHaveBeenCalled();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("form-integrated: a field value set after mount displays, without onChange", async () => {
|
|
267
|
+
const DatePicker = await loadDatePicker();
|
|
268
|
+
const onChange = vi.fn();
|
|
269
|
+
let formApi: { setFieldValue: (name: "date", value: string | null) => void } | undefined;
|
|
270
|
+
const TestComponent = () => {
|
|
271
|
+
const form = useForm({ defaultValues: { date: null as string | null } });
|
|
272
|
+
formApi = form as unknown as typeof formApi;
|
|
273
|
+
return (
|
|
274
|
+
<Form form={form}>
|
|
275
|
+
<DatePicker label="Due Date" name="date" onChange={onChange} />
|
|
276
|
+
</Form>
|
|
277
|
+
);
|
|
278
|
+
};
|
|
279
|
+
const result = renderWithProviders(<TestComponent />);
|
|
280
|
+
expect(getDisplayInput(result.container)?.value ?? "").toBe("");
|
|
281
|
+
|
|
282
|
+
await act(async () => {
|
|
283
|
+
formApi?.setFieldValue("date", "2026-03-05");
|
|
284
|
+
});
|
|
285
|
+
await waitFor(() => {
|
|
286
|
+
expect(getDisplayInput(result.container)?.value).toBe("Mar 5, 2026");
|
|
287
|
+
});
|
|
288
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("form-integrated: clearing the field value programmatically clears the display", async () => {
|
|
292
|
+
const DatePicker = await loadDatePicker();
|
|
293
|
+
const onChange = vi.fn();
|
|
294
|
+
let formApi: { setFieldValue: (name: "date", value: string | null) => void } | undefined;
|
|
295
|
+
const TestComponent = () => {
|
|
296
|
+
const form = useForm({ defaultValues: { date: "2026-03-05" as string | null } });
|
|
297
|
+
formApi = form as unknown as typeof formApi;
|
|
298
|
+
return (
|
|
299
|
+
<Form form={form}>
|
|
300
|
+
<DatePicker label="Due Date" name="date" onChange={onChange} />
|
|
301
|
+
</Form>
|
|
302
|
+
);
|
|
303
|
+
};
|
|
304
|
+
const result = renderWithProviders(<TestComponent />);
|
|
305
|
+
await waitFor(() => {
|
|
306
|
+
expect(getDisplayInput(result.container)?.value).toBe("Mar 5, 2026");
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
await act(async () => {
|
|
310
|
+
formApi?.setFieldValue("date", null);
|
|
311
|
+
});
|
|
312
|
+
await waitFor(() => {
|
|
313
|
+
expect(getDisplayInput(result.container)?.value ?? "").toBe("");
|
|
314
|
+
});
|
|
315
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
226
319
|
describe("date utilities", () => {
|
|
227
320
|
it("formats display values in the house absolute register and handles null", () => {
|
|
228
321
|
expect(formatDateDisplay(null)).toBe("");
|
|
@@ -116,7 +116,8 @@ export const Disabled: Story = {
|
|
|
116
116
|
export const WithError: Story = {
|
|
117
117
|
args: {
|
|
118
118
|
label: "Required Date",
|
|
119
|
-
|
|
119
|
+
// DG-COPY-02: error copy states the rule broken — never "please"/"invalid"/"error".
|
|
120
|
+
error: "A date is required",
|
|
120
121
|
required: true,
|
|
121
122
|
},
|
|
122
123
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { useForm } from "@tanstack/react-form";
|
|
1
2
|
import { act, fireEvent, waitFor } from "@testing-library/react";
|
|
2
3
|
import { describe, expect, it, vi } from "vitest";
|
|
3
4
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
5
|
+
import { Form } from "../../Form";
|
|
4
6
|
|
|
5
7
|
vi.mock("react-native", () => ({
|
|
6
8
|
Platform: {
|
|
@@ -87,4 +89,74 @@ describe("DateRangePicker", () => {
|
|
|
87
89
|
expect(input?.value ?? "").toContain("–");
|
|
88
90
|
});
|
|
89
91
|
});
|
|
92
|
+
|
|
93
|
+
describe("late-arriving value display (create-mode seeding)", () => {
|
|
94
|
+
// The form branch displays from `rangeStart`/`rangeEnd`, which only user
|
|
95
|
+
// picks and the standalone `value` prop used to update — a field value
|
|
96
|
+
// applied AFTER first paint (create-mode engine seeding, setFieldValue)
|
|
97
|
+
// never rendered. Adopting a programmatic value must not emit onChange
|
|
98
|
+
// (LC-14: a seeded value is not a user edit).
|
|
99
|
+
it("form-integrated: a field value set after mount displays, without onChange", async () => {
|
|
100
|
+
const DateRangePicker = await loadDateRangePicker();
|
|
101
|
+
const onChange = vi.fn();
|
|
102
|
+
type Range = { start: Date; end: Date } | null;
|
|
103
|
+
let formApi: { setFieldValue: (name: "range", value: Range) => void } | undefined;
|
|
104
|
+
const TestComponent = () => {
|
|
105
|
+
const form = useForm({ defaultValues: { range: null as Range } });
|
|
106
|
+
formApi = form as unknown as typeof formApi;
|
|
107
|
+
return (
|
|
108
|
+
<Form form={form}>
|
|
109
|
+
<DateRangePicker label="Range" name="range" onChange={onChange} />
|
|
110
|
+
</Form>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
const result = renderWithProviders(<TestComponent />);
|
|
114
|
+
const getInput = () => result.container.querySelector("input") as HTMLInputElement | null;
|
|
115
|
+
expect(getInput()?.value ?? "").toBe("");
|
|
116
|
+
|
|
117
|
+
await act(async () => {
|
|
118
|
+
formApi?.setFieldValue("range", {
|
|
119
|
+
start: new Date(2026, 2, 5),
|
|
120
|
+
end: new Date(2026, 2, 12),
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
await waitFor(() => {
|
|
124
|
+
expect(getInput()?.value).toBe("Mar 5, 2026 – Mar 12, 2026");
|
|
125
|
+
});
|
|
126
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("form-integrated: clearing the field value programmatically clears the display", async () => {
|
|
130
|
+
const DateRangePicker = await loadDateRangePicker();
|
|
131
|
+
const onChange = vi.fn();
|
|
132
|
+
type Range = { start: Date; end: Date } | null;
|
|
133
|
+
let formApi: { setFieldValue: (name: "range", value: Range) => void } | undefined;
|
|
134
|
+
const TestComponent = () => {
|
|
135
|
+
const form = useForm({
|
|
136
|
+
defaultValues: {
|
|
137
|
+
range: { start: new Date(2026, 2, 5), end: new Date(2026, 2, 12) } as Range,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
formApi = form as unknown as typeof formApi;
|
|
141
|
+
return (
|
|
142
|
+
<Form form={form}>
|
|
143
|
+
<DateRangePicker label="Range" name="range" onChange={onChange} />
|
|
144
|
+
</Form>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
const result = renderWithProviders(<TestComponent />);
|
|
148
|
+
const getInput = () => result.container.querySelector("input") as HTMLInputElement | null;
|
|
149
|
+
await waitFor(() => {
|
|
150
|
+
expect(getInput()?.value).toBe("Mar 5, 2026 – Mar 12, 2026");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await act(async () => {
|
|
154
|
+
formApi?.setFieldValue("range", null);
|
|
155
|
+
});
|
|
156
|
+
await waitFor(() => {
|
|
157
|
+
expect(getInput()?.value ?? "").toBe("");
|
|
158
|
+
});
|
|
159
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
90
162
|
});
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
useDateNavigation,
|
|
33
33
|
yearPageSize,
|
|
34
34
|
} from "./parts";
|
|
35
|
+
import { FieldValueSync } from "./fieldValueSync";
|
|
35
36
|
|
|
36
37
|
import type { ViewMode } from "./parts";
|
|
37
38
|
|
|
@@ -471,24 +472,31 @@ export function DateRangePicker<
|
|
|
471
472
|
setOpen(true);
|
|
472
473
|
}, [setDisplayDate]);
|
|
473
474
|
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
475
|
+
// Adopt an externally-applied value (controlled `value` prop or the live
|
|
476
|
+
// TanStack field value) into the display state without emitting onChange
|
|
477
|
+
// (LC-14). Skip while a range is half-picked (start set, end pending): the
|
|
478
|
+
// first click of a new range used to call onChange(null), and syncing that
|
|
479
|
+
// null wiped rangeStart so the second click could never complete (SB-T2-03).
|
|
478
480
|
const pickingRangeRef = useRef(false);
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (pickingRangeRef.current &&
|
|
481
|
+
const adoptExternalValue = useCallback((raw: unknown) => {
|
|
482
|
+
const next = (raw ?? null) as DateRange | null;
|
|
483
|
+
if (pickingRangeRef.current && next == null) return;
|
|
482
484
|
pickingRangeRef.current = false;
|
|
483
|
-
const nextStart =
|
|
484
|
-
const nextEnd =
|
|
485
|
+
const nextStart = next?.start ?? null;
|
|
486
|
+
const nextEnd = next?.end ?? null;
|
|
485
487
|
setRangeStart((prev) =>
|
|
486
488
|
prev === nextStart || (prev && nextStart && isSameDay(prev, nextStart)) ? prev : nextStart,
|
|
487
489
|
);
|
|
488
490
|
setRangeEnd((prev) =>
|
|
489
491
|
prev === nextEnd || (prev && nextEnd && isSameDay(prev, nextEnd)) ? prev : nextEnd,
|
|
490
492
|
);
|
|
491
|
-
}, [
|
|
493
|
+
}, []);
|
|
494
|
+
|
|
495
|
+
// Keep standalone/controlled state in sync when the parent value changes.
|
|
496
|
+
useEffect(() => {
|
|
497
|
+
if (valueProp === undefined) return;
|
|
498
|
+
adoptExternalValue(valueProp);
|
|
499
|
+
}, [valueProp, adoptExternalValue]);
|
|
492
500
|
|
|
493
501
|
const closePopover = useCallback(() => {
|
|
494
502
|
setOpen(false);
|
|
@@ -647,26 +655,32 @@ export function DateRangePicker<
|
|
|
647
655
|
const resolvedError = getFieldError(field, errorProp);
|
|
648
656
|
|
|
649
657
|
return (
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
{
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
658
|
+
<>
|
|
659
|
+
{/* Form branch displays from `rangeStart`/`rangeEnd` — keep them
|
|
660
|
+
synced to the live field value (late seeds / programmatic
|
|
661
|
+
updates). */}
|
|
662
|
+
<FieldValueSync value={field.state.value} onSync={adoptExternalValue} />
|
|
663
|
+
<FieldLayout
|
|
664
|
+
id={id}
|
|
665
|
+
label={label}
|
|
666
|
+
labelProps={labelProps}
|
|
667
|
+
error={resolvedError}
|
|
668
|
+
helperText={helperText}
|
|
669
|
+
required={required}
|
|
670
|
+
size={size}
|
|
671
|
+
knobProps={knobProps}
|
|
672
|
+
// LC-40: see standalone branch — FieldLayout carries dimWhole.
|
|
673
|
+
disabled={disabled}
|
|
674
|
+
>
|
|
675
|
+
<InputParts size={size || knobProps.sizeToken}>
|
|
676
|
+
{renderPopover(
|
|
677
|
+
!!resolvedError,
|
|
678
|
+
mergeFieldHandler(field, "handleBlur", _onBlur),
|
|
679
|
+
mergeFieldHandler(field, "handleChange"),
|
|
680
|
+
)}
|
|
681
|
+
</InputParts>
|
|
682
|
+
</FieldLayout>
|
|
683
|
+
</>
|
|
670
684
|
);
|
|
671
685
|
}}
|
|
672
686
|
</Field>
|
|
@@ -146,4 +146,43 @@ describe("DatetimePicker", () => {
|
|
|
146
146
|
expect(onSubmit.mock.calls[0][0].appt).toBeInstanceOf(Date);
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
|
+
|
|
150
|
+
describe("late-arriving value display (create-mode seeding)", () => {
|
|
151
|
+
// The form branch displays from `selectedDatetime`, kept synced to the
|
|
152
|
+
// live field value by DatePickerFieldValueSync — a value applied AFTER
|
|
153
|
+
// first paint (create-mode engine seeding, setFieldValue, live server
|
|
154
|
+
// merge) must render. Adopting a programmatic value must not emit
|
|
155
|
+
// onChange (LC-14: a seeded value is not a user edit).
|
|
156
|
+
it("form-integrated: a field value set after mount displays, without onChange", async () => {
|
|
157
|
+
const onChange = vi.fn();
|
|
158
|
+
const onValueChange = vi.fn();
|
|
159
|
+
let formApi: { setFieldValue: (name: "appt", value: Date | null) => void } | undefined;
|
|
160
|
+
const TestComponent = () => {
|
|
161
|
+
const form = useForm({ defaultValues: { appt: null as Date | null } });
|
|
162
|
+
formApi = form as unknown as typeof formApi;
|
|
163
|
+
return (
|
|
164
|
+
<Form form={form}>
|
|
165
|
+
<DatetimePicker
|
|
166
|
+
name="appt"
|
|
167
|
+
label="Appointment"
|
|
168
|
+
onChange={onChange}
|
|
169
|
+
onValueChange={onValueChange}
|
|
170
|
+
/>
|
|
171
|
+
</Form>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
const result = renderWithProviders(<TestComponent />);
|
|
175
|
+
const getInput = () => result.container.querySelector("input") as HTMLInputElement | null;
|
|
176
|
+
expect(getInput()?.value ?? "").toBe("");
|
|
177
|
+
|
|
178
|
+
await act(async () => {
|
|
179
|
+
formApi?.setFieldValue("appt", new Date(2026, 2, 5, 14, 30));
|
|
180
|
+
});
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(getInput()?.value ?? "").toContain("Mar 5, 2026");
|
|
183
|
+
});
|
|
184
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
185
|
+
expect(onValueChange).not.toHaveBeenCalled();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
149
188
|
});
|
|
@@ -368,7 +368,7 @@ function TimeSection({
|
|
|
368
368
|
backgroundColor={isSelected ? formInputColors.background.focus : "transparent"}
|
|
369
369
|
hoverStyle={{ backgroundColor: formInputColors.background.focus }}
|
|
370
370
|
onPress={() => handlePeriodChange(p)}
|
|
371
|
-
{...(knobProps.transition ? {
|
|
371
|
+
{...(knobProps.transition ? { transition: knobProps.transition } : undefined)}
|
|
372
372
|
>
|
|
373
373
|
<Text
|
|
374
374
|
fontSize="$3"
|
|
@@ -142,4 +142,65 @@ describe("MonthPicker", () => {
|
|
|
142
142
|
);
|
|
143
143
|
});
|
|
144
144
|
});
|
|
145
|
+
|
|
146
|
+
describe("late-arriving value display (create-mode seeding)", () => {
|
|
147
|
+
// `selectedValue` was hardcoded to null at mount and only user picks set
|
|
148
|
+
// it, so a field value applied at ANY point (even the Field-level
|
|
149
|
+
// defaultValue) never displayed. The form branch now adopts the live
|
|
150
|
+
// field value — without emitting onChange (LC-14: a seeded value is not
|
|
151
|
+
// a user edit).
|
|
152
|
+
it("form-integrated: a field value set after mount displays, without onChange", async () => {
|
|
153
|
+
const onChange = vi.fn();
|
|
154
|
+
let formApi:
|
|
155
|
+
| { setFieldValue: (name: "month", value: { month: number; year: number } | null) => void }
|
|
156
|
+
| undefined;
|
|
157
|
+
const TestComponent = () => {
|
|
158
|
+
const form = useForm({
|
|
159
|
+
defaultValues: { month: null as { month: number; year: number } | null },
|
|
160
|
+
});
|
|
161
|
+
formApi = form as unknown as typeof formApi;
|
|
162
|
+
return (
|
|
163
|
+
<Form form={form}>
|
|
164
|
+
<MonthPicker name="month" label="Month" onChange={onChange} />
|
|
165
|
+
</Form>
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
const result = renderWithProviders(<TestComponent />);
|
|
169
|
+
const getInput = () => result.container.querySelector("input") as HTMLInputElement | null;
|
|
170
|
+
expect(getInput()?.value ?? "").toBe("");
|
|
171
|
+
|
|
172
|
+
await act(async () => {
|
|
173
|
+
formApi?.setFieldValue("month", { month: 2, year: 2026 });
|
|
174
|
+
});
|
|
175
|
+
await waitFor(() => {
|
|
176
|
+
expect(getInput()?.value).toBe("March 2026");
|
|
177
|
+
});
|
|
178
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("form-integrated: a Field-level defaultValue displays at mount", async () => {
|
|
182
|
+
const onChange = vi.fn();
|
|
183
|
+
const TestComponent = () => {
|
|
184
|
+
const form = useForm({
|
|
185
|
+
defaultValues: { month: undefined as { month: number; year: number } | undefined },
|
|
186
|
+
});
|
|
187
|
+
return (
|
|
188
|
+
<Form form={form}>
|
|
189
|
+
<MonthPicker
|
|
190
|
+
name="month"
|
|
191
|
+
label="Month"
|
|
192
|
+
defaultValue={{ month: 6, year: 2025 }}
|
|
193
|
+
onChange={onChange}
|
|
194
|
+
/>
|
|
195
|
+
</Form>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
const result = renderWithProviders(<TestComponent />);
|
|
199
|
+
await waitFor(() => {
|
|
200
|
+
const input = result.container.querySelector("input") as HTMLInputElement | null;
|
|
201
|
+
expect(input?.value).toBe("July 2025");
|
|
202
|
+
});
|
|
203
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
145
206
|
});
|
|
@@ -5,7 +5,6 @@ import { Text, View, useProps, type Popover } from "tamagui";
|
|
|
5
5
|
|
|
6
6
|
import { Button } from "../../Button";
|
|
7
7
|
import { Field, FieldLayout } from "../../fieldLayout";
|
|
8
|
-
import { formCommonColors } from "../../shared/colorRamps";
|
|
9
8
|
import { t } from "../../shared/t";
|
|
10
9
|
import {
|
|
11
10
|
getFieldError,
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
useDateNavigation,
|
|
29
28
|
yearPageSize,
|
|
30
29
|
} from "./parts";
|
|
30
|
+
import { FieldValueSync } from "./fieldValueSync";
|
|
31
31
|
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
// Types
|
|
@@ -264,6 +264,27 @@ export function MonthPicker<
|
|
|
264
264
|
const { displayDate, setDisplayDate } = useDateNavigation();
|
|
265
265
|
const popoverRef = useRef<Popover>(null);
|
|
266
266
|
|
|
267
|
+
// Display comes from `selectedValue`, which only user picks used to set —
|
|
268
|
+
// a field value applied AFTER mount (create-mode seeding, setFieldValue,
|
|
269
|
+
// even the Field-level defaultValue) never rendered. Adopt the live field
|
|
270
|
+
// value without emitting onChange (LC-14). Identity is preserved when the
|
|
271
|
+
// month is unchanged so the close-on-select effect never re-fires.
|
|
272
|
+
const adoptFieldValue = useCallback((raw: unknown) => {
|
|
273
|
+
const next =
|
|
274
|
+
raw !== null &&
|
|
275
|
+
typeof raw === "object" &&
|
|
276
|
+
typeof (raw as MonthPickerValue).month === "number" &&
|
|
277
|
+
typeof (raw as MonthPickerValue).year === "number"
|
|
278
|
+
? (raw as MonthPickerValue)
|
|
279
|
+
: null;
|
|
280
|
+
setSelectedValue((prev) =>
|
|
281
|
+
prev === next ||
|
|
282
|
+
(prev !== null && next !== null && prev.month === next.month && prev.year === next.year)
|
|
283
|
+
? prev
|
|
284
|
+
: next,
|
|
285
|
+
);
|
|
286
|
+
}, []);
|
|
287
|
+
|
|
267
288
|
const closePopover = useCallback(() => {
|
|
268
289
|
setOpen(false);
|
|
269
290
|
popoverRef.current?.close();
|
|
@@ -395,26 +416,31 @@ export function MonthPicker<
|
|
|
395
416
|
const resolvedError = getFieldError(field, errorProp);
|
|
396
417
|
|
|
397
418
|
return (
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
419
|
+
<>
|
|
420
|
+
{/* Form branch displays from `selectedValue` — keep it synced to
|
|
421
|
+
the live field value (late seeds / programmatic updates). */}
|
|
422
|
+
<FieldValueSync value={field.state.value} onSync={adoptFieldValue} />
|
|
423
|
+
<FieldLayout
|
|
424
|
+
id={id}
|
|
425
|
+
label={label}
|
|
426
|
+
labelProps={labelProps}
|
|
427
|
+
error={resolvedError}
|
|
428
|
+
helperText={helperText}
|
|
429
|
+
required={required}
|
|
430
|
+
size={size}
|
|
431
|
+
knobProps={knobProps}
|
|
432
|
+
// LC-40: see standalone branch — FieldLayout carries dimWhole.
|
|
433
|
+
disabled={disabled}
|
|
434
|
+
>
|
|
435
|
+
<InputParts size={size || knobProps.sizeToken}>
|
|
436
|
+
{renderPopover(
|
|
437
|
+
!!resolvedError,
|
|
438
|
+
mergeFieldHandler(field, "handleBlur", _onBlur),
|
|
439
|
+
mergeFieldHandler(field, "handleChange"),
|
|
440
|
+
)}
|
|
441
|
+
</InputParts>
|
|
442
|
+
</FieldLayout>
|
|
443
|
+
</>
|
|
418
444
|
);
|
|
419
445
|
}}
|
|
420
446
|
</Field>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { useForm } from "@tanstack/react-form";
|
|
1
2
|
import { act, fireEvent, waitFor } from "@testing-library/react";
|
|
2
3
|
import { describe, expect, it, vi } from "vitest";
|
|
3
4
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
5
|
+
import { Form } from "../../Form";
|
|
4
6
|
|
|
5
7
|
vi.mock("react-native", () => ({
|
|
6
8
|
Platform: {
|
|
@@ -90,4 +92,36 @@ describe("MultiDatePicker", () => {
|
|
|
90
92
|
expect(input?.value ?? "").toBe("");
|
|
91
93
|
});
|
|
92
94
|
});
|
|
95
|
+
|
|
96
|
+
describe("late-arriving value display (create-mode seeding)", () => {
|
|
97
|
+
// The form branch displays from `selectedDates`, which only user picks
|
|
98
|
+
// and the standalone `value` prop used to update — a field value applied
|
|
99
|
+
// AFTER first paint (create-mode engine seeding, setFieldValue) never
|
|
100
|
+
// rendered. Adopting a programmatic value must not emit onChange (LC-14).
|
|
101
|
+
it("form-integrated: a field value set after mount displays, without onChange", async () => {
|
|
102
|
+
const MultiDatePicker = await loadMultiDatePicker();
|
|
103
|
+
const onChange = vi.fn();
|
|
104
|
+
let formApi: { setFieldValue: (name: "dates", value: Date[]) => void } | undefined;
|
|
105
|
+
const TestComponent = () => {
|
|
106
|
+
const form = useForm({ defaultValues: { dates: [] as Date[] } });
|
|
107
|
+
formApi = form as unknown as typeof formApi;
|
|
108
|
+
return (
|
|
109
|
+
<Form form={form}>
|
|
110
|
+
<MultiDatePicker label="Dates" name="dates" onChange={onChange} />
|
|
111
|
+
</Form>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
const result = renderWithProviders(<TestComponent />);
|
|
115
|
+
const getInput = () => result.container.querySelector("input") as HTMLInputElement | null;
|
|
116
|
+
expect(getInput()?.value ?? "").toBe("");
|
|
117
|
+
|
|
118
|
+
await act(async () => {
|
|
119
|
+
formApi?.setFieldValue("dates", [new Date(2026, 2, 5)]);
|
|
120
|
+
});
|
|
121
|
+
await waitFor(() => {
|
|
122
|
+
expect(getInput()?.value).toBe("Mar 5, 2026");
|
|
123
|
+
});
|
|
124
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
93
127
|
});
|