Skip to content

Commit

Permalink
[pickers] Add [email protected] adapter (mui#11462)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas <[email protected]>
Co-authored-by: Michel Engelen <[email protected]>
  • Loading branch information
LukasTy and michelengelen authored Jan 11, 2024
1 parent 92b62b9 commit a69432d
Show file tree
Hide file tree
Showing 23 changed files with 694 additions and 200 deletions.
11 changes: 11 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ module.exports = function getBabelConfig(api) {

if (process.env.NODE_ENV === 'test') {
plugins.push(['@babel/plugin-transform-export-namespace-from']);
// We replace `date-fns` imports with an aliased `date-fns@v3` version installed as `date-fns-v3` for tests.
// The plugin is patched to only run on `AdapterDateFnsV3.ts`.
// TODO: remove when we upgrade to date-fns v3 by default.
plugins.push([
'babel-plugin-replace-imports',
{
test: /date-fns/i,
replacer: 'date-fns-v3',
ignoreFilenames: 'AdapterDateFns.ts',
},
]);
}

if (process.env.NODE_ENV === 'production') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import InputBase from '@mui/material/InputBase';
import locale from 'date-fns/locale/en-US';
import { enUS as locale } from 'date-fns/locale';
import { styled } from '@mui/material/styles';

const dateAdapter = new AdapterDateFns({ locale });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import InputBase, { InputBaseProps } from '@mui/material/InputBase';
import locale from 'date-fns/locale/en-US';
import { enUS as locale } from 'date-fns/locale';
import { styled } from '@mui/material/styles';
import { TextFieldProps } from '@mui/material/TextField';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import * as React from 'react';
import de from 'date-fns/locale/de';
import enGB from 'date-fns/locale/en-GB';
import zhCN from 'date-fns/locale/zh-CN';
import { de, enGB, zhCN } from 'date-fns/locale';
import Stack from '@mui/material/Stack';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import * as React from 'react';
import de from 'date-fns/locale/de';
import enGB from 'date-fns/locale/en-GB';
import zhCN from 'date-fns/locale/zh-CN';
import { de, enGB, zhCN } from 'date-fns/locale';
import Stack from '@mui/material/Stack';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
Expand Down
17 changes: 17 additions & 0 deletions docs/data/date-pickers/adapters-locale/adapters-locale.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,23 @@ import 'dayjs/locale/de';

For `date-fns`, import the locale and pass it to `LocalizationProvider`:

:::info
Both `date-fns` major versions (v2.x and v3.x) are supported.

A single adapter can not work for both `date-fns` v2.x and v3.x, because the way functions are exported has been changed in v3.x.

To use `date-fns` v3.x, you will have to import the adapter from `@mui/x-date-pickers/AdapterDateFnsV3` instead of `@mui/x-date-pickers/AdapterDateFns`.
:::

```tsx
// with date-fns v2.x
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
// with date-fns v3.x
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
// with date-fns v2.x
import de from 'date-fns/locale/de';
// with date-fns v3.x
import { de } from 'date-fns/locale/de';

<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={de}>
{children}
Expand Down Expand Up @@ -280,7 +294,10 @@ dayjs.updateLocale('en', {
For `date-fns`, use the `setDefaultOptions` utility:

```ts
// with date-fns v2.x
import setDefaultOptions from 'date-fns/setDefaultOptions';
// with date-fns v3.x
import { setDefaultOptions } from 'date-fns/setDefaultOptions';

setDefaultOptions({
// Sunday = 0, Monday = 1.
Expand Down
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"core-js": "^2.6.12",
"cross-env": "^7.0.3",
"date-fns": "^2.30.0",
"date-fns-v3": "https://registry.npmjs.org/date-fns/-/date-fns-3.2.0.tgz",
"date-fns-jalali": "^2.21.3-1",
"dayjs": "^1.11.10",
"doctrine": "^3.0.0",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
"babel-plugin-module-resolver": "^5.0.0",
"babel-plugin-optimize-clsx": "^2.6.2",
"babel-plugin-react-remove-properties": "^0.3.0",
"babel-plugin-replace-imports": "^1.0.2",
"babel-plugin-search-and-replace": "^1.1.1",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"babel-plugin-transform-rename-import": "^2.3.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/x-date-pickers-pro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.8.6",
"date-fns": "^2.25.0",
"date-fns": "^2.25.0 || ^3.2.0",
"date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.10.7",
"luxon": "^3.0.2",
Expand Down
1 change: 1 addition & 0 deletions packages/x-date-pickers-pro/src/AdapterDateFnsV3/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
2 changes: 1 addition & 1 deletion packages/x-date-pickers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.8.6",
"date-fns": "^2.25.0",
"date-fns": "^2.25.0 || ^3.2.0",
"date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.10.7",
"luxon": "^3.0.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import {
describeGregorianAdapter,
TEST_DATE_ISO_STRING,
} from 'test/utils/pickers';
import enUS from 'date-fns/locale/en-US';
import fr from 'date-fns/locale/fr';
import de from 'date-fns/locale/de';
import ru from 'date-fns/locale/ru';
import { enUS, fr, de, ru } from 'date-fns/locale';

describe('<AdapterDateFns />', () => {
describeGregorianAdapter(AdapterDateFns, {
Expand Down
198 changes: 15 additions & 183 deletions packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,109 +44,11 @@ import isWithinInterval from 'date-fns/isWithinInterval';
import defaultLocale from 'date-fns/locale/en-US';
// @ts-ignore
import longFormatters from 'date-fns/_lib/format/longFormatters';
import {
AdapterFormats,
AdapterOptions,
DateBuilderReturnType,
FieldFormatTokenMap,
MuiPickersAdapter,
} from '../models';
import { AdapterFormats, AdapterOptions, MuiPickersAdapter } from '../models';
import { AdapterDateFnsBase } from '../AdapterDateFnsBase';

type DateFnsLocale = typeof defaultLocale;

const formatTokenMap: FieldFormatTokenMap = {
// Year
y: { sectionType: 'year', contentType: 'digit', maxLength: 4 },
yy: 'year',
yyy: { sectionType: 'year', contentType: 'digit', maxLength: 4 },
yyyy: 'year',

// Month
M: { sectionType: 'month', contentType: 'digit', maxLength: 2 },
MM: 'month',
MMMM: { sectionType: 'month', contentType: 'letter' },
MMM: { sectionType: 'month', contentType: 'letter' },
L: { sectionType: 'month', contentType: 'digit', maxLength: 2 },
LL: 'month',
LLL: { sectionType: 'month', contentType: 'letter' },
LLLL: { sectionType: 'month', contentType: 'letter' },

// Day of the month
d: { sectionType: 'day', contentType: 'digit', maxLength: 2 },
dd: 'day',
do: { sectionType: 'day', contentType: 'digit-with-letter' },

// Day of the week
E: { sectionType: 'weekDay', contentType: 'letter' },
EE: { sectionType: 'weekDay', contentType: 'letter' },
EEE: { sectionType: 'weekDay', contentType: 'letter' },
EEEE: { sectionType: 'weekDay', contentType: 'letter' },
EEEEE: { sectionType: 'weekDay', contentType: 'letter' },
i: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 },
ii: 'weekDay',
iii: { sectionType: 'weekDay', contentType: 'letter' },
iiii: { sectionType: 'weekDay', contentType: 'letter' },
e: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 },
ee: 'weekDay',
eee: { sectionType: 'weekDay', contentType: 'letter' },
eeee: { sectionType: 'weekDay', contentType: 'letter' },
eeeee: { sectionType: 'weekDay', contentType: 'letter' },
eeeeee: { sectionType: 'weekDay', contentType: 'letter' },
c: { sectionType: 'weekDay', contentType: 'digit', maxLength: 1 },
cc: 'weekDay',
ccc: { sectionType: 'weekDay', contentType: 'letter' },
cccc: { sectionType: 'weekDay', contentType: 'letter' },
ccccc: { sectionType: 'weekDay', contentType: 'letter' },
cccccc: { sectionType: 'weekDay', contentType: 'letter' },

// Meridiem
a: 'meridiem',
aa: 'meridiem',
aaa: 'meridiem',

// Hours
H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 },
HH: 'hours',
h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 },
hh: 'hours',

// Minutes
m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 },
mm: 'minutes',

// Seconds
s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 },
ss: 'seconds',
};

const defaultFormats: AdapterFormats = {
year: 'yyyy',
month: 'LLLL',
monthShort: 'MMM',
dayOfMonth: 'd',
weekday: 'EEEE',
weekdayShort: 'EEEEEE',
hours24h: 'HH',
hours12h: 'hh',
meridiem: 'aa',
minutes: 'mm',
seconds: 'ss',

fullDate: 'PP',
keyboardDate: 'P',
shortDate: 'MMM d',
normalDate: 'd MMMM',
normalDateWithWeekday: 'EEE, MMM d',

fullTime: 'p',
fullTime12h: 'hh:mm aa',
fullTime24h: 'HH:mm',

keyboardDateTime: 'P p',
keyboardDateTime12h: 'P hh:mm aa',
keyboardDateTime24h: 'P HH:mm',
};

/**
* Based on `@date-io/date-fns`
*
Expand All @@ -172,54 +74,21 @@ const defaultFormats: AdapterFormats = {
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
export class AdapterDateFns implements MuiPickersAdapter<Date, DateFnsLocale> {
public isMUIAdapter = true;

public isTimezoneCompatible = false;

public lib = 'date-fns';

public locale?: DateFnsLocale;

public formats: AdapterFormats;

public formatTokenMap = formatTokenMap;

public escapedCharacters = { start: "'", end: "'" };

export class AdapterDateFns
extends AdapterDateFnsBase<DateFnsLocale>
implements MuiPickersAdapter<Date, DateFnsLocale>
{
constructor({ locale, formats }: AdapterOptions<DateFnsLocale, never> = {}) {
this.locale = locale;
this.formats = { ...defaultFormats, ...formats };
}

public date = <T extends string | null | undefined>(
value?: T,
): DateBuilderReturnType<T, Date> => {
type R = DateBuilderReturnType<T, Date>;
if (typeof value === 'undefined') {
return <R>new Date();
}

if (value === null) {
return <R>null;
if (typeof addDays !== 'function') {
throw new Error(
[
'MUI: The `date-fns` package v3.x is not compatible with this adapter.',
'Please, install v2.x of the package or use the `AdapterDateFnsV3` instead.',
].join('\n'),
);
}

return <R>new Date(value);
};

public getInvalidDate = () => new Date('Invalid Date');

public getTimezone = (): string => {
return 'default';
};

public setTimezone = (value: Date): Date => {
return value;
};

public toJsDate = (value: Date) => {
return value;
};
super({ locale: locale ?? defaultLocale, formats, longFormatters });
}

public parse = (value: string, format: string) => {
if (value === '') {
Expand All @@ -229,39 +98,6 @@ export class AdapterDateFns implements MuiPickersAdapter<Date, DateFnsLocale> {
return dateFnsParse(value, format, new Date(), { locale: this.locale });
};

public getCurrentLocaleCode = () => {
return this.locale?.code || 'en-US';
};

// Note: date-fns input types are more lenient than this adapter, so we need to expose our more
// strict signature and delegate to the more lenient signature. Otherwise, we have downstream type errors upon usage.
public is12HourCycleInCurrentLocale = () => {
if (this.locale) {
return /a/.test(this.locale.formatLong!.time());
}

// By default, date-fns is using en-US locale with am/pm enabled
return true;
};

public expandFormat = (format: string) => {
const longFormatRegexp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g;

// @see https://github.com/date-fns/date-fns/blob/master/src/format/index.js#L31
return format
.match(longFormatRegexp)!
.map((token: string) => {
const firstCharacter = token[0];
if (firstCharacter === 'p' || firstCharacter === 'P') {
const longFormatter = longFormatters[firstCharacter];
const locale = this.locale || defaultLocale;
return longFormatter(token, locale.formatLong, {});
}
return token;
})
.join('');
};

public isValid = (value: Date | null) => {
if (value == null) {
return false;
Expand All @@ -278,10 +114,6 @@ export class AdapterDateFns implements MuiPickersAdapter<Date, DateFnsLocale> {
return dateFnsFormat(value, formatString, { locale: this.locale });
};

public formatNumber = (numberToFormat: string) => {
return numberToFormat;
};

public isEqual = (value: Date | null, comparing: Date | null) => {
if (value === null && comparing === null) {
return true;
Expand Down
Loading

0 comments on commit a69432d

Please sign in to comment.