From a84e5a6ced7bb77c770e4c1f2e736d157b426035 Mon Sep 17 00:00:00 2001 From: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:59:32 +0200 Subject: [PATCH] fix(ui,dashboard): Revamp DatePicker component (#7891) **What** - Revamps the DatePicker component. - Addresses all issues with broken DatePickers across admin. **Note** - Part of this PR is adding a I18nProvider which is used to set the locale that is used for our DatePicker and Calendar components. Per default they use the browser locale. In the current implementation, we are grabbing the locale to use from the language that is picked in the "Profile" section. This means that currently the only possible locale is "en-US", meaning times uses AM/PM. This is likely not what we want, but we need to make a decision on how we want to handle this globally, will create a ticket for it and we can then clean it up later on. - This PR does not include "presets" or a DateRange picker that were part of the old implementation. Will open tickets to re-add this later on, but since we aren't using it in admin any where it makes sense to address later. - This PR also bumps and pin every `@radix-ui` dependency in `@medusajs/ui` and `@medusajs/dashboard`. Our different versions were pulling in multiple versions of internal radix dependencies which were breaking Popover and Dialog behaviour across admin. One thing to note is that Radix have started to print warnings for missing Descriptions and Titles in dialogs. We should add these as we go, for better accessibility. Its not an urgent task but something we can add as we clean up admin over the following weeks. CLOSES CORE-2382 --- packages/admin-next/dashboard/package.json | 6 +- packages/admin-next/dashboard/src/app.tsx | 5 +- .../date-range-display/date-range-display.tsx | 4 +- .../src/components/common/form/form.tsx | 14 +- .../localized-date-picker/index.ts | 1 - .../localized-date-picker.tsx | 37 - .../modals/stacked-focus-modal/index.ts | 2 +- ...oucs-modal.tsx => stacked-focus-modal.tsx} | 0 .../data-table-filter/date-filter.tsx | 11 +- .../dashboard/src/hooks/use-date.tsx | 2 + .../providers/i18n-provider/i18n-provider.tsx | 16 + .../src/providers/i18n-provider/index.ts | 1 + .../edit-campaign-form/edit-campaign-form.tsx | 22 +- .../create-campaign-form-fields.tsx | 16 +- .../price-list-configuration-form.tsx | 16 +- .../price-list-create-form.tsx | 1 - .../price-list-details-form.tsx | 13 +- .../price-list-detail/price-list-detail.tsx | 4 +- packages/design-system/ui/package.json | 41 +- .../components/calendar/calendar.stories.tsx | 79 - .../ui/src/components/calendar/calendar.tsx | 174 -- .../ui/src/components/calendar/index.ts | 1 - .../calender/_internal-calendar.tsx | 53 + .../components/calender/calendar-button.tsx | 29 + .../src/components/calender/calendar-cell.tsx | 75 + .../src/components/calender/calendar-grid.tsx | 46 + .../components/calender/calendar.stories.tsx | 61 + .../ui/src/components/calender/calendar.tsx | 110 + .../ui/src/components/calender/index.ts | 2 + .../date-picker/date-picker-button.tsx | 42 + .../date-picker/date-picker-clear-button.tsx | 40 + .../date-picker/date-picker-field.tsx | 55 + .../date-picker/date-picker.spec.tsx | 58 - .../date-picker/date-picker.stories.tsx | 308 +-- .../components/date-picker/date-picker.tsx | 1229 ++------- .../components/date-segment/date-segment.tsx | 54 + .../ui/src/components/date-segment/index.ts | 1 + .../i18n-provider/i18n-provider.tsx | 12 + .../ui/src/components/i18n-provider/index.ts | 1 + .../ui/src/components/popover/popover.tsx | 3 +- .../ui/src/components/prompt/prompt.tsx | 4 +- .../time-input/time-input.stories.tsx | 56 - .../src/components/time-input/time-input.tsx | 125 +- packages/design-system/ui/src/index.ts | 3 +- packages/design-system/ui/src/types.ts | 2 + .../design-system/ui/src/utils/calendar.ts | 138 + yarn.lock | 2325 +++++++++++++---- 47 files changed, 2874 insertions(+), 2424 deletions(-) delete mode 100644 packages/admin-next/dashboard/src/components/localization/localized-date-picker/index.ts delete mode 100644 packages/admin-next/dashboard/src/components/localization/localized-date-picker/localized-date-picker.tsx rename packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/{stacked-foucs-modal.tsx => stacked-focus-modal.tsx} (100%) create mode 100644 packages/admin-next/dashboard/src/providers/i18n-provider/i18n-provider.tsx create mode 100644 packages/admin-next/dashboard/src/providers/i18n-provider/index.ts delete mode 100644 packages/design-system/ui/src/components/calendar/calendar.stories.tsx delete mode 100644 packages/design-system/ui/src/components/calendar/calendar.tsx delete mode 100644 packages/design-system/ui/src/components/calendar/index.ts create mode 100644 packages/design-system/ui/src/components/calender/_internal-calendar.tsx create mode 100644 packages/design-system/ui/src/components/calender/calendar-button.tsx create mode 100644 packages/design-system/ui/src/components/calender/calendar-cell.tsx create mode 100644 packages/design-system/ui/src/components/calender/calendar-grid.tsx create mode 100644 packages/design-system/ui/src/components/calender/calendar.stories.tsx create mode 100644 packages/design-system/ui/src/components/calender/calendar.tsx create mode 100644 packages/design-system/ui/src/components/calender/index.ts create mode 100644 packages/design-system/ui/src/components/date-picker/date-picker-button.tsx create mode 100644 packages/design-system/ui/src/components/date-picker/date-picker-clear-button.tsx create mode 100644 packages/design-system/ui/src/components/date-picker/date-picker-field.tsx delete mode 100644 packages/design-system/ui/src/components/date-picker/date-picker.spec.tsx create mode 100644 packages/design-system/ui/src/components/date-segment/date-segment.tsx create mode 100644 packages/design-system/ui/src/components/date-segment/index.ts create mode 100644 packages/design-system/ui/src/components/i18n-provider/i18n-provider.tsx create mode 100644 packages/design-system/ui/src/components/i18n-provider/index.ts delete mode 100644 packages/design-system/ui/src/components/time-input/time-input.stories.tsx create mode 100644 packages/design-system/ui/src/utils/calendar.ts diff --git a/packages/admin-next/dashboard/package.json b/packages/admin-next/dashboard/package.json index 5d6cb4ffb8..477473f7fa 100644 --- a/packages/admin-next/dashboard/package.json +++ b/packages/admin-next/dashboard/package.json @@ -35,14 +35,14 @@ "@medusajs/icons": "1.2.1", "@medusajs/js-sdk": "0.0.1", "@medusajs/ui": "3.0.0", - "@radix-ui/react-collapsible": "1.0.3", - "@radix-ui/react-hover-card": "^1.0.7", + "@radix-ui/react-collapsible": "1.1.0", + "@radix-ui/react-hover-card": "1.1.1", "@tanstack/react-query": "^5.28.14", "@tanstack/react-table": "8.10.7", "@tanstack/react-virtual": "^3.0.4", "@uiw/react-json-view": "^2.0.0-alpha.17", "cmdk": "^0.2.0", - "date-fns": "^3.2.0", + "date-fns": "^3.6.0", "framer-motion": "^11.0.3", "i18next": "23.7.11", "i18next-browser-languagedetector": "7.2.0", diff --git a/packages/admin-next/dashboard/src/app.tsx b/packages/admin-next/dashboard/src/app.tsx index def2787f4b..e1a0ffd4ac 100644 --- a/packages/admin-next/dashboard/src/app.tsx +++ b/packages/admin-next/dashboard/src/app.tsx @@ -3,6 +3,7 @@ import { QueryClientProvider } from "@tanstack/react-query" import { I18n } from "./components/utilities/i18n" import { queryClient } from "./lib/query-client" +import { I18nProvider } from "./providers/i18n-provider" import { RouterProvider } from "./providers/router-provider" import { ThemeProvider } from "./providers/theme-provider" @@ -14,7 +15,9 @@ function App() { - + + + diff --git a/packages/admin-next/dashboard/src/components/common/date-range-display/date-range-display.tsx b/packages/admin-next/dashboard/src/components/common/date-range-display/date-range-display.tsx index 8243e41b54..738c9d9f5e 100644 --- a/packages/admin-next/dashboard/src/components/common/date-range-display/date-range-display.tsx +++ b/packages/admin-next/dashboard/src/components/common/date-range-display/date-range-display.tsx @@ -27,7 +27,7 @@ export const DateRangeDisplay = ({ {t("fields.startDate")} - + {startDate ? getFullDate({ date: startDate, @@ -44,7 +44,7 @@ export const DateRangeDisplay = ({ {t("fields.endDate")} - + {endDate ? getFullDate({ date: endDate, diff --git a/packages/admin-next/dashboard/src/components/common/form/form.tsx b/packages/admin-next/dashboard/src/components/common/form/form.tsx index 9799136d8f..877946d59f 100644 --- a/packages/admin-next/dashboard/src/components/common/form/form.tsx +++ b/packages/admin-next/dashboard/src/components/common/form/form.tsx @@ -78,6 +78,7 @@ const useFormField = () => { id, name: fieldContext.name, formItemId: `${id}-form-item`, + formLabelId: `${id}-form-item-label`, formDescriptionId: `${id}-form-item-description`, formErrorMessageId: `${id}-form-item-message`, ...fieldState, @@ -109,12 +110,13 @@ const Label = forwardRef< icon?: ReactNode } >(({ className, optional = false, tooltip, icon, ...props }, ref) => { - const { formItemId } = useFormField() + const { formLabelId, formItemId } = useFormField() const { t } = useTranslation() return (
, React.ComponentPropsWithoutRef >(({ ...props }, ref) => { - const { error, formItemId, formDescriptionId, formErrorMessageId } = - useFormField() + const { + error, + formItemId, + formDescriptionId, + formErrorMessageId, + formLabelId, + } = useFormField() return ( ) diff --git a/packages/admin-next/dashboard/src/components/localization/localized-date-picker/index.ts b/packages/admin-next/dashboard/src/components/localization/localized-date-picker/index.ts deleted file mode 100644 index 3b64d8f60f..0000000000 --- a/packages/admin-next/dashboard/src/components/localization/localized-date-picker/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./localized-date-picker" diff --git a/packages/admin-next/dashboard/src/components/localization/localized-date-picker/localized-date-picker.tsx b/packages/admin-next/dashboard/src/components/localization/localized-date-picker/localized-date-picker.tsx deleted file mode 100644 index b84f047a11..0000000000 --- a/packages/admin-next/dashboard/src/components/localization/localized-date-picker/localized-date-picker.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { DatePicker } from "@medusajs/ui" -import { ComponentPropsWithoutRef } from "react" -import { useTranslation } from "react-i18next" -import { languages } from "../../../i18n/languages" - -type LocalizedDatePickerProps = Omit< - ComponentPropsWithoutRef, - "translations" | "locale" -> - -export const LocalizedDatePicker = ({ - mode = "single", - ...props -}: LocalizedDatePickerProps) => { - const { i18n, t } = useTranslation() - - const locale = languages.find( - (lang) => lang.code === i18n.language - )?.date_locale - - const translations = { - cancel: t("actions.cancel"), - apply: t("general.apply"), - end: t("general.end"), - start: t("general.start"), - range: t("general.range"), - } - - return ( - - ) -} diff --git a/packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/index.ts b/packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/index.ts index e130e6356a..e1c5081749 100644 --- a/packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/index.ts +++ b/packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/index.ts @@ -1 +1 @@ -export * from "./stacked-foucs-modal" +export * from "./stacked-focus-modal" diff --git a/packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/stacked-foucs-modal.tsx b/packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/stacked-focus-modal.tsx similarity index 100% rename from packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/stacked-foucs-modal.tsx rename to packages/admin-next/dashboard/src/components/modals/stacked-focus-modal/stacked-focus-modal.tsx diff --git a/packages/admin-next/dashboard/src/components/table/data-table/data-table-filter/date-filter.tsx b/packages/admin-next/dashboard/src/components/table/data-table/data-table-filter/date-filter.tsx index 255285ddff..5533633cea 100644 --- a/packages/admin-next/dashboard/src/components/table/data-table/data-table-filter/date-filter.tsx +++ b/packages/admin-next/dashboard/src/components/table/data-table/data-table-filter/date-filter.tsx @@ -65,10 +65,7 @@ export const DateFilter = ({ const customStartValue = getDateFromComparison(currentDateComparison, "$gte") const customEndValue = getDateFromComparison(currentDateComparison, "$lte") - const handleCustomDateChange = ( - value: Date | undefined, - pos: "start" | "end" - ) => { + const handleCustomDateChange = (value: Date | null, pos: "start" | "end") => { const key = pos === "start" ? "$gte" : "$lte" const dateValue = value ? value.toISOString() : undefined @@ -201,8 +198,7 @@ export const DateFilter = ({
handleCustomDateChange(d, "start")} /> @@ -216,8 +212,7 @@ export const DateFilter = ({
{ handleCustomDateChange(d, "end") diff --git a/packages/admin-next/dashboard/src/hooks/use-date.tsx b/packages/admin-next/dashboard/src/hooks/use-date.tsx index af8769f712..15e40541f5 100644 --- a/packages/admin-next/dashboard/src/hooks/use-date.tsx +++ b/packages/admin-next/dashboard/src/hooks/use-date.tsx @@ -4,6 +4,8 @@ import { useTranslation } from "react-i18next" import { languages } from "../i18n/languages" +// TODO: We rely on the current language to determine the date locale. This is not ideal, as we use en-US for the english translation. +// We either need to also have an en-GB translation or we need to separate the date locale from the translation language. export const useDate = () => { const { i18n } = useTranslation() diff --git a/packages/admin-next/dashboard/src/providers/i18n-provider/i18n-provider.tsx b/packages/admin-next/dashboard/src/providers/i18n-provider/i18n-provider.tsx new file mode 100644 index 0000000000..6da3118eb8 --- /dev/null +++ b/packages/admin-next/dashboard/src/providers/i18n-provider/i18n-provider.tsx @@ -0,0 +1,16 @@ +import { I18nProvider as Provider } from "@medusajs/ui" +import { PropsWithChildren } from "react" +import { useTranslation } from "react-i18next" +import { languages } from "../../i18n/languages" + +type I18nProviderProps = PropsWithChildren + +export const I18nProvider = ({ children }: I18nProviderProps) => { + const { i18n } = useTranslation() + + const locale = + languages.find((lan) => lan.code === i18n.language)?.code || + languages[0].code + + return {children} +} diff --git a/packages/admin-next/dashboard/src/providers/i18n-provider/index.ts b/packages/admin-next/dashboard/src/providers/i18n-provider/index.ts new file mode 100644 index 0000000000..ed399e5acf --- /dev/null +++ b/packages/admin-next/dashboard/src/providers/i18n-provider/index.ts @@ -0,0 +1 @@ +export * from "./i18n-provider" diff --git a/packages/admin-next/dashboard/src/routes/campaigns/campaign-edit/components/edit-campaign-form/edit-campaign-form.tsx b/packages/admin-next/dashboard/src/routes/campaigns/campaign-edit/components/edit-campaign-form/edit-campaign-form.tsx index 6482bd74b9..9eb49155b6 100644 --- a/packages/admin-next/dashboard/src/routes/campaigns/campaign-edit/components/edit-campaign-form/edit-campaign-form.tsx +++ b/packages/admin-next/dashboard/src/routes/campaigns/campaign-edit/components/edit-campaign-form/edit-campaign-form.tsx @@ -5,10 +5,7 @@ import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" import { Form } from "../../../../../components/common/form" -import { - RouteDrawer, - useRouteModal, -} from "../../../../../components/modals" +import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { useUpdateCampaign } from "../../../../../hooks/api/campaigns" type EditCampaignFormProps = { @@ -133,18 +130,16 @@ export const EditCampaignForm = ({ campaign }: EditCampaignFormProps) => { { + render={({ field }) => { return ( {t("campaigns.fields.start_date")} { - onChange(v ?? null) - }} + granularity="minute" + hourCycle={12} + shouldCloseOnSelect={false} {...field} /> @@ -158,16 +153,15 @@ export const EditCampaignForm = ({ campaign }: EditCampaignFormProps) => { { + render={({ field }) => { return ( {t("campaigns.fields.end_date")} onChange(v ?? null)} + granularity="minute" + shouldCloseOnSelect={false} {...field} /> diff --git a/packages/admin-next/dashboard/src/routes/campaigns/common/components/create-campaign-form-fields/create-campaign-form-fields.tsx b/packages/admin-next/dashboard/src/routes/campaigns/common/components/create-campaign-form-fields/create-campaign-form-fields.tsx index ceadd2b5a5..1403aa55ad 100644 --- a/packages/admin-next/dashboard/src/routes/campaigns/common/components/create-campaign-form-fields/create-campaign-form-fields.tsx +++ b/packages/admin-next/dashboard/src/routes/campaigns/common/components/create-campaign-form-fields/create-campaign-form-fields.tsx @@ -138,18 +138,15 @@ export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => { { + render={({ field }) => { return ( {t("campaigns.fields.start_date")} { - onChange(v ?? null) - }} + granularity="minute" + shouldCloseOnSelect={false} {...field} /> @@ -163,16 +160,15 @@ export const CreateCampaignFormFields = ({ form, fieldScope = "" }) => { { + render={({ field }) => { return ( {t("campaigns.fields.end_date")} onChange(v ?? null)} + granularity="minute" + shouldCloseOnSelect={false} {...field} /> diff --git a/packages/admin-next/dashboard/src/routes/price-lists/price-list-configuration/components/price-list-configuration-form/price-list-configuration-form.tsx b/packages/admin-next/dashboard/src/routes/price-lists/price-list-configuration/components/price-list-configuration-form/price-list-configuration-form.tsx index f26d34a69f..1ef624c33b 100644 --- a/packages/admin-next/dashboard/src/routes/price-lists/price-list-configuration/components/price-list-configuration-form/price-list-configuration-form.tsx +++ b/packages/admin-next/dashboard/src/routes/price-lists/price-list-configuration/components/price-list-configuration-form/price-list-configuration-form.tsx @@ -41,7 +41,6 @@ const PriceListConfigurationSchema = z.object({ const STACKED_MODAL_ID = "cg" -// TODO: Fix DatePickers once new version is merged. export const PriceListConfigurationForm = ({ priceList, customerGroups, @@ -122,7 +121,7 @@ export const PriceListConfigurationForm = ({ { + render={({ field }) => { return (
@@ -135,12 +134,10 @@ export const PriceListConfigurationForm = ({
- {/* TODO: Add timepicker see CORE-2382 */} onChange(value ?? null)} - value={value ?? undefined} />
@@ -153,7 +150,7 @@ export const PriceListConfigurationForm = ({ { + render={({ field }) => { return (
@@ -167,10 +164,9 @@ export const PriceListConfigurationForm = ({
onChange(value ?? null)} - value={value ?? undefined} /> diff --git a/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-create-form.tsx b/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-create-form.tsx index 2476028c72..82abdb79aa 100644 --- a/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-create-form.tsx +++ b/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-create-form.tsx @@ -46,7 +46,6 @@ type PriceListCreateFormProps = { currencies: HttpTypes.AdminStoreCurrency[] } -// TODO: Fix DatePickers once new version is merged. export const PriceListCreateForm = ({ regions, currencies, diff --git a/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-details-form.tsx b/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-details-form.tsx index 63631690de..17c9ec3d41 100644 --- a/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-details-form.tsx +++ b/packages/admin-next/dashboard/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-details-form.tsx @@ -173,7 +173,7 @@ export const PriceListDetailsForm = ({ form }: PriceListDetailsFormProps) => { { + render={({ field }) => { return (
@@ -186,11 +186,10 @@ export const PriceListDetailsForm = ({ form }: PriceListDetailsFormProps) => {
- {/* TODO: Add timepicker see CORE-2382 */} @@ -203,7 +202,7 @@ export const PriceListDetailsForm = ({ form }: PriceListDetailsFormProps) => { { + render={({ field }) => { return (
@@ -215,9 +214,9 @@ export const PriceListDetailsForm = ({ form }: PriceListDetailsFormProps) => {
diff --git a/packages/admin-next/dashboard/src/routes/price-lists/price-list-detail/price-list-detail.tsx b/packages/admin-next/dashboard/src/routes/price-lists/price-list-detail/price-list-detail.tsx index af0ae3a37e..d6e4a0da91 100644 --- a/packages/admin-next/dashboard/src/routes/price-lists/price-list-detail/price-list-detail.tsx +++ b/packages/admin-next/dashboard/src/routes/price-lists/price-list-detail/price-list-detail.tsx @@ -34,7 +34,7 @@ export const PriceListDetails = () => { ) })}
-
+
{after.widgets.map((w, i) => { @@ -48,7 +48,7 @@ export const PriceListDetails = () => {
-
+
{sideBefore.widgets.map((w, i) => { return (
diff --git a/packages/design-system/ui/package.json b/packages/design-system/ui/package.json index fb7f3c0897..208818c76b 100644 --- a/packages/design-system/ui/package.json +++ b/packages/design-system/ui/package.json @@ -81,34 +81,35 @@ }, "dependencies": { "@medusajs/icons": "^1.2.1", - "@radix-ui/react-accordion": "^1.1.2", - "@radix-ui/react-alert-dialog": "1.0.4", - "@radix-ui/react-avatar": "^1.0.3", - "@radix-ui/react-checkbox": "^1.0.4", - "@radix-ui/react-dialog": "1.0.4", - "@radix-ui/react-dropdown-menu": "^2.0.5", - "@radix-ui/react-label": "^2.0.2", - "@radix-ui/react-popover": "^1.0.6", - "@radix-ui/react-portal": "^1.0.3", - "@radix-ui/react-radio-group": "^1.1.3", - "@radix-ui/react-scroll-area": "^1.0.4", - "@radix-ui/react-select": "^2.0.0", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-switch": "^1.0.3", - "@radix-ui/react-tabs": "^1.0.4", - "@radix-ui/react-tooltip": "^1.0.6", - "@react-aria/datepicker": "^3.5.0", - "@react-stately/datepicker": "^3.5.0", + "@radix-ui/react-accordion": "1.2.0", + "@radix-ui/react-alert-dialog": "1.1.1", + "@radix-ui/react-avatar": "1.1.0", + "@radix-ui/react-checkbox": "1.1.1", + "@radix-ui/react-dialog": "1.1.1", + "@radix-ui/react-dropdown-menu": "2.1.1", + "@radix-ui/react-label": "2.1.0", + "@radix-ui/react-popover": "1.1.1", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-radio-group": "1.2.0", + "@radix-ui/react-scroll-area": "1.1.0", + "@radix-ui/react-select": "2.1.1", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-switch": "1.1.0", + "@radix-ui/react-tabs": "1.1.0", + "@radix-ui/react-tooltip": "1.1.2", "clsx": "^1.2.1", "copy-to-clipboard": "^3.3.3", "cva": "1.0.0-beta.1", "date-fns": "^2.30.0", "prism-react-renderer": "^2.0.6", "prismjs": "^1.29.0", + "react-aria": "^3.33.1", "react-currency-input-field": "^3.6.11", - "react-day-picker": "^8.8.0", + "react-stately": "^3.31.1", "sonner": "^1.4.41", - "tailwind-merge": "^2.2.1" + "tailwind-merge": "^2.2.1", + "upgrade": "^1.1.0", + "yarn": "^1.22.22" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/design-system/ui/src/components/calendar/calendar.stories.tsx b/packages/design-system/ui/src/components/calendar/calendar.stories.tsx deleted file mode 100644 index ed24ea624d..0000000000 --- a/packages/design-system/ui/src/components/calendar/calendar.stories.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react" -import * as React from "react" - -import { Text } from "@/components/text" -import { DateRange } from "react-day-picker" -import { Calendar } from "./calendar" - -const Demo = ({ mode, ...args }: Parameters[0]) => { - const [date, setDate] = React.useState(new Date()) - const [dateRange, setDateRange] = React.useState( - undefined - ) - - return ( -
- - - {mode === "single" && ( - - Selected Date: {date ? date.toDateString() : "None"} - - )} - {mode === "range" && ( - - Selected Range:{" "} - {dateRange - ? `${dateRange.from?.toDateString()} – ${ - dateRange.to?.toDateString() ?? "" - }` - : "None"} - - )} -
- ) -} - -const meta: Meta = { - title: "Components/Calendar", - component: Calendar, - render: Demo, - parameters: { - layout: "centered", - }, -} - -export default meta - -type Story = StoryObj - -export const Single: Story = { - args: { - mode: "single", - }, -} - -export const TwoMonthSingle: Story = { - args: { - mode: "single", - numberOfMonths: 2, - }, -} - -export const Range: Story = { - args: { - mode: "range", - }, -} - -export const TwoMonthRange: Story = { - args: { - mode: "range", - numberOfMonths: 2, - }, -} diff --git a/packages/design-system/ui/src/components/calendar/calendar.tsx b/packages/design-system/ui/src/components/calendar/calendar.tsx deleted file mode 100644 index 7a8364635a..0000000000 --- a/packages/design-system/ui/src/components/calendar/calendar.tsx +++ /dev/null @@ -1,174 +0,0 @@ -"use client" - -import { TriangleLeftMini, TriangleRightMini } from "@medusajs/icons" -import * as React from "react" -import { - DayPicker, - useDayRender, - type DayPickerRangeProps, - type DayPickerSingleProps, - type DayProps, -} from "react-day-picker" - -import { clx } from "@/utils/clx" -import { iconButtonVariants } from "../icon-button" - -type OmitKeys = { - [P in keyof T as P extends K ? never : P]: T[P] -} - -type KeysToOmit = "showWeekNumber" | "captionLayout" | "mode" - -type SingleProps = OmitKeys -type RangeProps = OmitKeys - -/** - * @interface - */ -type CalendarProps = - | ({ - mode: "single" - } & SingleProps) - | ({ - mode?: undefined - } & SingleProps) - | ({ - mode: "range" - } & RangeProps) - -/** - * This component is based on the [react-date-picker](https://www.npmjs.com/package/react-date-picker) package. - * - * @excludeExternal - */ -const Calendar = ({ - /** - * @ignore - */ - className, - /** - * @ignore - */ - classNames, - /** - * The calendar's mode. - */ - mode = "single", - /** - * Whether to show days of previous and next months. - * - * @keep - */ - showOutsideDays = true, - /** - * The locale to use for formatting dates. To change the locale pass a date-fns locale object. - * - * @keep - */ - locale, - ...props -}: CalendarProps) => { - return ( - , - IconRight: () => , - Day: Day, - }} - {...(props as SingleProps & RangeProps)} - /> - ) -} -Calendar.displayName = "Calendar" - -const Day = ({ date, displayMonth }: DayProps) => { - const ref = React.useRef(null) - const { activeModifiers, buttonProps, divProps, isButton, isHidden } = - useDayRender(date, displayMonth, ref) - - const { selected, today, disabled, range_middle } = activeModifiers - - React.useEffect(() => { - if (selected) { - ref.current?.focus() - } - }, [selected]) - - if (isHidden) { - return <> - } - - if (!isButton) { - return ( -
- ) - } - - const { - children: buttonChildren, - className: buttonClassName, - ...buttonPropsRest - } = buttonProps - - return ( - - ) -} - -export { Calendar } diff --git a/packages/design-system/ui/src/components/calendar/index.ts b/packages/design-system/ui/src/components/calendar/index.ts deleted file mode 100644 index 33dfcd855d..0000000000 --- a/packages/design-system/ui/src/components/calendar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./calendar" diff --git a/packages/design-system/ui/src/components/calender/_internal-calendar.tsx b/packages/design-system/ui/src/components/calender/_internal-calendar.tsx new file mode 100644 index 0000000000..230190b3c8 --- /dev/null +++ b/packages/design-system/ui/src/components/calender/_internal-calendar.tsx @@ -0,0 +1,53 @@ +"use client" + +import { createCalendar } from "@internationalized/date" +import { TriangleLeftMini, TriangleRightMini } from "@medusajs/icons" +import * as React from "react" +import { + DateValue, + useCalendar, + useLocale, + type CalendarProps, +} from "react-aria" +import { useCalendarState } from "react-stately" + +import { CalendarButton } from "./calendar-button" +import { CalendarGrid } from "./calendar-grid" + +/** + * InternalCalendar is the internal implementation of the Calendar component. + * It's not for public use, but only used for other components like DatePicker. + */ +const InternalCalendar = ( + props: CalendarProps +) => { + const { locale } = useLocale() + + const state = useCalendarState({ + ...props, + locale, + createCalendar, + }) + + const { calendarProps, prevButtonProps, nextButtonProps, title } = + useCalendar(props, state) + + return ( +
+
+ + + +
+

{title}

+
+ + + +
+ +
+ ) +} + +export { InternalCalendar } diff --git a/packages/design-system/ui/src/components/calender/calendar-button.tsx b/packages/design-system/ui/src/components/calender/calendar-button.tsx new file mode 100644 index 0000000000..e6575475fe --- /dev/null +++ b/packages/design-system/ui/src/components/calender/calendar-button.tsx @@ -0,0 +1,29 @@ +import * as React from "react" +import { AriaButtonProps, useButton } from "react-aria" + +import { IconButton } from "@/components/icon-button" + +interface CalendarButtonProps extends AriaButtonProps<"button"> {} + +const CalendarButton = React.forwardRef( + ({ children, ...props }, ref) => { + const innerRef = React.useRef(null) + React.useImperativeHandle(ref, () => innerRef.current as HTMLButtonElement) + + const { buttonProps } = useButton(props, innerRef) + + return ( + + {children} + + ) + } +) +CalendarButton.displayName = "CalendarButton" + +export { CalendarButton } diff --git a/packages/design-system/ui/src/components/calender/calendar-cell.tsx b/packages/design-system/ui/src/components/calender/calendar-cell.tsx new file mode 100644 index 0000000000..31a83006e5 --- /dev/null +++ b/packages/design-system/ui/src/components/calender/calendar-cell.tsx @@ -0,0 +1,75 @@ +"use client" + +import { CalendarDate } from "@internationalized/date" +import * as React from "react" +import { useCalendarCell } from "react-aria" +import { CalendarState } from "react-stately" + +import { clx } from "@/utils/clx" + +interface CalendarCellProps { + date: CalendarDate + state: CalendarState +} + +const CalendarCell = ({ state, date }: CalendarCellProps) => { + const ref = React.useRef(null) + const { + cellProps, + buttonProps, + isSelected, + isOutsideVisibleRange, + isDisabled, + isUnavailable, + formattedDate, + } = useCalendarCell({ date }, state, ref) + + const isToday = getIsToday(date) + + return ( + +