diff --git a/src/layout/Datepicker/DatePickerInput.tsx b/src/layout/Datepicker/DatePickerInput.tsx index 888a6c14c..60b623fed 100644 --- a/src/layout/Datepicker/DatePickerInput.tsx +++ b/src/layout/Datepicker/DatePickerInput.tsx @@ -9,11 +9,11 @@ import { format, isValid } from 'date-fns'; import { useLanguage } from 'src/features/language/useLanguage'; import styles from 'src/layout/Datepicker/Calendar.module.css'; import { getSaveFormattedDateString, strictParseFormat, strictParseISO } from 'src/utils/dateHelpers'; -import { getFormatDisplay, getFormatPattern } from 'src/utils/formatDateLocale'; +import { getFormatPattern } from 'src/utils/formatDateLocale'; export interface DatePickerInputProps { id: string; - formatString: string; + datepickerFormat: string; timeStamp: boolean; value?: string; onValueChange?: (value: string) => void; @@ -24,13 +24,12 @@ export interface DatePickerInputProps { export const DatePickerInput = forwardRef( ( - { id, value, formatString, timeStamp, onValueChange, isDialogOpen, readOnly, onClick }: DatePickerInputProps, + { id, value, datepickerFormat, timeStamp, onValueChange, isDialogOpen, readOnly, onClick }: DatePickerInputProps, ref: RefObject, ) => { - const formatDisplay = getFormatDisplay(formatString); - const formatPattern = getFormatPattern(formatDisplay); + const formatPattern = getFormatPattern(datepickerFormat); const dateValue = strictParseISO(value); - const formattedDateValue = dateValue ? format(dateValue, formatString) : value; + const formattedDateValue = dateValue ? format(dateValue, datepickerFormat) : value; const [inputValue, setInputValue] = useState(formattedDateValue ?? ''); useEffect(() => { @@ -39,7 +38,7 @@ export const DatePickerInput = forwardRef( const saveValue = (e: React.ChangeEvent) => { const stringValue = e.target.value; - const date = strictParseFormat(stringValue, formatString); + const date = strictParseFormat(stringValue, datepickerFormat); const valueToSave = getSaveFormattedDateString(date, timeStamp) ?? stringValue; onValueChange && onValueChange(valueToSave); }; @@ -48,7 +47,7 @@ export const DatePickerInput = forwardRef( const stringValue = e.target.value; setInputValue(stringValue); // If the date is valid, save immediately - if (isValid(strictParseFormat(stringValue, formatString))) { + if (stringValue.length == 0 || isValid(strictParseFormat(stringValue, datepickerFormat))) { saveValue(e); } }; @@ -65,7 +64,7 @@ export const DatePickerInput = forwardRef( type='text' id={id} value={inputValue} - placeholder={formatDisplay} + placeholder={datepickerFormat.toUpperCase()} onChange={handleChange} onBlur={saveValue} readOnly={readOnly} diff --git a/src/layout/Datepicker/DatepickerComponent.tsx b/src/layout/Datepicker/DatepickerComponent.tsx index 24ce08365..670858fc7 100644 --- a/src/layout/Datepicker/DatepickerComponent.tsx +++ b/src/layout/Datepicker/DatepickerComponent.tsx @@ -13,13 +13,8 @@ import { ComponentStructureWrapper } from 'src/layout/ComponentStructureWrapper' import styles from 'src/layout/Datepicker/Calendar.module.css'; import { DatePickerCalendar } from 'src/layout/Datepicker/DatePickerCalendar'; import { DatePickerInput } from 'src/layout/Datepicker/DatePickerInput'; -import { - getDateConstraint, - getDateFormat, - getLocale, - getSaveFormattedDateString, - strictParseISO, -} from 'src/utils/dateHelpers'; +import { getDateConstraint, getDateFormat, getSaveFormattedDateString, strictParseISO } from 'src/utils/dateHelpers'; +import { getDatepickerFormat } from 'src/utils/formatDateLocale'; import { useNodeItem } from 'src/utils/layout/useNodeItem'; import type { PropsFromGenericComponent } from 'src/layout'; @@ -30,7 +25,6 @@ export type IDatepickerProps = PropsFromGenericComponent<'Datepicker'>; export function DatepickerComponent({ node }: IDatepickerProps) { const { langAsString } = useLanguage(); const languageLocale = useCurrentLanguage(); - const currentLocale = getLocale(languageLocale ?? 'nb'); const { minDate, maxDate, format, timeStamp = true, readOnly, required, id, dataModelBindings } = useNodeItem(node); const [isDialogOpen, setIsDialogOpen] = useState(false); @@ -38,7 +32,7 @@ export function DatepickerComponent({ node }: IDatepickerProps) { const calculatedMinDate = getDateConstraint(minDate, 'min'); const calculatedMaxDate = getDateConstraint(maxDate, 'max'); - const dateFormat = getDateFormat(format, languageLocale); + const dateFormat = getDatepickerFormat(getDateFormat(format, languageLocale)); const isMobile = useIsMobile(); const { setValue, formData } = useDataModelBindings(dataModelBindings); @@ -111,7 +105,7 @@ export function DatepickerComponent({ node }: IDatepickerProps) { id={id} value={value} isDialogOpen={isMobile ? modalRef.current?.open : isDialogOpen} - formatString={dateFormat} + datepickerFormat={dateFormat} timeStamp={timeStamp} onValueChange={handleInputValueChange} onClick={() => (isMobile ? modalRef.current?.showModal() : setIsDialogOpen(!isDialogOpen))} @@ -131,7 +125,7 @@ export function DatepickerComponent({ node }: IDatepickerProps) { )} - {langAsString('date_picker.format_text', [formatDate(new Date(), dateFormat, { locale: currentLocale })])} + {langAsString('date_picker.format_text', [formatDate(new Date(), dateFormat)])} diff --git a/src/layout/Datepicker/index.tsx b/src/layout/Datepicker/index.tsx index ad1da6fd3..531f5e1d2 100644 --- a/src/layout/Datepicker/index.tsx +++ b/src/layout/Datepicker/index.tsx @@ -9,7 +9,7 @@ import { DatepickerComponent } from 'src/layout/Datepicker/DatepickerComponent'; import { DatepickerSummary } from 'src/layout/Datepicker/DatepickerSummary'; import { SummaryItemSimple } from 'src/layout/Summary/SummaryItemSimple'; import { formatISOString, getDateConstraint, getDateFormat, strictParseISO } from 'src/utils/dateHelpers'; -import { getFormatDisplay } from 'src/utils/formatDateLocale'; +import { getDatepickerFormat } from 'src/utils/formatDateLocale'; import type { LayoutValidationCtx } from 'src/features/devtools/layoutValidation/types'; import type { DisplayDataProps } from 'src/features/displayData'; import type { BaseValidation, ComponentValidation, ValidationDataSources } from 'src/features/validation'; @@ -88,13 +88,13 @@ export class Datepicker extends DatepickerDef implements ValidateComponent<'Date nodeDataSelector((picker) => picker(node)?.item?.format, [node]), currentLanguage, ); - const formatDisplay = getFormatDisplay(format); + const datePickerFormat = getDatepickerFormat(format).toUpperCase(); const validations: ComponentValidation[] = []; const date = strictParseISO(dataAsString); if (!date) { validations.push({ - message: { key: 'date_picker.invalid_date_message', params: [formatDisplay] }, + message: { key: 'date_picker.invalid_date_message', params: [datePickerFormat] }, severity: 'error', source: FrontendValidationSource.Component, category: ValidationMask.Component, diff --git a/src/utils/formatDateLocale.ts b/src/utils/formatDateLocale.ts index a28da15c3..4087fa0fb 100644 --- a/src/utils/formatDateLocale.ts +++ b/src/utils/formatDateLocale.ts @@ -114,26 +114,29 @@ export function formatDateLocale(localeStr: string, date: Date, unicodeFormat?: }, ''); } -export function getFormatDisplay(unicodeFormat: string): string { +/** + * This function will massage locale date formats to require a fixed number of characters so that a pattern-format can be used on the text input + */ +export function getDatepickerFormat(unicodeFormat: string): string { const tokens = unicodeFormat.split(UNICODE_TOKENS) as Token[]; const separators: Separator[] = unicodeFormat.match(UNICODE_TOKENS) ?? []; return tokens.reduce((acc, token: Token, index) => { if (['y', 'yy', 'yyy', 'yyyy', 'u', 'uu', 'uuu', 'uuuu'].includes(token)) { - return `${acc}YYYY${separators?.[index] ?? ''}`; + return `${acc}yyyy${separators?.[index] ?? ''}`; } if (['M', 'MM', 'MMM', 'MMMM', 'MMMMM'].includes(token)) { return `${acc}MM${separators?.[index] ?? ''}`; } if (['d', 'dd'].includes(token)) { - return `${acc}DD${separators?.[index] ?? ''}`; + return `${acc}dd${separators?.[index] ?? ''}`; } return acc; }, ''); } -export function getFormatPattern(formatDisplay: string): string { - return formatDisplay.replaceAll(/D|M|Y/g, '#'); +export function getFormatPattern(datePickerFormat: string): string { + return datePickerFormat.replaceAll(/d|m|y/gi, '#'); } function selectPartToUse(parts: Intl.DateTimeFormatPart[], token: Token) {