Skip to content

Commit

Permalink
make locales work with pattern-format
Browse files Browse the repository at this point in the history
  • Loading branch information
bjosttveit committed Oct 24, 2024
1 parent af53464 commit 9fabcb0
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 28 deletions.
17 changes: 8 additions & 9 deletions src/layout/Datepicker/DatePickerInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<HTMLButtonElement>,
) => {
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(() => {
Expand All @@ -39,7 +38,7 @@ export const DatePickerInput = forwardRef(

const saveValue = (e: React.ChangeEvent<HTMLInputElement>) => {
const stringValue = e.target.value;
const date = strictParseFormat(stringValue, formatString);
const date = strictParseFormat(stringValue, datepickerFormat);
const valueToSave = getSaveFormattedDateString(date, timeStamp) ?? stringValue;
onValueChange && onValueChange(valueToSave);
};
Expand All @@ -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);
}
};
Expand All @@ -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}
Expand Down
16 changes: 5 additions & 11 deletions src/layout/Datepicker/DatepickerComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -30,15 +25,14 @@ 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);
const modalRef = useRef<HTMLDialogElement>(null);

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);
Expand Down Expand Up @@ -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))}
Expand All @@ -131,7 +125,7 @@ export function DatepickerComponent({ node }: IDatepickerProps) {
)}
</Grid>
<span className={`${styles.formatText} no-visual-testing`}>
{langAsString('date_picker.format_text', [formatDate(new Date(), dateFormat, { locale: currentLocale })])}
{langAsString('date_picker.format_text', [formatDate(new Date(), dateFormat)])}
</span>
</div>
</ComponentStructureWrapper>
Expand Down
6 changes: 3 additions & 3 deletions src/layout/Datepicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 8 additions & 5 deletions src/utils/formatDateLocale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 9fabcb0

Please sign in to comment.