feat: add DatePicker and DatePickerModal components#77
Conversation
Add DatePicker (inline) and DatePickerModal (modal, controlled imperatively via ref) wrapping react-native-date-picker, with platform-aware palette theming and full test coverage. Also initialize OpenSpec in the repo and add the add-date-picker change with its proposal, design, specs and tasks.
| import Collapsible from 'atoms/Collapsible'; | ||
| import Modal from 'atoms/Modal'; | ||
| import DatePicker from 'atoms/DatePicker/DatePicker'; | ||
| import DatePickerModal from 'atoms/DatePicker/DatePickerModal'; |
There was a problem hiding this comment.
fijate aca @GonzaFran que estamos "rompiendo" un poco la convencion que canda componente tiene su index y se imorta todo desde la estructura interna del coomnponente.
Lo que podmeos hacer es que DAtePciker tenga su index como el resto de componenentes y dicho index tendria
export { default as DatePicker } from './DatePicker';
export { default as DatePickerModal } from './DatePickerModal';
en el index general podes importar como
import { DatePicker, DatePickerModal } from 'atoms/DatePicker';
````
| testID, | ||
| }: SharedDatePickerProps): RNDatePickerProps => { | ||
| if (minimumDate && maximumDate && minimumDate > maximumDate) { | ||
| console.warn('DatePicker: `minimumDate` is greater than `maximumDate`.'); |
There was a problem hiding this comment.
aca agregaria que solo loguee con isDev
| // buttonColor and dividerColor are Android-only props. | ||
| const androidTint = Platform.select({ | ||
| android: {buttonColor: palette.primary.main, dividerColor: palette.grey[300]}, | ||
| default: undefined, |
There was a problem hiding this comment.
por algun motivo definimos por default undefined y no {} que seria el tipo de dato que devuelte android? IOS necesita undefined?
|
@colomfernando listo los cambios! |
Coverage Report for CI Build 27980519294Coverage remained the same at 100.0%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…SRN-523-agregar-componente-date-picker-modal-a-ui-native
…tive' of github.com:janis-commerce/ui-native into APPSRN-523-agregar-componente-date-picker-modal-a-ui-native
colomfernando
left a comment
There was a problem hiding this comment.
@GonzaFran te deje comentarios
| import {palette} from 'theme/palette'; | ||
| import {isDevEnv} from 'utils/index'; | ||
|
|
||
| export type SharedDatePickerProps = Pick< |
There was a problem hiding this comment.
te comento lo mismo que deje en el de nico, me parece que los types no son utils. Podriamos tener el archivo types.ts en el root de Datepicker @GonzaFran
| @@ -0,0 +1,2 @@ | |||
| export {default as DatePicker} from './DatePicker'; | |||
There was a problem hiding this comment.
este archivo puede ser mejor ts, porque no tiene sintaxis de jsx
| }); | ||
|
|
||
| return { | ||
| date: date ?? new Date(), |
There was a problem hiding this comment.
aca tenemos un problema a revisar. LA funcion getSharedProps se termina usando en ambos componentes. Esto quiere decir que si por algun motivo se dispara un render, la funcion se ejecuta de vuelta y va a generar un nuevo date.
Una solucion es que en cada componente tengas un useState que se inicia con new Date y seria el fallback si date no viene. De esa manera te aseguras que el componente tiene un solo new Data cuando se monta
There was a problem hiding this comment.
corregido! pero en vez de un useState se usó un useMemo, porque el valor para date vive por fuera del componente, es del consumidor.
| useImperativeHandle(ref, () => ({ | ||
| open: () => setIsVisible(true), | ||
| close: () => setIsVisible(false), | ||
| })); |
There was a problem hiding this comment.
aca podriamos pasar el tercer argumentos deps vacias [], con esto evitamos que en cada render tengamos una referencia nueva del useImperativeHandle dado que es estable y no depende de cambios
| close: () => setIsVisible(false), | ||
| })); | ||
|
|
||
| const handleConfirm = (selectedDate: Date) => { |
There was a problem hiding this comment.
tanto handleConfirm como handleCAncel podrimaos usar useCallback con su dep de handleConfirm y hancledCancel. Obviamente que el consumidor tiene que memoizar los handlers cuando lo pase por props y como nosotros somos los consumidores tenemos mas control.
De esta manera evitamos 2 renders por los handlers
There was a problem hiding this comment.
@colomfernando estuve revisando esto con Claude para aplicar el cambio y encontré que el componente de date-picker que usamos internamente (el DatePicker de la librería) no está memoizado, así que agregarle un useCallback a estas funciones no le aporta nada ni evita re-renders. El date-picker original no compara referencias y se vuelve a renderizar cada vez que nuestro componente se re-renderiza, le pasemos funciones estables o no.
Lo que sí voy a agregar es memoizar nuestros componentes (los que expone ui-native) para evitar que se re-rendericen en cada render del consumidor
Forward onStateChange, expose dividerColor/buttonColor with palette fallback, move types to types.ts and resolve the date fallback via useMemo. Memoize both components with displayName and add the project CLAUDE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Link al ticket
Descripción del requerimiento
@janiscommerce/ui-nativeno tenía un componente nativo para selección de fechas y horas. Las apps que necesitaban un date picker tenían queresolver por su cuenta la dependencia y el wrapping, sin una interfaz consistente ni theming aplicado.
Descripción de la solución
Se incorporaron dos componentes que envuelven
react-native-date-picker(módulo nativo, peerDependency):DatePicker: picker inline, siempre visible. Declarativo: emite cambios en tiempo real víaonDateChange.DatePickerModal: picker en modal. Control imperativo vía ref (ref.current.open()/ref.current.close()), con callbacksonConfirmyonCancel.Ambos comparten props base (
mode,minimumDate,maximumDate,locale,minuteInterval,timeZoneOffsetInMinutes,theme) resueltas enla función pura
getSharedProps(src/components/atoms/DatePicker/utils/index.ts), que también aplica el theming por plataforma: tinte delpalette de Janis (
palette.primary.mainypalette.grey[300]) en los botones y divisores de Android, y proptheme('light' | 'dark' | 'auto') en iOS (limitación de la librería — en iOS no exponebuttonColor/dividerColor).getSharedPropsincluye una validación: siminimumDate > maximumDate, lanzaconsole.warn. El propdate={null}hace que el pickerarranque en "hoy" sin emitir cambios hasta que el usuario interactúe.
Se removió
@react-native-community/datetimepickerdedependencies(era una dependencia huérfana sin uso real en el código). Se agregóreact-native-date-picker >=5.0.0como peerDependency.Ambos componentes se exportan desde
src/index.ts. El README incluye instrucciones de instalación (peerDep + pod install + rebuild) y notassobre el comportamiento de
date={null}, la zona horaria y las limitaciones de Storybook web.Nivel de pruebas requerido
Documentación y ejemplos
¿Cómo se puede probar?
DatePickercondate={null}onDateChangehasta que el usuario mueva el wheelDatePickeronDateChangese llama con la fecha seleccionadaref.current.open()enDatePickerModalDatePickerModalonConfirmrecibe elDateseleccionado y el modal se cierraonCancelse ejecuta (si fue pasada) y el modal se cierraminimumDate > maximumDateconsole.warny el picker renderiza igualgetSharedPropsEvidencias, pruebas de cómo funciona
Link a la documentación
-a-ui-native/README.md#datepicker-component-installation)
Documentación y
ejemplos
¿Cómo se puede probar?
| Caso a probar | Resultado esperado | Resultado obtenido | Observaciones |
| [ ] | ALTO: Cambios moderados | Cambios que afectan aspectos importantes pero no críticos de la aplicación. | Se puede probar localmente, salvo que surja necesidad de crear un APK. Utilizar perfil regular
para las pruebas. |
| [X] | MEDIO: Cambios menores | Cambios menores que impactan una parte del flujo o ajustes que no modifican la funcionalidad general. | Validación localmente. Se puede usar perfil dev |
| [ ] | BAJO: Ajustes | Modificaciones pequeñas que no impactan el rendimiento o funcionalidad general de la app. | Validación localmente. Se puede usar perfil dev |
¿Cómo se puede probar?
DatePickercondate={null}onDateChangehasta que el usuario mueva el wheelDatePickeronDateChangese llama con la fecha seleccionadaref.current.open()enDatePickerModalDatePickerModalonConfirmrecibe elDateseleccionado y el modal se cierraonCancelse ejecuta (si fue pasada) y el modal se cierraminimumDate > maximumDateconsole.warny el picker renderiza igualgetSharedPropsEvidencias, pruebas de cómo funciona
Link a la documentación
Datos extra a tener en cuenta
react-native-date-pickercomo peerDependency en el repo destino.react-native-weben la librería). La story incluida funciona solo en el Storybook on-device (Android/iOS).@react-native-community/datetimepickerdedependencies(estaba como dependencia directa sin usar).CHANGELOG: