diff --git a/apps/web/content/docs/components/datepicker.mdx b/apps/web/content/docs/components/datepicker.mdx index 6ecad63e7..fe65ea6e1 100644 --- a/apps/web/content/docs/components/datepicker.mdx +++ b/apps/web/content/docs/components/datepicker.mdx @@ -19,12 +19,24 @@ Use this example to show a simple datepicker component. +## Formatted Date + +Use this `inputFormat` prop to set the format of the datepicker component to be displayed + +**Note:** don't use `DD` instead use `dd` and also for year don't use `YYYY` instead use `yyyy` + +**Note:** `Datepicker` component relies on `date-fns` for date formatting. + + + ## Localization Use the `language` prop to set the language of the datepicker component. The `labelTodayButton` and `labelClearButton` can also be used to update the text of the buttons. +**Note:** `Datepicker` component relies on `date-fns` for localization. Please refer to available localization from `date-fns`. + ## Limit the date diff --git a/apps/web/examples/datepicker/datepicker.format.tsx b/apps/web/examples/datepicker/datepicker.format.tsx new file mode 100644 index 000000000..0737f7c86 --- /dev/null +++ b/apps/web/examples/datepicker/datepicker.format.tsx @@ -0,0 +1,42 @@ +import { Datepicker } from "flowbite-react"; +import { type CodeData } from "~/components/code-demo"; + +const code = ` +"use client"; + +import { Datepicker } from "flowbite-react"; + +function Component() { + return ; +} +`; + +const codeRSC = ` +import { Datepicker } from "flowbite-react"; + +function Component() { + return ; +} +`; + +function Component() { + return ; +} + +export const format: CodeData = { + type: "single", + code: [ + { + fileName: "client", + language: "tsx", + code, + }, + { + fileName: "server", + language: "tsx", + code: codeRSC, + }, + ], + githubSlug: "datepicker/datepicker.format.tsx", + component: , +}; diff --git a/apps/web/examples/datepicker/datepicker.localization.tsx b/apps/web/examples/datepicker/datepicker.localization.tsx index 1432c748b..db5abc750 100644 --- a/apps/web/examples/datepicker/datepicker.localization.tsx +++ b/apps/web/examples/datepicker/datepicker.localization.tsx @@ -6,21 +6,21 @@ const code = ` import { Datepicker } from "flowbite-react"; -export function Component() { - return ; +function Component() { + return ; } `; const codeRSC = ` import { Datepicker } from "flowbite-react"; -export function Component() { - return ; +function Component() { + return ; } `; -export function Component() { - return ; +function Component() { + return ; } export const localization: CodeData = { diff --git a/apps/web/examples/datepicker/index.ts b/apps/web/examples/datepicker/index.ts index 9084d065e..ea44bd1ba 100644 --- a/apps/web/examples/datepicker/index.ts +++ b/apps/web/examples/datepicker/index.ts @@ -1,4 +1,5 @@ export { autoHide } from "./datepicker.autoHide"; +export { format } from "./datepicker.format"; export { inline } from "./datepicker.inline"; export { localization } from "./datepicker.localization"; export { range } from "./datepicker.range"; diff --git a/bun.lockb b/bun.lockb index 20649127d..4e0d3f233 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index fe95c0d5e..abc0bd6e1 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "@ianvs/prettier-plugin-sort-imports": "4.2.1", "@types/bun": "1.1.4", "@types/web": "0.0.149", - "clean-package": "2.2.0", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-tailwindcss": "3.17.3", diff --git a/packages/ui/package.json b/packages/ui/package.json index c4dc11e79..40b03b26d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -61,6 +61,7 @@ "@floating-ui/core": "1.6.2", "@floating-ui/react": "0.26.17", "classnames": "2.5.1", + "date-fns": "3.6.0", "debounce": "2.1.0", "flowbite": "2.3.0", "react-icons": "5.2.1", diff --git a/packages/ui/src/components/Datepicker/Datepicker.spec.tsx b/packages/ui/src/components/Datepicker/Datepicker.spec.tsx index 7f3b19e0c..15251a30b 100644 --- a/packages/ui/src/components/Datepicker/Datepicker.spec.tsx +++ b/packages/ui/src/components/Datepicker/Datepicker.spec.tsx @@ -8,7 +8,7 @@ import { getFormattedDate } from "./helpers"; describe("Components / Datepicker", () => { it("should display today's date by default", () => { - const todaysDateInDefaultLanguage = getFormattedDate("en", new Date()); + const todaysDateInDefaultLanguage = getFormattedDate("enUS", new Date(), {}, "dd-MMM-yyyy"); render(); @@ -28,7 +28,7 @@ describe("Components / Datepicker", () => { }); it("should reset to today's date when Clear button is clicked", async () => { - const todaysDateInDefaultLanguage = getFormattedDate("en", new Date()); + const todaysDateInDefaultLanguage = getFormattedDate("enUS", new Date(), {}, "dd-MMM-yyyy"); const todaysDayOfMonth = new Date().getDate(); const anotherDay = todaysDayOfMonth === 1 ? 2 : 1; @@ -43,7 +43,7 @@ describe("Components / Datepicker", () => { }); it("should use today's date when Today button is clicked", async () => { - const todaysDateInDefaultLanguage = getFormattedDate("en", new Date()); + const todaysDateInDefaultLanguage = getFormattedDate("enUS", new Date(), {}, "dd-MMM-yyyy"); const todaysDayOfMonth = new Date().getDate(); const anotherDay = todaysDayOfMonth === 1 ? 2 : 1; @@ -90,7 +90,7 @@ describe("Components / Datepicker", () => { }); it("should clear the value when ref.current.clear is called", async () => { - const todaysDateInDefaultLanguage = getFormattedDate("en", new Date()); + const todaysDateInDefaultLanguage = getFormattedDate("enUS", new Date(), {}, "dd-MMM-yyyy"); const todaysDayOfMonth = new Date().getDate(); const anotherDay = todaysDayOfMonth === 1 ? 2 : 1; @@ -105,4 +105,12 @@ describe("Components / Datepicker", () => { expect(screen.getByDisplayValue(todaysDateInDefaultLanguage)).toBeInTheDocument(); }); + + it("should display today's date in dd-MMM-yyyy format", () => { + const todaysDateInDefaultLanguage = getFormattedDate("enUS", new Date(), {}, "dd-MMM-yyyy"); + + render(); + + expect(screen.getByDisplayValue(todaysDateInDefaultLanguage)).toBeInTheDocument(); + }); }); diff --git a/packages/ui/src/components/Datepicker/Datepicker.stories.tsx b/packages/ui/src/components/Datepicker/Datepicker.stories.tsx index cb5c052e6..f544df87e 100644 --- a/packages/ui/src/components/Datepicker/Datepicker.stories.tsx +++ b/packages/ui/src/components/Datepicker/Datepicker.stories.tsx @@ -56,7 +56,22 @@ Default.args = { defaultDate: new Date(), minDate: undefined, maxDate: undefined, - language: "en", + language: "enUS", weekStart: WeekStart.Sunday, theme: {}, }; + +export const FormattedDate = Template.bind({}); +FormattedDate.args = { + open: false, + autoHide: true, + showClearButton: true, + showTodayButton: true, + defaultDate: new Date(), + minDate: undefined, + maxDate: undefined, + language: "enUS", + weekStart: WeekStart.Sunday, + theme: {}, + inputFormat: "dd MMM yyyy", +}; diff --git a/packages/ui/src/components/Datepicker/Datepicker.tsx b/packages/ui/src/components/Datepicker/Datepicker.tsx index 5600385f7..a8b0c5669 100644 --- a/packages/ui/src/components/Datepicker/Datepicker.tsx +++ b/packages/ui/src/components/Datepicker/Datepicker.tsx @@ -25,6 +25,9 @@ import { DatepickerViewsDecades, type FlowbiteDatepickerViewsDecadesTheme } from import { DatepickerViewsMonth, type FlowbiteDatepickerViewsMonthsTheme } from "./Views/Months"; import { DatepickerViewsYears, type FlowbiteDatepickerViewsYearsTheme } from "./Views/Years"; +// Define a type that represents the available locales +type AvailableLocales = keyof typeof import("date-fns/locale"); + export interface FlowbiteDatepickerTheme { root: { base: string; @@ -93,10 +96,11 @@ export interface DatepickerProps extends Omit { defaultDate?: Date; minDate?: Date; maxDate?: Date; - language?: string; + language?: AvailableLocales; weekStart?: WeekStart; theme?: DeepPartial; onSelectedDateChanged?: (date: Date) => void; + inputFormat?: string; } const DatepickerRender: ForwardRefRenderFunction = ( @@ -112,11 +116,12 @@ const DatepickerRender: ForwardRefRenderFunction defaultDate = new Date(), minDate, maxDate, - language = "en", + language = "enUS", weekStart = WeekStart.Sunday, className, theme: customTheme = {}, onSelectedDateChanged, + inputFormat = "dd-MMM-yyyy", ...props }, ref, @@ -272,7 +277,7 @@ const DatepickerRender: ForwardRefRenderFunction } setIsOpen(true); }} - value={selectedDate && getFormattedDate(language, selectedDate)} + value={selectedDate && getFormattedDate(language, selectedDate, {}, inputFormat)} readOnly {...props} /> diff --git a/packages/ui/src/components/Datepicker/DatepickerContext.tsx b/packages/ui/src/components/Datepicker/DatepickerContext.tsx index 42c4fcbdd..7d77a3020 100644 --- a/packages/ui/src/components/Datepicker/DatepickerContext.tsx +++ b/packages/ui/src/components/Datepicker/DatepickerContext.tsx @@ -4,9 +4,12 @@ import { createContext, useContext } from "react"; import type { FlowbiteDatepickerTheme } from "./Datepicker"; import type { Views, WeekStart } from "./helpers"; +// Define a type that represents the available locales +type AvailableLocales = keyof typeof import("date-fns/locale"); + type DatepickerContextProps = { theme: FlowbiteDatepickerTheme; - language: string; + language: AvailableLocales; weekStart: WeekStart; minDate?: Date; maxDate?: Date; diff --git a/packages/ui/src/components/Datepicker/helpers.spec.tsx b/packages/ui/src/components/Datepicker/helpers.spec.tsx index b7e3d8038..583922199 100644 --- a/packages/ui/src/components/Datepicker/helpers.spec.tsx +++ b/packages/ui/src/components/Datepicker/helpers.spec.tsx @@ -154,21 +154,21 @@ describe("addYears", () => { describe("getFormattedDate", () => { it("returns the formatted date string using the default options", () => { const date = new Date(2023, 0, 15); // January 15th, 2023 - const formattedDate = getFormattedDate("en", date); + const formattedDate = getFormattedDate("enUS", date, {}, "MMMM dd, yyyy"); expect(formattedDate).toBe("January 15, 2023"); }); it("returns the formatted date string using the specified options", () => { const date = new Date(2023, 0, 15); // January 15th, 2023 const options: Intl.DateTimeFormatOptions = { month: "short", year: "numeric" }; - const formattedDate = getFormattedDate("en", date, options); + const formattedDate = getFormattedDate("enUS", date, options, "MMM yyyy"); expect(formattedDate).toBe("Jan 2023"); }); it("returns the formatted date string using the specified language", () => { const date = new Date(2023, 0, 15); // January 15th, 2023 - const formattedDate = getFormattedDate("pt-BR", date); - expect(formattedDate).toBe("15 de janeiro de 2023"); + const formattedDate = getFormattedDate("ptBR", date); + expect(formattedDate).toBe("15-Jan-2023"); }); }); diff --git a/packages/ui/src/components/Datepicker/helpers.ts b/packages/ui/src/components/Datepicker/helpers.ts index d89131762..ed851d9ad 100644 --- a/packages/ui/src/components/Datepicker/helpers.ts +++ b/packages/ui/src/components/Datepicker/helpers.ts @@ -1,3 +1,9 @@ +import { format as DateFNSFormat } from "date-fns"; +import * as DateFNSLocale from "date-fns/locale"; + +// Define a type that represents the available locales +type AvailableLocales = keyof typeof import("date-fns/locale"); + export enum Views { Days = 0, Months = 1, @@ -99,7 +105,12 @@ export const addYears = (date: Date, amount: number): Date => { return newDate; }; -export const getFormattedDate = (language: string, date: Date, options?: Intl.DateTimeFormatOptions): string => { +export const getFormattedDate = ( + language: AvailableLocales = "enUS", + date: Date, + options?: Intl.DateTimeFormatOptions, + dateFormat: string = "dd-MMM-yyyy", +): string => { let defaultOptions: Intl.DateTimeFormatOptions = { day: "numeric", month: "long", @@ -109,8 +120,12 @@ export const getFormattedDate = (language: string, date: Date, options?: Intl.Da if (options) { defaultOptions = options; } + console.log(defaultOptions); + + const getLocale = + language === "enUS" ? DateFNSLocale["enUS"] : Object.values(DateFNSLocale).find((l) => l.code === language); - return new Intl.DateTimeFormat(language, defaultOptions).format(date); + return DateFNSFormat(date, dateFormat, { locale: getLocale }); }; export const startOfYearPeriod = (date: Date, years: number): number => {