Skip to content

Commit

Permalink
Feat: Implements #297 request (#4004)
Browse files Browse the repository at this point in the history
* [Feat] Add Date re-order option

* Added tests for date re-order

* Updated docs and example

* Update changelog

* Added documentation for getDateElementProps function

* Fix: test failing due to year change

* Removed redundant parameters info
  • Loading branch information
Shubhcoder authored Jan 7, 2024
1 parent 21d1c55 commit accbd16
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 74 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ it according to semantic versioning. For example, if your PR adds a breaking cha
should change the heading of the (upcoming) version to include a major version bump.
-->
# 5.15.3

## @rjsf/utils

- Added `getDateElementProps()` to refactor duplicate function in `core`, `antd` & `chakra-ui` AltDateWidget's source code. The same function, implements the feature requested in [#297](https://github.com/rjsf-team/react-jsonschema-form/issues/297)

## @rjsf/core

- Removed `dateElementProps` function implementation, and replaced it with `getDateElementProps` from `@rjsf/utils`.

## @rjsf/antd

- Removed `dateElementProps` function implementation, and replaced it with `getDateElementProps` from `@rjsf/utils`.

## @rjsf/chakra-ui

- Removed `dateElementProps` function implementation, and replaced it with `getDateElementProps` from `@rjsf/utils`.

## Dev / docs / playground

- Updated docs and playground with the implementation guide of newly added date re-order feature.

# 5.15.2

## @rjsf/core
Expand Down
36 changes: 9 additions & 27 deletions packages/antd/src/widgets/AltDateWidget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Col from 'antd/lib/col';
import Row from 'antd/lib/row';
import {
ariaDescribedByIds,
getDateElementProps,
pad,
parseDateString,
toDateString,
Expand All @@ -14,6 +15,7 @@ import {
StrictRJSFSchema,
TranslatableString,
WidgetProps,
DateElementFormat,
} from '@rjsf/utils';

type DateElementProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Pick<
Expand All @@ -37,31 +39,6 @@ const readyForChange = (state: DateObject) => {
return Object.values(state).every((value) => value !== -1);
};

function dateElementProps(
state: DateObject,
time: boolean,
yearsRange: [number, number] = [1900, new Date().getFullYear() + 2]
) {
const { year, month, day, hour, minute, second } = state;
const data = [
{
type: 'year',
range: yearsRange,
value: year,
},
{ type: 'month', range: [1, 12], value: month },
{ type: 'day', range: [1, 31], value: day },
] as { type: string; range: [number, number]; value: number }[];
if (time) {
data.push(
{ type: 'hour', range: [0, 23], value: hour || -1 },
{ type: 'minute', range: [0, 59], value: minute || -1 },
{ type: 'second', range: [0, 59], value: second || -1 }
);
}
return data;
}

export default function AltDateWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
Expand Down Expand Up @@ -146,7 +123,12 @@ export default function AltDateWidget<

return (
<Row gutter={[Math.floor(rowGutter / 2), Math.floor(rowGutter / 2)]}>
{dateElementProps(state, showTime, options.yearsRange as [number, number] | undefined).map((elemProps, i) => {
{getDateElementProps(
state,
showTime,
options.yearsRange as [number, number] | undefined,
options.format as DateElementFormat | undefined
).map((elemProps, i) => {
const elemId = id + '_' + elemProps.type;
return (
<Col flex='88px' key={elemId}>
Expand All @@ -163,7 +145,7 @@ export default function AltDateWidget<
select: handleChange,
// NOTE: antd components accept -1 rather than issue a warning
// like material-ui, so we need to convert -1 to undefined here.
value: elemProps.value < 0 ? undefined : elemProps.value,
value: elemProps.value || -1 < 0 ? undefined : elemProps.value,
})}
</Col>
);
Expand Down
29 changes: 8 additions & 21 deletions packages/chakra-ui/src/AltDateWidget/AltDateWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { MouseEvent, useEffect, useState } from 'react';
import {
ariaDescribedByIds,
DateElementFormat,
DateObject,
FormContextType,
getDateElementProps,
pad,
parseDateString,
RJSFSchema,
Expand Down Expand Up @@ -91,30 +93,15 @@ function AltDateWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F exten
onChange(undefined);
};

const dateElementProps = () => {
const { year, month, day, hour, minute, second } = state;

const data: { type: string; range: any; value?: number }[] = [
{ type: 'year', range: options.yearsRange, value: year },
{ type: 'month', range: [1, 12], value: month },
{ type: 'day', range: [1, 31], value: day },
];

if (showTime) {
data.push(
{ type: 'hour', range: [0, 23], value: hour },
{ type: 'minute', range: [0, 59], value: minute },
{ type: 'second', range: [0, 59], value: second }
);
}

return data;
};

return (
<Box>
<Box display='flex' flexWrap='wrap' alignItems='center'>
{dateElementProps().map((elemProps: any, i) => {
{getDateElementProps(
state,
showTime,
options.yearsRange as [number, number] | undefined,
options.format as DateElementFormat | undefined
).map((elemProps: any, i) => {
const elemId = id + '_' + elemProps.type;
return (
<Box key={elemId} mr='2' mb='2'>
Expand Down
34 changes: 8 additions & 26 deletions packages/core/src/components/widgets/AltDateWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {
toDateString,
pad,
DateObject,
type DateElementFormat,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
TranslatableString,
WidgetProps,
getDateElementProps,
} from '@rjsf/utils';

function rangeOptions(start: number, stop: number) {
Expand All @@ -24,31 +26,6 @@ function readyForChange(state: DateObject) {
return Object.values(state).every((value) => value !== -1);
}

function dateElementProps(
state: DateObject,
time: boolean,
yearsRange: [number, number] = [1900, new Date().getFullYear() + 2]
) {
const { year, month, day, hour, minute, second } = state;
const data = [
{
type: 'year',
range: yearsRange,
value: year,
},
{ type: 'month', range: [1, 12], value: month },
{ type: 'day', range: [1, 31], value: day },
] as { type: string; range: [number, number]; value: number | undefined }[];
if (time) {
data.push(
{ type: 'hour', range: [0, 23], value: hour },
{ type: 'minute', range: [0, 59], value: minute },
{ type: 'second', range: [0, 59], value: second }
);
}
return data;
}

type DateElementProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Pick<
WidgetProps<T, S, F>,
'value' | 'name' | 'disabled' | 'readonly' | 'autofocus' | 'registry' | 'onBlur' | 'onFocus'
Expand Down Expand Up @@ -161,7 +138,12 @@ function AltDateWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F exten

return (
<ul className='list-inline'>
{dateElementProps(state, time, options.yearsRange as [number, number] | undefined).map((elemProps, i) => (
{getDateElementProps(
state,
time,
options.yearsRange as [number, number] | undefined,
options.format as DateElementFormat | undefined
).map((elemProps, i) => (
<li className='list-inline-item' key={i}>
<DateElement
rootId={id}
Expand Down
96 changes: 96 additions & 0 deletions packages/core/test/StringField.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,54 @@ describe('StringField', () => {

expect(node.querySelector('#custom')).to.exist;
});

describe('AltDateTimeWidget with format option', () => {
const uiSchema = { 'ui:widget': 'alt-datetime', 'ui:options': { format: 'YMD' } };

it('should render a date field with YMD format', () => {
const { node } = createFormComponent({
schema: {
type: 'string',
format: 'date',
},
uiSchema,
});

const ids = [].map.call(node.querySelectorAll('select'), (node) => node.id);

expect(ids).eql(['root_year', 'root_month', 'root_day', 'root_hour', 'root_minute', 'root_second']);
});

it('should render a date field with DMY format', () => {
uiSchema['ui:options']['format'] = 'DMY';
const { node } = createFormComponent({
schema: {
type: 'string',
format: 'date',
},
uiSchema,
});

const ids = [].map.call(node.querySelectorAll('select'), (node) => node.id);

expect(ids).eql(['root_day', 'root_month', 'root_year', 'root_hour', 'root_minute', 'root_second']);
});

it('should render a date field with MDY format', () => {
uiSchema['ui:options']['format'] = 'MDY';
const { node } = createFormComponent({
schema: {
type: 'string',
format: 'date',
},
uiSchema,
});

const ids = [].map.call(node.querySelectorAll('select'), (node) => node.id);

expect(ids).eql(['root_month', 'root_day', 'root_year', 'root_hour', 'root_minute', 'root_second']);
});
});
});

describe('AltDateWidget', () => {
Expand Down Expand Up @@ -1448,6 +1496,54 @@ describe('StringField', () => {
}
});

describe('AltDateWidget with format option', () => {
const uiSchema = { 'ui:widget': 'alt-date', 'ui:options': { format: 'YMD' } };

it('should render a date field with YMD format', () => {
const { node } = createFormComponent({
schema: {
type: 'string',
format: 'date',
},
uiSchema,
});

const ids = [].map.call(node.querySelectorAll('select'), (node) => node.id);

expect(ids).eql(['root_year', 'root_month', 'root_day']);
});

it('should render a date field with MDY format', () => {
uiSchema['ui:options']['format'] = 'MDY';
const { node } = createFormComponent({
schema: {
type: 'string',
format: 'date',
},
uiSchema,
});

const ids = [].map.call(node.querySelectorAll('select'), (node) => node.id);

expect(ids).eql(['root_month', 'root_day', 'root_year']);
});

it('should render a date field with DMY format', () => {
uiSchema['ui:options']['format'] = 'DMY';
const { node } = createFormComponent({
schema: {
type: 'string',
format: 'date',
},
uiSchema,
});

const ids = [].map.call(node.querySelectorAll('select'), (node) => node.id);

expect(ids).eql(['root_day', 'root_month', 'root_year']);
});
});

describe('Action buttons', () => {
it('should render action buttons', () => {
const { node } = createFormComponent({
Expand Down
15 changes: 15 additions & 0 deletions packages/docs/docs/api-reference/utility-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,21 @@ Returns `undefined` when a valid discriminator is not present.

- string | undefined: The `discriminator.propertyName` if it exists in the schema, otherwise `undefined`

### getDateElementProps()

Given date & time information with optional yearRange & format, returns props for DateElement

#### Parameters

- date: DateObject - Object containing date with optional time information
- time: boolean - Determines whether to include time or not
- [yearRange=[1900, new Date().getFullYear() + 2]]: [number, number] - Controls the list of years to be displayed
- [format='YMD']: DateElementFormat - Controls the order in which day, month and year input element will be displayed

#### Returns

- Array of props for DateElement

### getInputProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()

Using the `schema`, `defaultType` and `options`, extract out the props for the `<input>` element that make sense.
Expand Down
3 changes: 3 additions & 0 deletions packages/docs/docs/usage/widgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Please note that, even though they are standardized, `datetime-local`, `date` an

You can customize the list of years displayed in the `year` dropdown by providing a `yearsRange` property to `ui:options` in your uiSchema. It's also possible to remove the `Now` and `Clear` buttons with the `hideNowButton` and `hideClearButton` options.

You can also, customize the order in which date input fields are displayed by providing `format` property to `ui:options` in your uiSchema, available values are `YMD`(default), `MDY` and `DMY`.

```tsx
import { RJSFSchema, UiSchema } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';
Expand All @@ -103,6 +105,7 @@ const uiSchema: UiSchema = {
'ui:widget': 'alt-datetime',
'ui:options': {
yearsRange: [1980, 2030],
format: 'MDY',
hideNowButton: true,
hideClearButton: true,
},
Expand Down
2 changes: 2 additions & 0 deletions packages/playground/src/samples/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ const date: Sample = {
'ui:widget': 'alt-datetime',
'ui:options': {
yearsRange: [1980, 2030],
format: 'YMD',
},
},
'alt-date': {
'ui:widget': 'alt-date',
'ui:options': {
yearsRange: [1980, 2030],
format: 'MDY',
},
},
},
Expand Down
Loading

0 comments on commit accbd16

Please sign in to comment.