Skip to content
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

fix(datepicker): datePicker now supports formatting of date #1314

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 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
12 changes: 12 additions & 0 deletions apps/web/content/docs/components/datepicker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,24 @@ Use this example to show a simple datepicker component.

<Example name="datepicker.root" />

## 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.

<Example name="datepicker.format" />

## 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`.

<Example name="datepicker.localization" />

## Limit the date
Expand Down
42 changes: 42 additions & 0 deletions apps/web/examples/datepicker/datepicker.format.tsx
Original file line number Diff line number Diff line change
@@ -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 <Datepicker inputFormat="dd-MMM-yyyy" />;
}
`;

const codeRSC = `
import { Datepicker } from "flowbite-react";

function Component() {
return <Datepicker inputFormat="dd-MMM-yyyy" />;
}
`;

function Component() {
return <Datepicker inputFormat="dd-MMM-yyyy" />;
}

export const format: CodeData = {
type: "single",
code: [
{
fileName: "client",
language: "tsx",
code,
},
{
fileName: "server",
language: "tsx",
code: codeRSC,
},
],
githubSlug: "datepicker/datepicker.format.tsx",
component: <Component />,
};
12 changes: 6 additions & 6 deletions apps/web/examples/datepicker/datepicker.localization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ const code = `

import { Datepicker } from "flowbite-react";

export function Component() {
return <Datepicker language="pt-BR" labelTodayButton="Hoje" labelClearButton="Limpar" />;
function Component() {
return <Datepicker language="ptBR" labelTodayButton="Hoje" labelClearButton="Limpar" />;
}
`;

const codeRSC = `
import { Datepicker } from "flowbite-react";

export function Component() {
return <Datepicker language="pt-BR" labelTodayButton="Hoje" labelClearButton="Limpar" />;
function Component() {
return <Datepicker language="ptBR" labelTodayButton="Hoje" labelClearButton="Limpar" />;
}
`;

export function Component() {
return <Datepicker language="pt-BR" labelTodayButton="Hoje" labelClearButton="Limpar" />;
function Component() {
return <Datepicker language="ptBR" labelTodayButton="Hoje" labelClearButton="Limpar" />;
}

export const localization: CodeData = {
Expand Down
1 change: 1 addition & 0 deletions apps/web/examples/datepicker/index.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"dev": "bun run build --watch",
"format": "prettier . --write",
"format:check": "prettier . --check",
"postinstall": "bun run build",
dhavalveera marked this conversation as resolved.
Show resolved Hide resolved
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"prepack": "clean-package",
Expand Down Expand Up @@ -76,6 +77,8 @@
"@typescript-eslint/parser": "7.4.0",
"@vitejs/plugin-react": "4.2.1",
"@vitest/coverage-v8": "1.4.0",
"clean-package": "2.2.0",
dhavalveera marked this conversation as resolved.
Show resolved Hide resolved
"date-fns": "3.6.0",
"eslint-plugin-react": "7.34.1",
"eslint-plugin-storybook": "0.8.0",
"eslint-plugin-vitest": "0.4.1",
Expand Down
16 changes: 12 additions & 4 deletions packages/ui/src/components/Datepicker/Datepicker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<Datepicker />);

Expand All @@ -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;

Expand All @@ -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;

Expand Down Expand Up @@ -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;

Expand All @@ -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(<Datepicker inputFormat="dd-MMM-yyyy" />);

expect(screen.getByDisplayValue(todaysDateInDefaultLanguage)).toBeInTheDocument();
});
});
17 changes: 16 additions & 1 deletion packages/ui/src/components/Datepicker/Datepicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
};
11 changes: 8 additions & 3 deletions packages/ui/src/components/Datepicker/Datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -93,10 +96,11 @@ export interface DatepickerProps extends Omit<TextInputProps, "theme"> {
defaultDate?: Date;
minDate?: Date;
maxDate?: Date;
language?: string;
language?: AvailableLocales;
weekStart?: WeekStart;
theme?: DeepPartial<FlowbiteDatepickerTheme>;
onSelectedDateChanged?: (date: Date) => void;
inputFormat?: string;
}

const DatepickerRender: ForwardRefRenderFunction<DatepickerRef, DatepickerProps> = (
Expand All @@ -112,11 +116,12 @@ const DatepickerRender: ForwardRefRenderFunction<DatepickerRef, DatepickerProps>
defaultDate = new Date(),
minDate,
maxDate,
language = "en",
language = "enUS",
weekStart = WeekStart.Sunday,
className,
theme: customTheme = {},
onSelectedDateChanged,
inputFormat = "dd-MMM-yyyy",
...props
},
ref,
Expand Down Expand Up @@ -272,7 +277,7 @@ const DatepickerRender: ForwardRefRenderFunction<DatepickerRef, DatepickerProps>
}
setIsOpen(true);
}}
value={selectedDate && getFormattedDate(language, selectedDate)}
value={selectedDate && getFormattedDate(language, selectedDate, {}, inputFormat)}
readOnly
{...props}
/>
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/src/components/Datepicker/DatepickerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions packages/ui/src/components/Datepicker/helpers.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
dhavalveera marked this conversation as resolved.
Show resolved Hide resolved
expect(formattedDate).toBe("15-Jan-2023");
});
});

Expand Down
20 changes: 18 additions & 2 deletions packages/ui/src/components/Datepicker/helpers.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -72,7 +78,7 @@
const date = new Date(0);
date.setDate(date.getDate() - date.getDay() + weekStart);

const formatter = new Intl.DateTimeFormat(lang, { weekday: "short" });

Check failure on line 81 in packages/ui/src/components/Datepicker/helpers.ts

View workflow job for this annotation

GitHub Actions / 🔬 Test

src/components/Datepicker/Datepicker.spec.tsx > Components / Datepicker > should update date when a different day is clicked

RangeError: Incorrect locale information provided ❯ Module.getWeekDays src/components/Datepicker/helpers.ts:81:21 ❯ DatepickerViewsDays src/components/Datepicker/Views/Days.tsx:41:20 ❯ renderWithHooks ../../node_modules/react-dom/cjs/react-dom.development.js:16305:18 ❯ mountIndeterminateComponent ../../node_modules/react-dom/cjs/react-dom.development.js:20074:13 ❯ beginWork ../../node_modules/react-dom/cjs/react-dom.development.js:21587:16 ❯ beginWork$1 ../../node_modules/react-dom/cjs/react-dom.development.js:27426:14 ❯ performUnitOfWork ../../node_modules/react-dom/cjs/react-dom.development.js:26560:12 ❯ workLoopSync ../../node_modules/react-dom/cjs/react-dom.development.js:26466:5 ❯ renderRootSync ../../node_modules/react-dom/cjs/react-dom.development.js:26434:7

Check failure on line 81 in packages/ui/src/components/Datepicker/helpers.ts

View workflow job for this annotation

GitHub Actions / 🔬 Test

src/components/Datepicker/Datepicker.spec.tsx > Components / Datepicker > should reset to today's date when Clear button is clicked

RangeError: Incorrect locale information provided ❯ Module.getWeekDays src/components/Datepicker/helpers.ts:81:21 ❯ DatepickerViewsDays src/components/Datepicker/Views/Days.tsx:41:20 ❯ renderWithHooks ../../node_modules/react-dom/cjs/react-dom.development.js:16305:18 ❯ mountIndeterminateComponent ../../node_modules/react-dom/cjs/react-dom.development.js:20074:13 ❯ beginWork ../../node_modules/react-dom/cjs/react-dom.development.js:21587:16 ❯ beginWork$1 ../../node_modules/react-dom/cjs/react-dom.development.js:27426:14 ❯ performUnitOfWork ../../node_modules/react-dom/cjs/react-dom.development.js:26560:12 ❯ workLoopSync ../../node_modules/react-dom/cjs/react-dom.development.js:26466:5 ❯ renderRootSync ../../node_modules/react-dom/cjs/react-dom.development.js:26434:7

Check failure on line 81 in packages/ui/src/components/Datepicker/helpers.ts

View workflow job for this annotation

GitHub Actions / 🔬 Test

src/components/Datepicker/Datepicker.spec.tsx > Components / Datepicker > should use today's date when Today button is clicked

RangeError: Incorrect locale information provided ❯ Module.getWeekDays src/components/Datepicker/helpers.ts:81:21 ❯ DatepickerViewsDays src/components/Datepicker/Views/Days.tsx:41:20 ❯ renderWithHooks ../../node_modules/react-dom/cjs/react-dom.development.js:16305:18 ❯ mountIndeterminateComponent ../../node_modules/react-dom/cjs/react-dom.development.js:20074:13 ❯ beginWork ../../node_modules/react-dom/cjs/react-dom.development.js:21587:16 ❯ beginWork$1 ../../node_modules/react-dom/cjs/react-dom.development.js:27426:14 ❯ performUnitOfWork ../../node_modules/react-dom/cjs/react-dom.development.js:26560:12 ❯ workLoopSync ../../node_modules/react-dom/cjs/react-dom.development.js:26466:5 ❯ renderRootSync ../../node_modules/react-dom/cjs/react-dom.development.js:26434:7

Check failure on line 81 in packages/ui/src/components/Datepicker/helpers.ts

View workflow job for this annotation

GitHub Actions / 🔬 Test

src/components/Datepicker/Datepicker.spec.tsx > Components / Datepicker > should call `onSelectedDateChange` when a new date is selected

RangeError: Incorrect locale information provided ❯ Module.getWeekDays src/components/Datepicker/helpers.ts:81:21 ❯ DatepickerViewsDays src/components/Datepicker/Views/Days.tsx:41:20 ❯ renderWithHooks ../../node_modules/react-dom/cjs/react-dom.development.js:16305:18 ❯ mountIndeterminateComponent ../../node_modules/react-dom/cjs/react-dom.development.js:20074:13 ❯ beginWork ../../node_modules/react-dom/cjs/react-dom.development.js:21587:16 ❯ beginWork$1 ../../node_modules/react-dom/cjs/react-dom.development.js:27426:14 ❯ performUnitOfWork ../../node_modules/react-dom/cjs/react-dom.development.js:26560:12 ❯ workLoopSync ../../node_modules/react-dom/cjs/react-dom.development.js:26466:5 ❯ renderRootSync ../../node_modules/react-dom/cjs/react-dom.development.js:26434:7

Check failure on line 81 in packages/ui/src/components/Datepicker/helpers.ts

View workflow job for this annotation

GitHub Actions / 🔬 Test

src/components/Datepicker/Datepicker.spec.tsx > Components / Datepicker > should focus the input when ref.current.focus is called

RangeError: Incorrect locale information provided ❯ Module.getWeekDays src/components/Datepicker/helpers.ts:81:21 ❯ DatepickerViewsDays src/components/Datepicker/Views/Days.tsx:41:20 ❯ renderWithHooks ../../node_modules/react-dom/cjs/react-dom.development.js:16305:18 ❯ mountIndeterminateComponent ../../node_modules/react-dom/cjs/react-dom.development.js:20074:13 ❯ beginWork ../../node_modules/react-dom/cjs/react-dom.development.js:21587:16 ❯ beginWork$1 ../../node_modules/react-dom/cjs/react-dom.development.js:27426:14 ❯ performUnitOfWork ../../node_modules/react-dom/cjs/react-dom.development.js:26560:12 ❯ workLoopSync ../../node_modules/react-dom/cjs/react-dom.development.js:26466:5 ❯ renderRootSync ../../node_modules/react-dom/cjs/react-dom.development.js:26434:7

Check failure on line 81 in packages/ui/src/components/Datepicker/helpers.ts

View workflow job for this annotation

GitHub Actions / 🔬 Test

src/components/Datepicker/Datepicker.spec.tsx > Components / Datepicker > should clear the value when ref.current.clear is called

RangeError: Incorrect locale information provided ❯ Module.getWeekDays src/components/Datepicker/helpers.ts:81:21 ❯ DatepickerViewsDays src/components/Datepicker/Views/Days.tsx:41:20 ❯ renderWithHooks ../../node_modules/react-dom/cjs/react-dom.development.js:16305:18 ❯ mountIndeterminateComponent ../../node_modules/react-dom/cjs/react-dom.development.js:20074:13 ❯ beginWork ../../node_modules/react-dom/cjs/react-dom.development.js:21587:16 ❯ beginWork$1 ../../node_modules/react-dom/cjs/react-dom.development.js:27426:14 ❯ performUnitOfWork ../../node_modules/react-dom/cjs/react-dom.development.js:26560:12 ❯ workLoopSync ../../node_modules/react-dom/cjs/react-dom.development.js:26466:5 ❯ renderRootSync ../../node_modules/react-dom/cjs/react-dom.development.js:26434:7

for (let i = 0; i < 7; i++) {
weekdays.push(formatter.format(addDays(date, i)));
Expand All @@ -99,7 +105,12 @@
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",
Expand All @@ -109,8 +120,13 @@
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 => {
Expand Down
Loading