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

Datetime widget fixes and improvements #7261

Merged
merged 23 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ef46f40
fix: set default values if format is boolean
martinjagodic Jul 4, 2024
54b7b46
Pass avatar_url un git-gateway backend (#7247)
loteoo Jul 8, 2024
0fb9dd3
fix: update ukrainian lang (#7252)
olegfedak Jul 29, 2024
daa62ab
fix(select-widget): select widget not able to select number value `0`…
imangd Aug 1, 2024
a46b441
fix: fetch GitHub PR author name, fixes #7232 (#7253)
domcleal Aug 1, 2024
70b038b
Remove typo from error message (#7249)
ryangittings Aug 1, 2024
29f7f48
Change `Authorization` header type for API requests from "token" to "…
floscher Aug 1, 2024
aef22c7
fix(backend): allow a custom API root for backend (#7214)
JbIPS Aug 2, 2024
1115dd0
fix(i18n): allow to store the new entry in the draft after a pre save…
jmfiaschi Aug 2, 2024
9ff2d20
fix: improve field error position (#7260)
martinjagodic Aug 2, 2024
65952c4
fix(i18n): improve Thai locale (#7248)
weeix Aug 5, 2024
427ed00
fix: make default value empty
martinjagodic Aug 5, 2024
b6e5957
feat: add {{now}} option as datetime default
martinjagodic Aug 5, 2024
f77b625
Merge branch 'main' into datetime
martinjagodic Aug 5, 2024
febbc22
feat: add UTC indicator to datetime control
martinjagodic Aug 7, 2024
cd604d8
fix: format
martinjagodic Aug 7, 2024
5d322bd
fix: convert Z to [Z] if picker_utc: true
martinjagodic Aug 7, 2024
cecda08
fix: reorder getFormat so that `format`comes before time and date for…
martinjagodic Aug 7, 2024
72d69a0
fix: formatting
martinjagodic Aug 7, 2024
18b83ed
Merge branch 'main' into datetime
martinjagodic Aug 7, 2024
92875ce
fix: add defaults to test datetime
martinjagodic Aug 8, 2024
61a3b88
feat: add datetime unit test
martinjagodic Aug 8, 2024
7b0cf78
fix: make datetime test work
martinjagodic Aug 9, 2024
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
1 change: 1 addition & 0 deletions dev-test/backends/azure/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ collections:
name: date
widget: datetime
format: 'YYYY-MM-DDTHH:mm'
default: 1970-01-01T01:00
- label: Description
name: description
widget: text
Expand Down
1 change: 1 addition & 0 deletions dev-test/backends/bitbucket/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ collections:
name: date
widget: datetime
format: 'YYYY-MM-DDTHH:mm'
default: 1970-01-01T01:00
- label: Description
name: description
widget: text
Expand Down
1 change: 1 addition & 0 deletions dev-test/backends/git-gateway/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ collections:
name: date
widget: datetime
format: 'YYYY-MM-DDTHH:mm'
default: 1970-01-01T01:00
- label: Description
name: description
widget: text
Expand Down
1 change: 1 addition & 0 deletions dev-test/backends/gitea/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ collections:
name: date
widget: datetime
format: 'YYYY-MM-DDTHH:mm'
default: 1970-01-01T01:00
- label: Description
name: description
widget: text
Expand Down
1 change: 1 addition & 0 deletions dev-test/backends/github/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ collections:
name: date
widget: datetime
format: 'YYYY-MM-DDTHH:mm'
default: 1970-01-01T01:00
- label: Description
name: description
widget: text
Expand Down
1 change: 1 addition & 0 deletions dev-test/backends/gitlab/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ collections:
name: date
widget: datetime
format: 'YYYY-MM-DDTHH:mm'
default: 1970-01-01T01:00
- label: Description
name: description
widget: text
Expand Down
1 change: 1 addition & 0 deletions dev-test/backends/proxy/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ collections:
name: date
widget: datetime
format: 'YYYY-MM-DDTHH:mm'
default: 1970-01-01T01:00
- label: Description
name: description
widget: text
Expand Down
3 changes: 1 addition & 2 deletions dev-test/backends/test/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ collections: # A list of collections the CMS should be able to edit
label: 'Publish Date',
name: 'date',
widget: 'datetime',
date_format: 'YYYY-MM-DD',
time_format: 'HH:mm',
format: 'YYYY-MM-DD HH:mm',
default: '{{now}}',
}
- label: 'Cover Image'
name: 'image'
Expand Down
3 changes: 1 addition & 2 deletions dev-test/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ collections: # A list of collections the CMS should be able to edit
label: 'Publish Date',
name: 'date',
widget: 'datetime',
date_format: 'YYYY-MM-DD',
time_format: 'HH:mm',
format: 'YYYY-MM-DD HH:mm',
default: '{{now}}',
}
- label: 'Cover Image'
name: 'image'
Expand Down
110 changes: 64 additions & 46 deletions packages/decap-cms-widget-datetime/src/DateTimeControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dayjs.extend(customParseFormat);
dayjs.extend(localizedFormat);
dayjs.extend(utc);

function Buttons({ t, handleChange, inputFormat, isUtc }) {
function Buttons({ t, handleChange, getNow }) {
return (
<div
css={css`
Expand All @@ -26,9 +26,8 @@ function Buttons({ t, handleChange, inputFormat, isUtc }) {
${buttons.button}
${buttons.widget}
`}
onClick={() =>
handleChange(isUtc ? dayjs.utc().format(inputFormat) : dayjs().format(inputFormat))
}
onClick={() => handleChange(getNow())}
data-testid="now-button"
>
{t('editor.editorWidgets.datetime.now')}
</button>
Expand All @@ -38,6 +37,7 @@ function Buttons({ t, handleChange, inputFormat, isUtc }) {
${buttons.widget}
`}
onClick={() => handleChange('')}
data-testid="clear-button"
>
{t('editor.editorWidgets.datetime.clear')}
</button>
Expand All @@ -62,58 +62,71 @@ class DateTimeControl extends React.Component {
isDisabled: false,
};

isUtc = this.props.field.get('picker_utc') || false;

escapeZ(str) {
if (/Z(?![\]])/.test(str)) {
return str.replace('Z', '[Z]');
}
return str;
}

getFormat() {
const { field } = this.props;
const format = field?.get('format') || 'YYYY-MM-DDTHH:mm:ss.SSS[Z]';
const dateFormat = field?.get('date_format');
const timeFormat = field?.get('time_format');
let inputFormat = 'YYYY-MM-DDTHH:mm';
let inputType = 'datetime-local';

if (dateFormat && timeFormat) {
return { format: `${dateFormat}T${timeFormat}`, inputType, inputFormat };
let inputFormat = 'YYYY-MM-DDTHH:mm';
let format = 'YYYY-MM-DDTHH:mm:ss.SSS[Z]';
let userFormat = field?.get('format');
let dateFormat = field?.get('date_format');
let timeFormat = field?.get('time_format');
if (dateFormat === true) dateFormat = 'YYYY-MM-DD';
if (timeFormat === true) timeFormat = 'HH:mm';

if (this.isUtc) {
userFormat = this.escapeZ(userFormat);
dateFormat = this.escapeZ(dateFormat);
timeFormat = this.escapeZ(timeFormat);
}

if (timeFormat) {
if (typeof dateFormat === 'string' && typeof timeFormat === 'string') {
format = `${dateFormat}T${timeFormat}`;
} else if (typeof timeFormat === 'string') {
inputType = 'time';
inputFormat = 'HH:mm';
return { format: timeFormat, inputType, inputFormat };
format = timeFormat;
} else if (typeof dateFormat === 'string') {
inputType = 'date';
format = dateFormat;
}

if (dateFormat) {
inputType = 'date';
inputFormat = 'YYYY-MM-DD';
return { format: dateFormat, inputType, inputFormat };
if (typeof userFormat === 'string') {
format = userFormat;
inputType = 'datetime-local';
}

return { format, inputType, inputFormat };
}
if (dateFormat === false) inputType = 'time';
if (timeFormat === false) inputType = 'date';
if (inputType === 'datetime-local') inputFormat = 'YYYY-MM-DDTHH:mm';
if (inputType === 'date') inputFormat = 'YYYY-MM-DD';
if (inputType === 'time') inputFormat = 'HH:mm';

getDefaultValue() {
const { field } = this.props;
const defaultValue = field.get('default');
return defaultValue;
return { format, inputType, inputFormat };
}

isUtc = this.props.field.get('picker_utc') || false;
isValidDate = datetime => dayjs(datetime).isValid() || datetime === '';
defaultValue = this.getDefaultValue();
isValidDate = dt => dayjs(dt, this.getFormat().inputFormat).isValid() || dt === '';

componentDidMount() {
const { value } = this.props;
getNow() {
const { inputFormat } = this.getFormat();
if (value === undefined) {
setTimeout(() => {
this.handleChange(
this.defaultValue === undefined ? dayjs().format(inputFormat) : this.defaultValue,
);
}, 0);
}
return this.isUtc ? dayjs.utc().format(inputFormat) : dayjs().format(inputFormat);
}

formatInputValue(value) {
if (value === '') return value;
const { format, inputFormat } = this.getFormat();

if (typeof value === 'string' && value?.replace(/\s+/g, '') === '{{now}}') {
return this.getNow();
}

const inputValue = this.isUtc
? dayjs.utc(value, format).format(inputFormat)
: dayjs(value, format).format(inputFormat);
Expand All @@ -139,14 +152,13 @@ class DateTimeControl extends React.Component {

onInputChange = e => {
const etv = e.target.value;
const newValue = dayjs(etv);
this.handleChange(etv === '' ? '' : newValue);
this.handleChange(etv);
};

render() {
const { forID, value, classNameWrapper, setActiveStyle, setInactiveStyle, t, isDisabled } =
this.props;
const { inputType, inputFormat } = this.getFormat();
const { inputType } = this.getFormat();

return (
<div
Expand All @@ -159,20 +171,26 @@ class DateTimeControl extends React.Component {
>
<input
id={forID}
data-testid={forID}
type={inputType}
value={this.formatInputValue(value)}
value={value ? this.formatInputValue(value) : ''}
onChange={this.onInputChange}
onFocus={setActiveStyle}
onBlur={setInactiveStyle}
disabled={isDisabled}
/>
{this.isUtc && (
<span
css={css`
font-size: 0.8em;
color: #666;
`}
>
UTC
</span>
)}
{!isDisabled && (
<Buttons
t={t}
handleChange={v => this.handleChange(v)}
inputFormat={inputFormat}
isUtc={this.isUtc}
/>
<Buttons t={t} handleChange={v => this.handleChange(v)} getNow={() => this.getNow()} />
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import dayjs from 'dayjs';

import DateTimeControl from '../DateTimeControl';

function setup(propsOverrides = {}) {
const props = {
forID: 'test-datetime',
onChange: jest.fn(),
classNameWrapper: 'classNameWrapper',
setActiveStyle: jest.fn(),
setInactiveStyle: jest.fn(),
value: '',
t: key => key,
isDisabled: false,
field: {
get: jest.fn().mockReturnValue('DD.MM.YYYY'),
},
...propsOverrides,
};

const utils = render(<DateTimeControl {...props} />);
const input = utils.getByTestId('test-datetime');
const nowButton = utils.getByTestId('now-button');
const clearButton = utils.getByTestId('clear-button');

return {
...utils,
props,
input,
nowButton,
clearButton,
};
}

describe('DateTimeControl', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('renders the component with input, now button, and clear button', () => {
const { getByTestId } = setup();
expect(getByTestId('test-datetime')).toBeInTheDocument();
expect(getByTestId('now-button')).toBeInTheDocument();
expect(getByTestId('clear-button')).toBeInTheDocument();
});

test('set value to current date if now button is clicked', () => {
const { nowButton, props } = setup();
fireEvent.click(nowButton);
expect(props.onChange).toHaveBeenCalledWith(dayjs().format('DD.MM.YYYY'));
});

test('set value to empty string if clear button is clicked', () => {
const { clearButton, props } = setup({ value: '1970-01-01' });
fireEvent.click(clearButton);
expect(props.onChange).toHaveBeenCalledWith('');
});
});
Loading