Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,17 @@ function utcStringToLocalDateValue(
if (!utcString) return null;

if (isDateOnly) {
return parseDate(utcString);
// Extract just the date portion in case the string is a full ISO datetime
const dateOnly = utcString.split('T')[0];
return parseDate(dateOnly);
}
try {
const localTimeZone = getLocalTimeZone();
return parseAbsolute(utcString, localTimeZone);
// If the value is a date-only string (no 'T'), convert to a full datetime
const dateTimeString = utcString.includes('T')
? utcString
: `${utcString}T12:00:00Z`;
return parseAbsolute(dateTimeString, localTimeZone);
} catch (error) {
console.warn('Failed to parse UTC string:', utcString, error);
return null;
Expand Down Expand Up @@ -84,6 +90,23 @@ export function DateTimePicker({
return utcStringToLocalDateValue(initialValue, isDateOnly(granularity));
});

// Ensure internalValue is compatible with current granularity synchronously
// to avoid react-aria throwing during render on granularity changes.
const resolvedValue = (() => {
if (value !== undefined) {
return utcStringToLocalDateValue(value, isDateOnly(granularity));
}
if (
internalValue &&
!isDateOnly(granularity) &&
!(internalValue instanceof ZonedDateTime)
) {
// Convert CalendarDate to ZonedDateTime when switching to minute granularity
return toZoned(internalValue, getLocalTimeZone());
}
return internalValue;
})();

useEffect(() => {
if (value !== undefined) {
setInternalValue(
Expand Down Expand Up @@ -138,7 +161,7 @@ export function DateTimePicker({
<div className="flex items-center">
<AriaDatePicker
{...props}
value={value !== undefined ? internalValue : internalValue}
value={resolvedValue}
defaultValue={
value === undefined
? utcStringToLocalDateValue(
Expand Down Expand Up @@ -171,7 +194,7 @@ export function DateTimePicker({
variant="icon"
className="h-7 w-7 flex-shrink-0 p-1"
onPress={handleReset}
isDisabled={isDisabled || !internalValue || props.isReadOnly}
isDisabled={isDisabled || !resolvedValue || props.isReadOnly}
aria-label="Clear date and time"
slot={null}
>
Expand Down
36 changes: 30 additions & 6 deletions packages/volto/cypress/tests/core/basic/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ describe('Add Content Tests', () => {
});
it('As an editor I can set the effective date of a page', function () {
cy.getSlateTitle().focus().click().type('My Page').contains('My Page');
cy.get('input#effective-date').click();
cy.get('input#effective-date').type('{selectall}12/24/2050{esc}');
cy.get('input#effective-time').type('{downarrow}');
cy.get('.rc-time-picker-panel-input').type('{selectall}10:00 AM{esc}');
// Set date using react-aria date segments
cy.get('.field-wrapper-effective [data-type="month"]').click().type('12');
cy.get('.field-wrapper-effective [data-type="day"]').click().type('24');
cy.get('.field-wrapper-effective [data-type="year"]').click().type('2050');
cy.get('.field-wrapper-effective [data-type="hour"]').click().type('3');
cy.get('.field-wrapper-effective [data-type="minute"]').click().type('30');
cy.get('.field-wrapper-effective [data-type="dayPeriod"]')
.click()
.type('PM');
cy.get('#toolbar-save').click();
cy.get('body.view-viewview #page-document .documentFirstHeading').should(
'have.text',
Expand All @@ -24,8 +29,27 @@ describe('Add Content Tests', () => {
cy.get('.edit').click();
cy.wait('@content');

cy.get('input#effective-date').should('have.value', '12/24/2050');
cy.get('input#effective-time').should('have.value', '10:00 AM');
// Verify the date segments have the correct values
cy.get('.field-wrapper-effective [data-type="month"]').should(
'have.text',
'12',
);
cy.get('.field-wrapper-effective [data-type="day"]').should(
'have.text',
'24',
);
cy.get('.field-wrapper-effective [data-type="year"]').should(
'have.text',
'2050',
);
cy.get('.field-wrapper-effective [data-type="hour"]').should(
'have.text',
'3',
);
cy.get('.field-wrapper-effective [data-type="minute"]').should(
'have.text',
'30',
);
});

it('As an editor, given a document with no title or a validation error when I save the sidebar tab switches to the metadata tab', function () {
Expand Down
16 changes: 12 additions & 4 deletions packages/volto/cypress/tests/core/basic/recurrence-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ const createEvent = () => {
cy.get('#toolbar-add-event').click();
cy.get('#field-title').type('Test recurrence');

cy.get('input#start-date').type('{selectall}05/04/2020{esc}'); //May,4 2020
cy.get('input#start-time').type('{selectall}01:00 AM{esc}');
cy.get('input#end-date').type('{selectall}05/16/2020{esc}'); //May,16 2020
cy.get('input#end-time').type('{selectall}01:00 AM{esc}');
// Set start date: May 4, 2020
cy.get('.field-wrapper-start [data-type="month"]').click().type('5');
cy.get('.field-wrapper-start [data-type="day"]').click().type('4');
cy.get('.field-wrapper-start [data-type="year"]').click().type('2020');
cy.get('.field-wrapper-start [data-type="hour"]').click().type('1');
cy.get('.field-wrapper-start [data-type="minute"]').click().type('00');
// Set end date: May 16, 2020
cy.get('.field-wrapper-end [data-type="month"]').click().type('5');
cy.get('.field-wrapper-end [data-type="day"]').click().type('16');
cy.get('.field-wrapper-end [data-type="year"]').click().type('2020');
cy.get('.field-wrapper-end [data-type="hour"]').click().type('1');
cy.get('.field-wrapper-end [data-type="minute"]').click().type('00');
};

const openRecurrenceModal = () => {
Expand Down
55 changes: 31 additions & 24 deletions packages/volto/cypress/tests/core/content/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ describe('Add Content Tests', () => {
// because of lazyloading wait for the element to reach an actionable state
.clear()
.type('datetimeWidget test');
cy.get('#start-time').click();
cy.get('.rc-time-picker-panel-input').click();
cy.get('.rc-time-picker-panel-input').clear().type('6:40 AM');
// Set time using react-aria date segments
cy.get('.field-wrapper-start [data-type="hour"]').click().type('6');
cy.get('.field-wrapper-start [data-type="minute"]').click().type('40');
cy.get('#toolbar-save').click();

// then
Expand All @@ -170,38 +170,45 @@ describe('Add Content Tests', () => {
cy.get('#toolbar-add-event').click();
cy.get('#field-title').clear().type('Event checkbox test');

// then the time fields should be visible initially
cy.get('#start-time').should('be.visible');
cy.get('#end-time').should('be.visible');
cy.get('#end-date').should('be.visible');
// then the time segments should be visible initially
cy.get('.field-wrapper-start [data-type="hour"]').should('be.visible');
cy.get('.field-wrapper-end [data-type="hour"]').should('be.visible');
cy.get('.field-wrapper-end').should('be.visible');

// when I check the whole_day checkbox
cy.get('label[for="field-whole_day"]').click({ scrollBehavior: false });
cy.get('.field-wrapper-whole_day label').click();

// then the time fields should disappear
cy.get('#start-time').should('not.exist');
cy.get('#end-time').should('not.exist');
// then the time segments should disappear (granularity switches to day)
cy.get('.field-wrapper-start [data-type="hour"]').should('not.exist');
cy.get('.field-wrapper-end [data-type="hour"]').should('not.exist');

// when I uncheck the whole_day checkbox
cy.get('label[for="field-whole_day"]').click({ scrollBehavior: false });
cy.get('.field-wrapper-whole_day label', { timeout: 10000 }).click();

// then the time fields should be visible again
cy.get('#start-time').should('be.visible');
cy.get('#end-time').should('be.visible');
// then the time segments should be visible again
cy.get('.field-wrapper-start [data-type="hour"]')
.scrollIntoView()
.should('be.visible');
cy.get('.field-wrapper-end [data-type="hour"]')
.scrollIntoView()
.should('be.visible');

// when I check the open_end checkbox
cy.get('label[for="field-open_end"]').click({ scrollBehavior: false });
cy.get('.field-wrapper-open_end label').scrollIntoView().click();

// then the end-date and end-time fields should disappear
cy.get('#end-date').should('not.exist');
cy.get('#end-time').should('not.exist');
// then the end field should disappear entirely
cy.get('.field-wrapper-end').should('not.exist');

// when I uncheck the open_end checkbox
cy.get('label[for="field-open_end"]').click({ scrollBehavior: false });

// then the end-date and end-time fields should be visible again
cy.get('#end-date').should('be.visible');
cy.get('#end-time').should('be.visible');
cy.get('.field-wrapper-open_end label', { timeout: 10000 })
.scrollIntoView()
.click();

// then the end field should be visible again
cy.get('.field-wrapper-end').scrollIntoView().should('be.visible');
cy.get('.field-wrapper-end [data-type="hour"]')
.scrollIntoView()
.should('be.visible');
});

it('As editor I can add a Link (with an external link)', function () {
Expand Down
6 changes: 3 additions & 3 deletions packages/volto/cypress/tests/minimal/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ describe('Add Content Tests', () => {
// because of lazyloading wait for the element to reach an actionable state
.clear()
.type('datetimeWidget test');
cy.get('#start-time').click();
cy.get('.rc-time-picker-panel-input').click();
cy.get('.rc-time-picker-panel-input').clear().type('6:40 AM');
// Set time using react-aria date segments
cy.get('.field-wrapper-start [data-type="hour"]').click().type('6');
cy.get('.field-wrapper-start [data-type="minute"]').click().type('40');
cy.get('#toolbar-save').click();

// then
Expand Down
7 changes: 0 additions & 7 deletions packages/volto/locales/af/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,6 @@ msgstr ""
msgid "Clear"
msgstr ""

#. Default: "Clear date and time"
#: components/manage/Widgets/DatetimeWidget
msgid "Clear date/time"
msgstr ""

#. Default: "Clear filters"
#: components/manage/Blocks/Search/components/FilterList
msgid "Clear filters"
Expand Down Expand Up @@ -1016,7 +1011,6 @@ msgstr ""
#. Default: "Date"
#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ModerateComments
#: components/manage/Widgets/DatetimeWidget
msgid "Date"
msgstr ""

Expand Down Expand Up @@ -4039,7 +4033,6 @@ msgid "This rule is assigned to the following locations:"
msgstr ""

#. Default: "Time"
#: components/manage/Widgets/DatetimeWidget
#: components/manage/Widgets/TimeWidget
msgid "Time"
msgstr ""
Expand Down
7 changes: 0 additions & 7 deletions packages/volto/locales/ar/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,6 @@ msgstr ""
msgid "Clear"
msgstr ""

#. Default: "Clear date and time"
#: components/manage/Widgets/DatetimeWidget
msgid "Clear date/time"
msgstr ""

#. Default: "Clear filters"
#: components/manage/Blocks/Search/components/FilterList
msgid "Clear filters"
Expand Down Expand Up @@ -1016,7 +1011,6 @@ msgstr ""
#. Default: "Date"
#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ModerateComments
#: components/manage/Widgets/DatetimeWidget
msgid "Date"
msgstr ""

Expand Down Expand Up @@ -4039,7 +4033,6 @@ msgid "This rule is assigned to the following locations:"
msgstr ""

#. Default: "Time"
#: components/manage/Widgets/DatetimeWidget
#: components/manage/Widgets/TimeWidget
msgid "Time"
msgstr ""
Expand Down
7 changes: 0 additions & 7 deletions packages/volto/locales/bg/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,6 @@ msgstr ""
msgid "Clear"
msgstr ""

#. Default: "Clear date and time"
#: components/manage/Widgets/DatetimeWidget
msgid "Clear date/time"
msgstr ""

#. Default: "Clear filters"
#: components/manage/Blocks/Search/components/FilterList
msgid "Clear filters"
Expand Down Expand Up @@ -1016,7 +1011,6 @@ msgstr ""
#. Default: "Date"
#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ModerateComments
#: components/manage/Widgets/DatetimeWidget
msgid "Date"
msgstr ""

Expand Down Expand Up @@ -4039,7 +4033,6 @@ msgid "This rule is assigned to the following locations:"
msgstr ""

#. Default: "Time"
#: components/manage/Widgets/DatetimeWidget
#: components/manage/Widgets/TimeWidget
msgid "Time"
msgstr ""
Expand Down
7 changes: 0 additions & 7 deletions packages/volto/locales/bn/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,6 @@ msgstr ""
msgid "Clear"
msgstr ""

#. Default: "Clear date and time"
#: components/manage/Widgets/DatetimeWidget
msgid "Clear date/time"
msgstr ""

#. Default: "Clear filters"
#: components/manage/Blocks/Search/components/FilterList
msgid "Clear filters"
Expand Down Expand Up @@ -1016,7 +1011,6 @@ msgstr ""
#. Default: "Date"
#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ModerateComments
#: components/manage/Widgets/DatetimeWidget
msgid "Date"
msgstr ""

Expand Down Expand Up @@ -4039,7 +4033,6 @@ msgid "This rule is assigned to the following locations:"
msgstr ""

#. Default: "Time"
#: components/manage/Widgets/DatetimeWidget
#: components/manage/Widgets/TimeWidget
msgid "Time"
msgstr ""
Expand Down
7 changes: 0 additions & 7 deletions packages/volto/locales/ca/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -699,11 +699,6 @@ msgstr "Trieu un fitxer"
msgid "Clear"
msgstr "Clar"

#. Default: "Clear date and time"
#: components/manage/Widgets/DatetimeWidget
msgid "Clear date/time"
msgstr ""

#. Default: "Clear filters"
#: components/manage/Blocks/Search/components/FilterList
msgid "Clear filters"
Expand Down Expand Up @@ -1017,7 +1012,6 @@ msgstr "Base de dades principal"
#. Default: "Date"
#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ModerateComments
#: components/manage/Widgets/DatetimeWidget
msgid "Date"
msgstr "Data"

Expand Down Expand Up @@ -4040,7 +4034,6 @@ msgid "This rule is assigned to the following locations:"
msgstr "Aquesta regla està assignada a les ubicacions següents:"

#. Default: "Time"
#: components/manage/Widgets/DatetimeWidget
#: components/manage/Widgets/TimeWidget
msgid "Time"
msgstr "Hora"
Expand Down
7 changes: 0 additions & 7 deletions packages/volto/locales/cs/LC_MESSAGES/volto.po
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,6 @@ msgstr ""
msgid "Clear"
msgstr ""

#. Default: "Clear date and time"
#: components/manage/Widgets/DatetimeWidget
msgid "Clear date/time"
msgstr ""

#. Default: "Clear filters"
#: components/manage/Blocks/Search/components/FilterList
msgid "Clear filters"
Expand Down Expand Up @@ -1016,7 +1011,6 @@ msgstr ""
#. Default: "Date"
#: components/manage/Controlpanels/Aliases
#: components/manage/Controlpanels/ModerateComments
#: components/manage/Widgets/DatetimeWidget
msgid "Date"
msgstr ""

Expand Down Expand Up @@ -4039,7 +4033,6 @@ msgid "This rule is assigned to the following locations:"
msgstr ""

#. Default: "Time"
#: components/manage/Widgets/DatetimeWidget
#: components/manage/Widgets/TimeWidget
msgid "Time"
msgstr ""
Expand Down
Loading
Loading