Skip to content

Commit

Permalink
XS-30 | Multi language support - Xola app content (#334)
Browse files Browse the repository at this point in the history
* XS-25 feat(purchase-guests): adds `CircleCheckFilledIcon` and `WarningFilledIcon`

* feat: adds localization support for DatePicker

* XS-30: adds translation-blacklist tag for DatePicker and Key

* fix: updates sidebar component to support `className` and `align` props

* XS-30 fixed date picker locale issues

* XS-30 fixed locale in month year selector

* XS-30 refactor

* XS-30 fixed currency

* XS-30 Added short months

* XS-30 added console.logs

* Moved locale files to import block

* XS-30 changed lot messge

* XS-30 fixed es-MX issue

* XS-30 Added Support for classnames to sidemenubar text

* XS-30 added condition for ignoring locale when its not there

* XS-30 removed console.logs, lint fix

* XS-30 Added XO config

* XS-30 Dummy commit

* Fix unicorn/no-console-spaces param

* Update eslint.yml to use `ES_LINT_TOKEN`

* npm audit fix by rushi

---------

Co-authored-by: Manoj Vaibhav <[email protected]>
Co-authored-by: Rushi Vishavadia <[email protected]>
  • Loading branch information
3 people authored Sep 9, 2024
1 parent 2b00715 commit b27eae8
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .xo-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"complexity": ["warn", { "max": 25 }]
}
}
50 changes: 26 additions & 24 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions src/components/DatePicker/DatePicker.helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as de from "dayjs/locale/de";
import * as en from "dayjs/locale/en";
import * as enGB from "dayjs/locale/en-gb";
import * as es from "dayjs/locale/es";
import * as esMX from "dayjs/locale/es-mx";
import * as fr from "dayjs/locale/fr";
import type { DayPickerProps } from "react-day-picker";

export type LocaleCode = keyof typeof locales;

interface ExtendedDayPickerProps extends DayPickerProps {
monthsShort?: string;
}

export type LocalizationProps = Pick<
ExtendedDayPickerProps,
"locale" | "months" | "monthsShort" | "weekdaysLong" | "weekdaysShort" | "firstDayOfWeek"
>;

const locales = {
en: en,
en_US: en,
en_GB: enGB,

es: es,
es_ES: es,
es_MX: esMX,

fr: fr,
de: de,
};

export const getLocalizationProps = async (localeCode: LocaleCode): Promise<Partial<LocalizationProps>> => {
try {
const locale = await locales[localeCode];

return {
locale: localeCode,
weekdaysLong: locale.weekdays,
weekdaysShort: locale.weekdaysShort,
months: locale.months,
monthsShort: locale.monthsShort,
firstDayOfWeek: locale.weekStart,
};
} catch (error) {
console.error("Error: React Day Picker localization error", error);
throw error;
}
};
21 changes: 17 additions & 4 deletions src/components/DatePicker/DatePicker.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import clsx from "clsx";
import dayjs from "dayjs";
import PropTypes from "prop-types";
import React, { useEffect, useMemo, useState } from "react";
import DayPicker, { DateUtils } from "react-day-picker";
import React, { useContext, useEffect, useMemo, useState } from "react";
import { DateUtils } from "react-day-picker";
import "react-day-picker/lib/style.css";
import "./DatePicker.css";
import { isArray, isFunction } from "lodash";
import { Tooltip } from "../..";
import { isSame, isValidTimeZoneName, now, toDate } from "../../helpers/date";
import { Context } from "../Provider";
import { Day } from "./Day";
import { LocalizedDayPicker } from "./LocalizedDayPicker";
import { MonthYearSelector } from "./MonthYearSelector";
import { NavbarElement } from "./NavbarElement";
import RangeDatePicker from "./RangeDatePicker";
Expand Down Expand Up @@ -38,9 +40,11 @@ export const DatePicker = ({
components = {},
getTooltip,
upcomingDates,
locale,
timezoneName = null, // seller timezone (e.g. "America/Los_Angeles") to return correct today date
...rest
}) => {
const { locale: contextLocale } = useContext(Context);
const initialValue = value ? (variant === variants.single ? value : value.from) : null;
const [currentMonth, setCurrentMonth] = useState(initialValue ?? now(null, timezoneName).toDate());
const [startMonth, setStartMonth] = useState(() => {
Expand Down Expand Up @@ -183,7 +187,14 @@ export const DatePicker = ({
// TODO: Should be outside this component because this returns JSX
const CaptionElement = useMemo(() => {
return shouldShowYearPicker && currentMonth
? ({ date }) => <MonthYearSelector date={date} currentMonth={currentMonth} onChange={handleMonthChange} />
? ({ date }) => (
<MonthYearSelector
date={date}
currentMonth={currentMonth}
locale={locale ?? contextLocale}
onChange={handleMonthChange}
/>
)
: undefined;
// Adding `handleMonthChange` causes a lot of re-renders, and closes drop-down.
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -250,11 +261,12 @@ export const DatePicker = ({
handleEndMonthChange={handleEndMonthChange}
handleTodayClick={handleTodayClick}
selectedDays={selectedDays}
locale={locale ?? contextLocale}
timezoneName={timezoneName}
{...rest}
/>
) : (
<DayPicker
<LocalizedDayPicker
className={clsx(
"ui-date-picker rounded-lg pt-3",
useDateRangeStyle ? "date-range-picker" : null,
Expand Down Expand Up @@ -312,5 +324,6 @@ DatePicker.propTypes = {
shouldShowRelativeRanges: PropTypes.bool,
components: PropTypes.shape({ Footer: PropTypes.oneOfType([PropTypes.node, PropTypes.func]) }),
getTooltip: PropTypes.func,
locale: PropTypes.string,
timezoneName: PropTypes.string,
};
2 changes: 1 addition & 1 deletion src/components/DatePicker/DatePickerPopover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const DefaultInput = forwardRef(({ className, ...rest }, reference) => {
<CalendarIcon className="z-10 inline-block" />
</div>

<Input className={clsx("cursor-pointer px-8", className)} {...rest} />
<Input className={clsx("no-translate cursor-pointer px-8", className)} {...rest} />

<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<DownArrowIcon className="inline-block" />
Expand Down
22 changes: 22 additions & 0 deletions src/components/DatePicker/LocalizedDayPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import clsx from "clsx";
import React, { forwardRef, useContext, useEffect, useState } from "react";
import DayPicker, { DayPickerProps } from "react-day-picker";
import { Context } from "../Provider";
import { getLocalizationProps, LocaleCode, LocalizationProps } from "./DatePicker.helpers";

export const LocalizedDayPicker = forwardRef<any, DayPickerProps>(({ className, ...rest }, ref) => {
const { locale } = useContext(Context);
const [localizationProps, setLocalizationProps] = useState<Partial<LocalizationProps>>({});
console.log("Locale", locale);

useEffect(() => {
setLocalizationProps({});

/** We don't want any localization-related props for "English" */
if (!locale || locale === "en" || locale === "en_US") return;

getLocalizationProps(locale as LocaleCode).then(setLocalizationProps);
}, [locale]);

return <DayPicker ref={ref} className={clsx(className)} {...localizationProps} {...rest} />;
});
9 changes: 6 additions & 3 deletions src/components/DatePicker/MonthYearSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ const getDiffInMonths = (to, from) => {
return 12 * (to.getFullYear() - from.getFullYear()) + (to.getMonth() - from.getMonth());
};

export const MonthYearSelector = ({ date, onChange, currentMonth }) => {
const months = [...Array.from({ length: 12 }).keys()].map((m) => today.month(m).format("MMM"));
export const MonthYearSelector = ({ date, locale, onChange, currentMonth }) => {
const months = [...Array.from({ length: 12 }).keys()].map((m) => today.locale(locale).month(m).format("MMM"));
// 2012 as baseline + 5 years in future
const years = [...Array.from({ length: today.year() - 2012 + 5 + 1 }).keys()].map((y) =>
today.year(2012 + y).format("YYYY"),
today
.locale(locale)
.year(2012 + y)
.format("YYYY"),
);

/**
Expand Down
11 changes: 7 additions & 4 deletions src/components/DatePicker/RangeDatePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import clsx from "clsx";
import { isArray, isFunction } from "lodash";
import PropTypes from "prop-types";
import React from "react";
import DayPicker from "react-day-picker";
import { now } from "../../helpers/date";
import { Tooltip } from "../Tooltip";
import { Day } from "./Day";
import { LocalizedDayPicker } from "./LocalizedDayPicker";
import { MonthYearSelector } from "./MonthYearSelector";
import { NavbarElement } from "./NavbarElement";

Expand All @@ -24,6 +24,7 @@ const RangeDatePicker = ({
handleStartMonthChange,
handleEndMonthChange,
handleTodayClick,
locale,
timezoneName,
...rest
}) => {
Expand All @@ -32,7 +33,9 @@ const RangeDatePicker = ({

const createCaptionElement = (currentMonth, handleChange) =>
shouldShowYearPicker && currentMonth
? ({ date }) => <MonthYearSelector date={date} currentMonth={currentMonth} onChange={handleChange} />
? ({ date }) => (
<MonthYearSelector date={date} currentMonth={currentMonth} locale={locale} onChange={handleChange} />
)
: undefined;

const CaptionStartElement = createCaptionElement(startMonth, handleStartMonthChange);
Expand Down Expand Up @@ -90,7 +93,7 @@ const RangeDatePicker = ({

return (
<div className="flex gap-4">
<DayPicker
<LocalizedDayPicker
className={clsx(
"ui-date-picker max-w-[400px] rounded-lg pt-3",
isDateRangeStyle ? "date-range-picker" : null,
Expand All @@ -110,7 +113,7 @@ const RangeDatePicker = ({
onTodayButtonClick={handleTodayClick}
{...rest}
/>
<DayPicker
<LocalizedDayPicker
className={clsx(
"ui-date-picker max-w-[400px] rounded-lg pt-3",
isDateRangeStyle ? "date-range-picker" : null,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Drawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Dialog, Transition } from "@headlessui/react";
import clsx from "clsx";
import PropTypes from "prop-types";
import React, { forwardRef, Fragment } from "react";
import { CloseIcon } from "../icons";
import { isIosBrowser } from "../helpers/browser";
import { useViewportHeight } from "../hooks/useViewportHeight";
import { CloseIcon } from "../icons";
import { Button } from "./Buttons/Button";

const sizes = {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Key.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const Key = ({ char, className, ...rest }) => {
<div
className={clsx(
"ui-key",
"inline-flex h-5 items-center justify-center rounded bg-gray-lighter py-1 px-2 text-xs font-semibold text-gray",
"no-translate inline-flex h-5 items-center justify-center rounded bg-gray-lighter py-1 px-2 text-xs font-semibold text-gray",
key.length === 1 && "w-5",
className,
)}
Expand Down
8 changes: 6 additions & 2 deletions src/components/Provider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@ export const Context = createContext({

return fallbackIdCounter++;
},

localize: true,

locale: "en_US",
});

/**
* UI Kit's default provider.
* Must be used from now on in order to generate correct component IDs.
* Also a good place to implement any global state required for the UI Kit in the future.
*/
export const Provider = ({ children }) => {
export const Provider = ({ children, localize = true, locale = "en_US" }) => {
const idCounterRef = useRef(1);

const generateId = useCallback(() => {
return idCounterRef.current++;
}, [idCounterRef]);

const value = useMemo(() => ({ generateId }), [generateId]);
const value = useMemo(() => ({ generateId, localize, locale }), [generateId, localize, locale]);

return <Context.Provider value={value}>{children}</Context.Provider>;
};
8 changes: 6 additions & 2 deletions src/components/Sidebar/Sidebar.Button.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import clsx from "clsx";
import PropTypes from "prop-types";
import React from "react";

export const SidebarButton = ({ icon: Icon, label, ...rest }) => {
export const SidebarButton = ({ icon: Icon, label, className, ...rest }) => {
return (
<button
type="button"
className="ui-sidebar-button flex w-full cursor-pointer items-center rounded py-2 px-4 hover:bg-gray-darker"
className={clsx(
"ui-sidebar-button flex w-full cursor-pointer items-center rounded py-2 px-4 hover:bg-gray-darker",
className,
)}
{...rest}
>
<div className="p-1.5">
Expand Down
Loading

0 comments on commit b27eae8

Please sign in to comment.