Skip to content

fix(date, date-range): ensure date format override returns correct formatted value #7359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/components/date-range/date-range.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const DateRange = ({
endRef,
required,
isOptional,
dateFormatOverride: dateFormatOverrideProp,
datePickerStartAriaLabel,
datePickerStartAriaLabelledBy,
datePickerEndAriaLabel,
Expand All @@ -169,8 +170,12 @@ export const DateRange = ({
const l = useLocale();
const { dateFnsLocale, dateFormatOverride } = l.date;
const { format } = useMemo(
() => getFormatData(dateFnsLocale(), dateFormatOverride),
[dateFnsLocale, dateFormatOverride],
() =>
getFormatData(
dateFnsLocale(),
dateFormatOverrideProp || dateFormatOverride,
),
[dateFnsLocale, dateFormatOverrideProp, dateFormatOverride],
);
const inlineLabelWidth = 40;
const [lastChangedDate, setLastChangedDate] = useState("");
Expand Down
36 changes: 11 additions & 25 deletions src/components/date-range/date-range.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from "react";
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { enGB as enGBLocale } from "date-fns/locale/en-GB";
import { de as deLocale } from "date-fns/locale/de";

import DateRange, { DateRangeChangeEvent } from "./date-range.component";
import { testStyledSystemMargin } from "../../__spec_helper__/__internal__/test-utils";
Expand Down Expand Up @@ -1219,17 +1218,17 @@ test("should have the default styling when the `labelsInline` prop is set and `v
});

describe("Locale formatting overrides", () => {
test("should render with the input value matching the expected format when `dateFormatOverride` is set and the language is `de-DE`", () => {
it("should render with the input value matching the expected format when `dateFormatOverride` is passed as a translation key", () => {
render(
<I18nProvider
locale={{
locale: () => "de-DE",
locale: () => "en-GB",
date: {
ariaLabels: {
nextMonthButton: () => "foo",
previousMonthButton: () => "foo",
},
dateFnsLocale: () => deLocale,
dateFnsLocale: () => enGBLocale,
dateFormatOverride: "y-m-ddd",
},
}}
Expand All @@ -1251,28 +1250,15 @@ describe("Locale formatting overrides", () => {
);
});

test("should render with the input value matching the expected format when `dateFormatOverride` is set and the language is `en-GB`", () => {
it("should render with the input value matching the expected format when `dateFormatOverride` is passed as a prop", () => {
render(
<I18nProvider
locale={{
locale: () => "en-GB",
date: {
ariaLabels: {
nextMonthButton: () => "foo",
previousMonthButton: () => "foo",
},
dateFnsLocale: () => enGBLocale,
dateFormatOverride: "y-m-ddd",
},
}}
>
<DateRange
startLabel="start"
endLabel="end"
value={["2016-10-10", "2016-11-11"]}
onChange={() => {}}
/>
</I18nProvider>,
<DateRange
startLabel="start"
endLabel="end"
value={["2016-10-10", "2016-11-11"]}
onChange={() => {}}
dateFormatOverride="y-m-ddd"
/>,
);

expect(screen.getByRole("textbox", { name: "start" })).toHaveValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,39 +511,65 @@ test("should default to en-GB locale if no locale code string passed to `getForm
});

describe("dateFormatOverride tests", () => {
const localeCodes = [
// handled locales
"en-CA",
"en-US",
"en-ZA",
"fr-CA",
"ar-EG",
// random locale to cover default switch scenario
"zh-HK",
];
const dateFormatOverride = "dd Mo yyyy";

test.each(localeCodes)(
"should support %s locale code string passed to `getFormatData` when dateFormatOverride is provided",
(code: string) => {
const { formats, format } = getFormatData({ code }, dateFormatOverride);

const expectedFormats = getExpectedFormatForLocale(code);

expect(
expectedFormats.every((formatStr) => formats.includes(formatStr)) &&
formats.length === expectedFormats.length,
).toEqual(true);

expect(format).toEqual(dateFormatOverride);
},
);
it("should return the expected formats when `dateFormatOverride` is passed as NA locale", () => {
const dateFormatOverride = "MM/dd/yyyy";
const { formats, format } = getFormatData(
{ code: "en-GB" },
dateFormatOverride,
);

// since the dateFormatOverride starts with the month, it should be treated as a NA locale
const expectedFormats = getExpectedFormatForLocale("en-US");

expect(
expectedFormats.every((formatStr) => formats.includes(formatStr)) &&
formats.length === expectedFormats.length,
).toEqual(true);

expect(format).toEqual(dateFormatOverride);
});

it("should return the expected formats when `dateFormatOverride` is passed as EU locale", () => {
const dateFormatOverride = "dd/MM/yyyy";
const { formats, format } = getFormatData(
{ code: "en-US" },
dateFormatOverride,
);

// since the dateFormatOverride starts with the day, it should be treated as a EU locale
const expectedFormats = getExpectedFormatForLocale("en-GB");

expect(
expectedFormats.every((formatStr) => formats.includes(formatStr)) &&
formats.length === expectedFormats.length,
).toEqual(true);

expect(format).toEqual(dateFormatOverride);
});

it("should return the expected formats when `dateFormatOverride` is passed as CN locale", () => {
const dateFormatOverride = "yyyy/MM/dd";
const { formats, format } = getFormatData(
{ code: "en-GB" },
dateFormatOverride,
);

// since the dateFormatOverride starts with the year, it should be treated as a CN locale
const expectedFormats = getExpectedFormatForLocale("zh");

expect(
expectedFormats.every((formatStr) => formats.includes(formatStr)) &&
formats.length === expectedFormats.length,
).toEqual(true);

expect(format).toEqual(dateFormatOverride);
});
});

describe.each(euLocales)(
"when EU locales are passed to `getFormatData`",
(locale: string) => {
test(`should return the expected object shape for ${locale} locale`, () => {
it(`should return the expected object shape for ${locale} locale`, () => {
const { formats } = getFormatData({ code: locale });

const expectedFormats = getExpectedFormatForLocale(locale);
Expand All @@ -559,7 +585,7 @@ describe.each(euLocales)(
describe.each(naLocales)(
"when NA locales are passed to `getFormatData`",
(locale: string) => {
test(`should return the expected object shape for ${locale} locale`, () => {
it(`should return the expected object shape for ${locale} locale`, () => {
const { formats } = getFormatData({ code: locale });

const expectedFormats = getExpectedFormatForLocale(locale);
Expand All @@ -575,7 +601,7 @@ describe.each(naLocales)(
describe.each(cnLocales)(
"when CN locales are passed to `getFormatData`",
(locale: string) => {
test(`should return the expected object shape for ${locale} locale`, () => {
it(`should return the expected object shape for ${locale} locale`, () => {
const { formats } = getFormatData({ code: locale });

const expectedFormats = getExpectedFormatForLocale(locale);
Expand Down
55 changes: 17 additions & 38 deletions src/components/date/__internal__/date-formats/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,51 +145,30 @@ const getFormatData = (
dateFormatOverride?: string,
): LocaleFormats => {
const code = locale?.code || "en-GB";
if (dateFormatOverride) {
const { format } = getOutputFormatForLocale(code);
let formatFromLocale;

switch (code) {
case "en-CA":
case "en-US":
formatFromLocale = "MM/dd/yyyy";
break;
case "ar-EG":
case "en-ZA":
case "fr-CA":
formatFromLocale = "dd/MM/yyyy";
break;
default:
formatFromLocale = format;
}

const formatsForLocale = getInputFormatsArrayForLocale(formatFromLocale);
const dateFnsFormatOverrides: Record<
string,
{ format: string; separator?: Separator }
> = {
"en-CA": { format: "MM/dd/yyyy", separator: "/" },
"en-US": { format: "MM/dd/yyyy", separator: "/" },
"fr-CA": { format: "dd/MM/yyyy" },
"en-ZA": { format: "dd/MM/yyyy" },
"ar-EG": { format: "dd/MM/yyyy" },
};

return {
format: dateFormatOverride,
formats: generateFormats(formatsForLocale, "/"),
};
}
const { format: defaultFormat, separator: defaultSeparator } =
getOutputFormatForLocale(code);

if (["en-CA", "en-US"].includes(code)) {
const format = "MM/dd/yyyy";
const formats = getInputFormatsArrayForLocale(format);
return {
format,
formats: generateFormats(formats, "/"),
};
}
const override = dateFnsFormatOverrides[code] || {};

const { format, separator } = getOutputFormatForLocale(code);
const outputFormat = ["fr-CA", "en-ZA", "ar-EG"].includes(code)
? "dd/MM/yyyy"
: format;
const formatsForLocale = getInputFormatsArrayForLocale(outputFormat);
const format = dateFormatOverride ?? override.format ?? defaultFormat;
const separator = override.separator ?? defaultSeparator;

return {
format: outputFormat,
format,
formats: generateFormats(
formatsForLocale,
getInputFormatsArrayForLocale(format),
separator,
getTrailingChar(format),
),
Expand Down
Loading
Loading