@multiplatform.one/forms 6.4.2 → 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/FieldDisplay/formatters.cjs +7 -12
- package/dist/cjs/FieldDisplay/formatters.native.js +7 -12
- package/dist/cjs/FieldDisplay/formatters.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/Wheel/index.cjs +1 -1
- package/dist/cjs/Wheel/index.native.js +1 -1
- package/dist/cjs/Wheel/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/ColorPicker/parts.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 +21 -15
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +26 -16
- 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 +49 -0
- package/dist/cjs/fields/DatePicker/fieldValueSync.native.js +56 -0
- package/dist/cjs/fields/DatePicker/fieldValueSync.native.js.map +1 -0
- package/dist/cjs/fields/DatePicker/index.cjs +20 -14
- package/dist/cjs/fields/DatePicker/index.native.js +25 -15
- package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/parts.cjs +2 -3
- package/dist/cjs/fields/DatePicker/parts.native.js +2 -3
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/GeolocationMap.native.js +2 -2
- package/dist/cjs/fields/RadioGroup/index.cjs +4 -6
- package/dist/cjs/fields/RadioGroup/index.native.js +7 -6
- package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/index.cjs +20 -3
- package/dist/cjs/fields/Rating/index.native.js +31 -6
- 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 +16 -4
- package/dist/cjs/fields/Slider/index.native.js +24 -4
- 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/colorRamps.cjs +5 -1
- package/dist/cjs/shared/colorRamps.native.js +5 -1
- package/dist/cjs/shared/colorRamps.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/FieldDisplay/formatters.mjs +8 -13
- package/dist/esm/FieldDisplay/formatters.mjs.map +1 -1
- package/dist/esm/FieldDisplay/formatters.native.js +8 -13
- package/dist/esm/FieldDisplay/formatters.native.js.map +1 -1
- package/dist/esm/FieldDisplay/index.mjs.map +1 -1
- package/dist/esm/FloatingPanel/index.mjs.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/Wheel/index.mjs +1 -1
- package/dist/esm/Wheel/index.mjs.map +1 -1
- package/dist/esm/Wheel/index.native.js +1 -1
- package/dist/esm/Wheel/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/ColorPicker/index.mjs.map +1 -1
- package/dist/esm/fields/ColorPicker/parts.mjs.map +1 -1
- package/dist/esm/fields/ColorPicker/parts.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 +21 -15
- package/dist/esm/fields/DatePicker/DatetimePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js +26 -16
- 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 +23 -0
- package/dist/esm/fields/DatePicker/fieldValueSync.mjs.map +1 -0
- package/dist/esm/fields/DatePicker/fieldValueSync.native.js +27 -0
- package/dist/esm/fields/DatePicker/fieldValueSync.native.js.map +1 -0
- package/dist/esm/fields/DatePicker/index.mjs +21 -15
- package/dist/esm/fields/DatePicker/index.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/index.native.js +26 -16
- package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/parts.mjs +3 -4
- package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/parts.native.js +3 -4
- package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/utils.mjs.map +1 -1
- package/dist/esm/fields/Geolocation/GeolocationMap.native.js +2 -2
- package/dist/esm/fields/ImageField/index.mjs.map +1 -1
- package/dist/esm/fields/OTPInput/index.mjs.map +1 -1
- package/dist/esm/fields/RadioGroup/index.mjs +5 -7
- package/dist/esm/fields/RadioGroup/index.mjs.map +1 -1
- package/dist/esm/fields/RadioGroup/index.native.js +8 -7
- package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/esm/fields/Rating/index.mjs +20 -3
- package/dist/esm/fields/Rating/index.mjs.map +1 -1
- package/dist/esm/fields/Rating/index.native.js +31 -6
- 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 +16 -4
- package/dist/esm/fields/Slider/index.mjs.map +1 -1
- package/dist/esm/fields/Slider/index.native.js +24 -4
- 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/colorRamps.mjs +5 -2
- package/dist/esm/shared/colorRamps.mjs.map +1 -1
- package/dist/esm/shared/colorRamps.native.js +5 -2
- package/dist/esm/shared/colorRamps.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/FieldDisplay/formatters.mjs +8 -13
- package/dist/jsx/FieldDisplay/formatters.mjs.map +1 -1
- package/dist/jsx/FieldDisplay/formatters.native.js +7 -12
- package/dist/jsx/FieldDisplay/formatters.native.js.map +1 -1
- package/dist/jsx/FieldDisplay/index.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/index.mjs.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/Wheel/index.mjs +1 -1
- package/dist/jsx/Wheel/index.mjs.map +1 -1
- package/dist/jsx/Wheel/index.native.js +1 -1
- package/dist/jsx/Wheel/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/ColorPicker/index.mjs.map +1 -1
- package/dist/jsx/fields/ColorPicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/ColorPicker/parts.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 +21 -15
- package/dist/jsx/fields/DatePicker/DatetimePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +26 -16
- 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 +23 -0
- package/dist/jsx/fields/DatePicker/fieldValueSync.mjs.map +1 -0
- package/dist/jsx/fields/DatePicker/fieldValueSync.native.js +56 -0
- package/dist/jsx/fields/DatePicker/fieldValueSync.native.js.map +1 -0
- package/dist/jsx/fields/DatePicker/index.mjs +21 -15
- package/dist/jsx/fields/DatePicker/index.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/index.native.js +25 -15
- package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.mjs +3 -4
- package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.native.js +2 -3
- package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/utils.mjs.map +1 -1
- package/dist/jsx/fields/Geolocation/GeolocationMap.native.js +2 -2
- package/dist/jsx/fields/ImageField/index.mjs.map +1 -1
- package/dist/jsx/fields/OTPInput/index.mjs.map +1 -1
- package/dist/jsx/fields/RadioGroup/index.mjs +5 -7
- package/dist/jsx/fields/RadioGroup/index.mjs.map +1 -1
- package/dist/jsx/fields/RadioGroup/index.native.js +7 -6
- package/dist/jsx/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/jsx/fields/Rating/index.mjs +20 -3
- package/dist/jsx/fields/Rating/index.mjs.map +1 -1
- package/dist/jsx/fields/Rating/index.native.js +31 -6
- 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 +16 -4
- package/dist/jsx/fields/Slider/index.mjs.map +1 -1
- package/dist/jsx/fields/Slider/index.native.js +24 -4
- 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/colorRamps.mjs +5 -2
- package/dist/jsx/shared/colorRamps.mjs.map +1 -1
- package/dist/jsx/shared/colorRamps.native.js +5 -1
- package/dist/jsx/shared/colorRamps.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/FieldDisplay/formatters.ts +10 -14
- 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/DatePicker/parts.tsx +6 -1
- 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/FieldDisplay/formatters.d.ts +7 -1
- package/types/FieldDisplay/formatters.d.ts.map +1 -1
- package/types/InputParts/index.d.ts +4 -4
- 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 +3 -3
- 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/DatetimePicker.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 +31 -0
- package/types/fields/DatePicker/fieldValueSync.d.ts.map +1 -0
- package/types/fields/DatePicker/index.d.ts.map +1 -1
- package/types/fields/DatePicker/parts.d.ts.map +1 -1
- package/types/fields/Input/index.d.ts +2 -2
- package/types/fields/RadioGroup/index.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/colorRamps.d.ts +14 -0
- package/types/shared/colorRamps.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
|
@@ -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
|
});
|
|
@@ -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
|
|
|
@@ -414,19 +415,25 @@ export function MultiDatePicker<
|
|
|
414
415
|
setOpen(true);
|
|
415
416
|
}, [setDisplayDate]);
|
|
416
417
|
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const next = Array.isArray(
|
|
418
|
+
// Adopt an externally-applied value (controlled `value` prop or the live
|
|
419
|
+
// TanStack field value) into the display state without emitting onChange
|
|
420
|
+
// (LC-14). Skip setState when the calendar days are unchanged so a new
|
|
421
|
+
// array identity from the parent cannot force a render storm.
|
|
422
|
+
const adoptExternalValue = useCallback((raw: unknown) => {
|
|
423
|
+
const next = Array.isArray(raw) ? (raw as Date[]) : [];
|
|
423
424
|
const prev = selectedDatesRef.current;
|
|
424
425
|
const same =
|
|
425
426
|
prev.length === next.length && prev.every((d, i) => next[i] != null && isSameDay(d, next[i]));
|
|
426
427
|
if (same) return;
|
|
427
428
|
selectedDatesRef.current = next;
|
|
428
429
|
setSelectedDates(next);
|
|
429
|
-
}, [
|
|
430
|
+
}, []);
|
|
431
|
+
|
|
432
|
+
// Keep standalone/controlled state in sync when the parent value changes.
|
|
433
|
+
useEffect(() => {
|
|
434
|
+
if (valueProp === undefined) return;
|
|
435
|
+
adoptExternalValue(valueProp);
|
|
436
|
+
}, [valueProp, adoptExternalValue]);
|
|
430
437
|
|
|
431
438
|
const handleToggleDate = useCallback(
|
|
432
439
|
(date: Date, fieldChange?: (value: any) => void) => {
|
|
@@ -560,26 +567,31 @@ export function MultiDatePicker<
|
|
|
560
567
|
const resolvedError = getFieldError(field, errorProp);
|
|
561
568
|
|
|
562
569
|
return (
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
570
|
+
<>
|
|
571
|
+
{/* Form branch displays from `selectedDates` — keep it synced to
|
|
572
|
+
the live field value (late seeds / programmatic updates). */}
|
|
573
|
+
<FieldValueSync value={field.state.value} onSync={adoptExternalValue} />
|
|
574
|
+
<FieldLayout
|
|
575
|
+
id={id}
|
|
576
|
+
label={label}
|
|
577
|
+
labelProps={labelProps}
|
|
578
|
+
error={resolvedError}
|
|
579
|
+
helperText={helperText}
|
|
580
|
+
required={required}
|
|
581
|
+
size={size}
|
|
582
|
+
knobProps={knobProps}
|
|
583
|
+
// LC-40: see standalone branch — FieldLayout carries dimWhole.
|
|
584
|
+
disabled={disabled}
|
|
585
|
+
>
|
|
586
|
+
<InputParts size={size || knobProps.sizeToken}>
|
|
587
|
+
{renderPopover(
|
|
588
|
+
!!resolvedError,
|
|
589
|
+
mergeFieldHandler(field, "handleBlur", _onBlur),
|
|
590
|
+
mergeFieldHandler(field, "handleChange"),
|
|
591
|
+
)}
|
|
592
|
+
</InputParts>
|
|
593
|
+
</FieldLayout>
|
|
594
|
+
</>
|
|
583
595
|
);
|
|
584
596
|
}}
|
|
585
597
|
</Field>
|
|
@@ -32,3 +32,26 @@ export function DatePickerFieldValueSync({
|
|
|
32
32
|
}, [value, onSync]);
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Shape-agnostic variant for pickers whose display state is not a single
|
|
38
|
+
* Date (MonthPicker's {month,year}, MultiDatePicker's Date[],
|
|
39
|
+
* DateRangePicker's {start,end}): mirrors the live TanStack field value into
|
|
40
|
+
* the picker's adoption callback whenever it changes (including mount, so a
|
|
41
|
+
* Field-level defaultValue displays too). Each picker's `onSync` owns its
|
|
42
|
+
* coercion + identity-preserving equality guard, and must never emit
|
|
43
|
+
* onChange from this path (LC-14 — a programmatic value application is not
|
|
44
|
+
* a user edit).
|
|
45
|
+
*/
|
|
46
|
+
export function FieldValueSync({
|
|
47
|
+
value,
|
|
48
|
+
onSync,
|
|
49
|
+
}: {
|
|
50
|
+
value: unknown;
|
|
51
|
+
onSync: (value: unknown) => void;
|
|
52
|
+
}) {
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
onSync(value);
|
|
55
|
+
}, [value, onSync]);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
@@ -2,6 +2,7 @@ import { Calendar, ChevronLeft, ChevronRight, X } from "@tamagui/lucide-icons-2"
|
|
|
2
2
|
import {
|
|
3
3
|
ensureFocusVisibleRing,
|
|
4
4
|
formatAbsoluteDate,
|
|
5
|
+
getMonthNames,
|
|
5
6
|
useResolvedKnobs,
|
|
6
7
|
} from "@multiplatform.one/theme";
|
|
7
8
|
import {
|
|
@@ -100,6 +101,10 @@ export function useDateAnimation(current: string | number) {
|
|
|
100
101
|
// useCalendarData — compute calendar grid for a given month
|
|
101
102
|
// ---------------------------------------------------------------------------
|
|
102
103
|
|
|
104
|
+
// Picker headers speak the house month register (LC-12 render channel), not
|
|
105
|
+
// a locale pinned to en-US at the call site.
|
|
106
|
+
const monthNames = getMonthNames();
|
|
107
|
+
|
|
103
108
|
export function useCalendarData(displayDate: Date): CalendarData {
|
|
104
109
|
return useMemo(() => {
|
|
105
110
|
const year = displayDate.getFullYear();
|
|
@@ -122,7 +127,7 @@ export function useCalendarData(displayDate: Date): CalendarData {
|
|
|
122
127
|
return {
|
|
123
128
|
year,
|
|
124
129
|
month,
|
|
125
|
-
monthName:
|
|
130
|
+
monthName: monthNames[month],
|
|
126
131
|
days,
|
|
127
132
|
today: new Date(),
|
|
128
133
|
};
|
|
@@ -107,6 +107,15 @@ export const Basic: Story = {
|
|
|
107
107
|
};
|
|
108
108
|
|
|
109
109
|
export const Disabled: Story = {
|
|
110
|
+
parameters: {
|
|
111
|
+
// LC-57 allowlist: disabled-dial demo; FileUpload's internal upload Button
|
|
112
|
+
// has no disabledReason plumbing yet (component-contract follow-up).
|
|
113
|
+
guardrailSpecimen: {
|
|
114
|
+
warns: ["bare-disabled"],
|
|
115
|
+
reason:
|
|
116
|
+
"Disabled-dial specimen: FileUpload's internal upload Button has no disabledReason plumbing yet.",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
110
119
|
render: (args) => <FileUpload {...args} />,
|
|
111
120
|
args: {
|
|
112
121
|
label: "Disabled Upload",
|
|
@@ -119,7 +128,8 @@ export const WithError: Story = {
|
|
|
119
128
|
render: (args) => <FileUpload {...args} />,
|
|
120
129
|
args: {
|
|
121
130
|
label: "Required File",
|
|
122
|
-
|
|
131
|
+
// DG-COPY-02: error copy states the rule broken — never "please"/"invalid"/"error".
|
|
132
|
+
error: "A file is required",
|
|
123
133
|
required: true,
|
|
124
134
|
},
|
|
125
135
|
};
|
|
@@ -95,6 +95,15 @@ export const Basic: Story = {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
export const Disabled: Story = {
|
|
98
|
+
parameters: {
|
|
99
|
+
// LC-57 allowlist: disabled-dial demo; Geolocation's internal action
|
|
100
|
+
// Buttons have no disabledReason plumbing yet (component-contract follow-up).
|
|
101
|
+
guardrailSpecimen: {
|
|
102
|
+
warns: ["bare-disabled"],
|
|
103
|
+
reason:
|
|
104
|
+
"Disabled-dial specimen: Geolocation's internal action Buttons have no disabledReason plumbing yet.",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
98
107
|
args: {
|
|
99
108
|
label: "Location",
|
|
100
109
|
helperText: "This field is disabled",
|