Skip to content

Commit

Permalink
feat(i18n): convert date/datetime input strings to use i18n primitives (
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge committed Oct 9, 2023
1 parent 3942229 commit ab1477a
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 46 deletions.
39 changes: 39 additions & 0 deletions dev/test-studio/plugins/locale-no-nb/bundles/studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,45 @@ const studioResources: Record<StudioLocaleResourceKeys, string> = {
'timeAgo.seconds.minimal': '{{count}}m',
/* Relative time, granularity: seconds, using a minimal format, configured to show ago suffix*/
'timeAgo.seconds.minimal.ago': '{{count}}m ago',

/** --- DateTime (and Date) Input --- */

/** Action message for navigating to previous month */
'inputs.datetime.calendar.action.previous-month': `Forrige måned`,
/** Action message for navigating to next year */
'inputs.datetime.calendar.action.next-year': `Neste år`,
/** Action message for navigating to previous year */
'inputs.datetime.calendar.action.previous-year': `Forrige år`,
/** Action message for selecting hour */
'inputs.datetime.calendar.action.select-hour': `Velg time`,
/** Action message for setting to current time */
'inputs.datetime.calendar.action.set-to-current-time': `Sett til nå`,

/** Month names */
'inputs.datetime.calendar.month-names.january': 'Januar',
'inputs.datetime.calendar.month-names.february': 'Februar',
'inputs.datetime.calendar.month-names.march': 'Mars',
'inputs.datetime.calendar.month-names.april': 'April',
'inputs.datetime.calendar.month-names.may': 'Mai',
'inputs.datetime.calendar.month-names.june': 'Juni',
'inputs.datetime.calendar.month-names.july': 'Juli',
'inputs.datetime.calendar.month-names.august': 'August',
'inputs.datetime.calendar.month-names.september': 'September',
'inputs.datetime.calendar.month-names.october': 'Oktober',
'inputs.datetime.calendar.month-names.november': 'November',
'inputs.datetime.calendar.month-names.december': 'Desember',

/** Short weekday names */
'inputs.datetime.calendar.weekday-names.short.monday': 'Man',
'inputs.datetime.calendar.weekday-names.short.tuesday': 'Tir',
'inputs.datetime.calendar.weekday-names.short.wednesday': 'Ons',
'inputs.datetime.calendar.weekday-names.short.thursday': 'Tor',
'inputs.datetime.calendar.weekday-names.short.friday': 'Fre',
'inputs.datetime.calendar.weekday-names.short.saturday': 'Lør',
'inputs.datetime.calendar.weekday-names.short.sunday': 'Søn',

/** Label for selecting a hour preset. Receives a `time` param as a string on hh:mm format and a `date` param as a Date instance denoting the preset date */
'inputs.datetime.calendar.action.set-to-time-preset': '{{time}} on {{date, datetime}}',
}

export default studioResources
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, {useEffect} from 'react'
import {TextInput, useForwardedRef} from '@sanity/ui'
import {DateTimeInput} from './base/DateTimeInput'
import {ParseResult} from './types'
import {CalendarLabels} from './base/calendar/types'

export interface CommonDateTimeInputProps {
id: string
Expand All @@ -18,6 +19,7 @@ export interface CommonDateTimeInputProps {
serialize: (date: Date) => string
timeStep?: number
value: string | undefined
calendarLabels: CalendarLabels
}

const DEFAULT_PLACEHOLDER_TIME = new Date()
Expand Down Expand Up @@ -91,6 +93,7 @@ export const CommonDateTimeInput = React.forwardRef(function CommonDateTimeInput
) : (
<DateTimeInput
{...restProps}
calendarLabels={props.calendarLabels}
id={id}
selectTime={selectTime}
timeStep={timeStep}
Expand Down
27 changes: 8 additions & 19 deletions packages/sanity/src/core/form/inputs/DateInputs/DateInput.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import React, {useCallback} from 'react'
import React, {useCallback, useMemo} from 'react'
import {format, parse} from '@sanity/util/legacyDateFormat'
import {set, unset} from '../../patch'
import {StringInputProps} from '../../types'
import {useTranslation} from '../../../i18n'
import {CommonDateTimeInput} from './CommonDateTimeInput'

interface ParsedOptions {
dateFormat: string
calendarTodayLabel: string
}

interface SchemaOptions {
dateFormat?: string
calendarTodayLabel?: string
}
import {CalendarLabels} from './base/calendar/types'
import {getCalendarLabels} from './utils'

/**
* @hidden
Expand All @@ -24,13 +17,6 @@ const VALUE_FORMAT = 'YYYY-MM-DD'
// default to how they are stored
const DEFAULT_DATE_FORMAT = VALUE_FORMAT

function parseOptions(options: SchemaOptions = {}): ParsedOptions {
return {
dateFormat: options.dateFormat || DEFAULT_DATE_FORMAT,
calendarTodayLabel: options.calendarTodayLabel || 'Today',
}
}

const deserialize = (value: string) => parse(value, VALUE_FORMAT)
const serialize = (date: Date) => format(date, VALUE_FORMAT)

Expand All @@ -39,7 +25,8 @@ const serialize = (date: Date) => format(date, VALUE_FORMAT)
* @beta */
export function DateInput(props: DateInputProps) {
const {readOnly, onChange, schemaType, elementProps, value} = props
const {dateFormat} = parseOptions(schemaType.options)
const dateFormat = schemaType.options?.dateFormat || DEFAULT_DATE_FORMAT
const {t} = useTranslation()

const handleChange = useCallback(
(nextDate: string | null) => {
Expand All @@ -55,6 +42,7 @@ export function DateInput(props: DateInputProps) {
[dateFormat],
)

const calendarLabels: CalendarLabels = useMemo(() => getCalendarLabels(t), [t])
return (
<CommonDateTimeInput
{...elementProps}
Expand All @@ -63,6 +51,7 @@ export function DateInput(props: DateInputProps) {
onChange={handleChange}
parseInputValue={parseInputValue}
placeholder={schemaType.placeholder}
calendarLabels={calendarLabels}
readOnly={readOnly}
selectTime={false}
serialize={serialize}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import {format, parse} from '@sanity/util/legacyDateFormat'
import {getMinutes, setMinutes, parseISO} from 'date-fns'
import React, {useCallback} from 'react'
import React, {useCallback, useMemo} from 'react'
import {set, unset} from '../../patch'
import {StringInputProps} from '../../types'
import {useTranslation} from '../../../i18n'
import {CommonDateTimeInput} from './CommonDateTimeInput'
import {ParseResult} from './types'
import {isValidDate} from './utils'
import {getCalendarLabels, isValidDate} from './utils'
import {CalendarLabels} from './base/calendar/types'

interface ParsedOptions {
dateFormat: string
timeFormat: string
timeStep: number
calendarTodayLabel: string
}

interface SchemaOptions {
dateFormat?: string
timeFormat?: string
timeStep?: number
calendarTodayLabel?: string
}

/**
Expand All @@ -34,7 +34,6 @@ function parseOptions(options: SchemaOptions = {}): ParsedOptions {
dateFormat: options.dateFormat || DEFAULT_DATE_FORMAT,
timeFormat: options.timeFormat || DEFAULT_TIME_FORMAT,
timeStep: ('timeStep' in options && Number(options.timeStep)) || 1,
calendarTodayLabel: options.calendarTodayLabel || 'Today',
}
}

Expand Down Expand Up @@ -73,6 +72,7 @@ export function DateTimeInput(props: DateTimeInputProps) {
const {onChange, schemaType, value, elementProps} = props

const {dateFormat, timeFormat, timeStep} = parseOptions(schemaType.options)
const {t} = useTranslation()

const handleChange = useCallback(
(nextDate: string | null) => {
Expand All @@ -95,10 +95,11 @@ export function DateTimeInput(props: DateTimeInputProps) {
(inputValue: string) => parse(inputValue, `${dateFormat} ${timeFormat}`),
[dateFormat, timeFormat],
)

const calendarLabels: CalendarLabels = useMemo(() => getCalendarLabels(t), [t])
return (
<CommonDateTimeInput
{...elementProps}
calendarLabels={calendarLabels}
onChange={handleChange}
deserialize={deserialize}
formatInputValue={formatInputValue}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {ParseResult} from '../types'
import {CommonDateTimeInput} from '../CommonDateTimeInput'
import {isValidDate} from '../utils'
import {renderStringInput} from '../../../../../../test/form'
import {CalendarLabels} from '../base/calendar/types'

function parseInputValue(input: string): ParseResult {
const candidate = parse(input, 'yyyy-MM-dd HH:mm', 0)
Expand All @@ -28,6 +29,30 @@ function serialize(date: Date): string {
return date.toISOString()
}

const CALENDAR_LABELS: CalendarLabels = {
previousYear: 'Previous year',
nextYear: 'Next year',
goToPreviousMonth: 'Goto previous mont',
selectHour: 'Select hour',
setToCurrentTime: 'Set to current time',
monthNames: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
weekDayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
setToTimePreset: (time: string, date: Date) => `${time} on ${format(date, 'yyyy-MM-dd')}`,
}

async function renderInput() {
const onChange = jest.fn()

Expand All @@ -42,6 +67,7 @@ async function renderInput() {
return (
<CommonDateTimeInput
deserialize={deserialize}
calendarLabels={CALENDAR_LABELS}
id={id}
formatInputValue={formatInputValue}
onChange={onChange}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React from 'react'
import {Calendar} from './calendar/Calendar'
import {CalendarLabels} from './calendar/types'

export const DatePicker = React.forwardRef(function DatePicker(
props: Omit<React.ComponentProps<'div'>, 'onChange'> & {
value?: Date
onChange: (nextDate: Date) => void
selectTime?: boolean
timeStep?: number
calendarLabels: CalendarLabels
},
ref: React.ForwardedRef<HTMLDivElement>,
) {
const {value = new Date(), onChange, ...rest} = props
const {value = new Date(), onChange, calendarLabels, ...rest} = props
const [focusedDate, setFocusedDay] = React.useState<Date>()

const handleSelect = React.useCallback(
Expand All @@ -24,6 +26,7 @@ export const DatePicker = React.forwardRef(function DatePicker(
return (
<Calendar
{...rest}
labels={calendarLabels}
ref={ref}
selectedDate={value}
onSelect={handleSelect}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {Box, Button, LayerProvider, Popover, useClickOutside, useForwardedRef} f
import {CalendarIcon} from '@sanity/icons'
import {DatePicker} from './DatePicker'
import {LazyTextInput} from './LazyTextInput'
import {CalendarLabels, MonthNames} from './calendar/types'

export interface DateTimeInputProps {
customValidity?: string
Expand All @@ -16,13 +17,23 @@ export interface DateTimeInputProps {
selectTime?: boolean
timeStep?: number
value?: Date
calendarLabels: CalendarLabels
}

export const DateTimeInput = forwardRef(function DateTimeInput(
props: DateTimeInputProps,
ref: React.ForwardedRef<HTMLInputElement>,
) {
const {value, inputValue, onInputChange, onChange, selectTime, timeStep, ...rest} = props
const {
value,
inputValue,
onInputChange,
onChange,
selectTime,
timeStep,
calendarLabels,
...rest
} = props
const [popoverRef, setPopoverRef] = useState<HTMLElement | null>(null)
const forwardedRef = useForwardedRef(ref)
const buttonRef = useRef(null)
Expand Down Expand Up @@ -78,6 +89,7 @@ export const DateTimeInput = forwardRef(function DateTimeInput(
<Box overflow="auto">
<FocusLock onDeactivation={handleDeactivation}>
<DatePicker
calendarLabels={calendarLabels}
selectTime={selectTime}
timeStep={timeStep}
onKeyUp={handleKeyUp}
Expand Down
Loading

0 comments on commit ab1477a

Please sign in to comment.