diff --git a/src/customizations/volto/components/manage/Widgets/DatetimeWidget.jsx b/src/customizations/volto/components/manage/Widgets/DatetimeWidget.jsx
index fbb15bdbf..ca94bc2f1 100644
--- a/src/customizations/volto/components/manage/Widgets/DatetimeWidget.jsx
+++ b/src/customizations/volto/components/manage/Widgets/DatetimeWidget.jsx
@@ -2,26 +2,27 @@
* DatetimeWidget component.
* @module components/manage/Widgets/DatetimeWidget
*
- * https://github.com/plone/volto/blob/16.x.x/src/components/manage/Widgets/DatetimeWidget.jsx
- * https://github.com/plone/volto/blob/7ec45f7b4d46233236c651f39a951bad8e389184/src/components/manage/Widgets/DatetimeWidget.jsx
+ * https://github.com/plone/volto/blob/main/packages/volto/src/components/manage/Widgets/DatetimeWidget.jsx
*
* CUSTOMIZATIONS:
* - accept calendar `openDirection` prop and use it in SingleDatePicker,
* default to down if no openDirection is given
+ * - requirement aria attributes added to date and time inputs - To be backported https://github.com/plone/volto/pull/7494
*/
+
/**
* DatetimeWidget component.
* @module components/manage/Widgets/DatetimeWidget
*/
-import React, { Component } from 'react';
-import { compose } from 'redux';
+
+import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
+import { defineMessages, useIntl } from 'react-intl';
import loadable from '@loadable/component';
import cx from 'classnames';
-import { Icon, FormFieldWrapper } from '@plone/volto/components';
-import { parseDateTime, toBackendLang } from '@plone/volto/helpers';
+import Icon from '@plone/volto/components/theme/Icon/Icon';
+import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
+import { parseDateTime, toBackendLang } from '@plone/volto/helpers/Utils/Utils';
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
import leftKey from '@plone/volto/icons/left-key.svg';
@@ -43,6 +44,10 @@ const messages = defineMessages({
id: 'Time',
defaultMessage: 'Time',
},
+ clearDateTime: {
+ id: 'Clear date/time',
+ defaultMessage: 'Clear date and time',
+ },
});
const PrevIcon = () => (
@@ -60,6 +65,7 @@ const PrevIcon = () => (
);
+
const NextIcon = () => (
{
+ const {
+ id,
+ resettable,
+ reactDates,
+ widgetOptions,
+ moment,
+ value,
+ onChange,
+ dateOnly,
+ widget,
+ noPastDates: propNoPastDates,
+ isDisabled,
+ formData,
+ openDirection, // ✅ adicionada
+ } = props;
+
+ const intl = useIntl();
+ const lang = intl.locale;
+
+ const [focused, setFocused] = useState(false);
+ const [isDefault, setIsDefault] = useState(false);
+
+ const { SingleDatePicker } = reactDates;
+
+ useEffect(() => {
+ const parsedDateTime = parseDateTime(
+ toBackendLang(lang),
+ value,
undefined,
- this.moment,
+ moment.default,
+ );
+ setIsDefault(
+ parsedDateTime?.toISOString() === moment.default().utc().toISOString(),
+ );
+ }, [value, lang, moment]);
+
+ const getInternalValue = () => {
+ return parseDateTime(toBackendLang(lang), value, undefined, moment.default);
+ };
+
+ const getDateOnly = () => {
+ return (
+ dateOnly ||
+ widget === 'date' ||
+ ((id === 'start' || id === 'end') && formData?.whole_day)
);
- }
-
- getDateOnly() {
- return this.props.dateOnly || this.props.widget === 'date';
- }
-
- /**
- * Update date storage
- * @method onDateChange
- * @param {Object} date updated momentjs Object for date
- * @returns {undefined}
- */
- onDateChange = (date) => {
+ };
+
+ const onDateChange = (date) => {
if (date) {
- const moment = this.props.moment.default;
- const isDateOnly = this.getDateOnly();
- const base = (this.getInternalValue() || moment()).set({
+ const isDateOnly = getDateOnly();
+ const base = (getInternalValue() || moment.default()).set({
year: date.year(),
month: date.month(),
date: date.date(),
@@ -152,96 +149,135 @@ export class DatetimeWidgetComponent extends Component {
const dateValue = isDateOnly
? base.format('YYYY-MM-DD')
: base.toISOString();
- this.props.onChange(this.props.id, dateValue);
+ onChange(id, dateValue);
}
- this.setState({ isDefault: false });
+ setIsDefault(false);
};
- /**
- * Update date storage
- * @method onTimeChange
- * @param {Object} time updated momentjs Object for time
- * @returns {undefined}
- */
- onTimeChange = (time) => {
- const moment = this.props.moment.default;
+ const onTimeChange = (time) => {
if (time) {
- const base = (this.getInternalValue() || moment()).set({
+ const base = (getInternalValue() || moment.default()).set({
hours: time?.hours() ?? 0,
minutes: time?.minutes() ?? 0,
seconds: 0,
});
const dateValue = base.toISOString();
- this.props.onChange(this.props.id, dateValue);
+ onChange(id, dateValue);
}
};
- onResetDates = () => {
- this.setState({ isDefault: false });
- this.props.onChange(this.props.id, null);
+ const onResetDates = () => {
+ setIsDefault(false);
+ onChange(id, null);
};
- /**
- * Handle SingleDatePicker focus
- * @method onFocusChange
- * @param {boolean} focused component focus state.
- * @returns {undefined}
- */
- onFocusChange = ({ focused }) => this.setState({ focused });
-
- render() {
- const { id, resettable, intl, reactDates, widgetOptions, lang } =
- this.props;
- const noPastDates =
- this.props.noPastDates || widgetOptions?.pattern_options?.noPastDates;
- const moment = this.props.moment.default;
- const datetime = this.getInternalValue();
- const dateOnly = this.getDateOnly();
- const { SingleDatePicker } = reactDates;
+ const onFocusChange = ({ focused }) => setFocused(focused);
- return (
-
+ const noPastDates =
+ propNoPastDates || widgetOptions?.pattern_options?.noPastDates;
+ const datetime = getInternalValue();
+ const isDateOnly = getDateOnly();
+
+ const renderWidget = !(id === 'end' && formData?.open_end);
+
+ //pezzo aggiunto per gestire aria-required - https://github.com/plone/volto/pull/7494
+ useEffect(() => {
+ const dateSelectors = [
+ `#${id}-date`,
+ `#${id}-date .DateInput_input`,
+ `#${id}-date input`,
+ `#${id}`,
+ `.DateInput_input#${id}`,
+ ];
+
+ const timeSelectors = [
+ `#${id}-time input`,
+ `#${id}-time .rc-time-picker-input`,
+ `.rc-time-picker-input#${id}-time`,
+ `.time-input #${id}-time`,
+ `.time-input .rc-time-picker-input`,
+ ];
+
+ function findInput(selectors) {
+ for (let selector of selectors) {
+ const item = document.querySelector(selector);
+ if (item && item.tagName === 'INPUT') return item;
+ if (item && item.querySelector) {
+ const inner = item.querySelector('input');
+ if (inner) return inner;
+ }
+ }
+ return null;
+ }
+
+ function applyAria() {
+ const dateInput = findInput(dateSelectors);
+ if (!dateInput) return;
+
+ if (props.required) dateInput.setAttribute('aria-required', 'true');
+ else dateInput.removeAttribute('aria-required');
+
+ if (props.required && !isDateOnly) {
+ const timeInput = findInput(timeSelectors);
+ if (timeInput) timeInput.setAttribute('aria-required', 'true');
+ }
+ }
+
+ applyAria();
+
+ const observer = new MutationObserver(applyAria);
+ observer.observe(document.body, { childList: true, subtree: true });
+
+ // Cleanup
+ return () => observer.disconnect();
+ }, [props.required, id, isDateOnly]);
+
+ // fine pezzo aggiunto per gestire aria-required
+
+ return (
+
+ {renderWidget && (
false })}
- onFocusChange={this.onFocusChange}
+ onFocusChange={onFocusChange}
noBorder
- displayFormat={moment
+ displayFormat={moment.default
.localeData(toBackendLang(lang))
.longDateFormat('L')}
navPrev={}
navNext={}
id={`${id}-date`}
placeholder={intl.formatMessage(messages.date)}
- openDirection={this.props.openDirection ?? 'down'}
+ openDirection={openDirection ?? 'down'}
/>
- {!dateOnly && (
+ {!isDateOnly && (
this.onResetDates()}
+ type="button"
+ disabled={isDisabled || !datetime}
+ onClick={onResetDates}
className="item ui noborder button"
+ aria-label={intl.formatMessage(messages.clearDateTime)}
>
)}
-
- );
- }
-}
+ )}
+
+ );
+};
-/**
- * Property types.
- * @property {Object} propTypes Property types.
- * @static
- */
DatetimeWidgetComponent.propTypes = {
id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
@@ -285,11 +318,6 @@ DatetimeWidgetComponent.propTypes = {
openDirection: PropTypes.oneOf(['up', 'down']),
};
-/**
- * Default properties.
- * @property {Object} defaultProps Default properties.
- * @static
- */
DatetimeWidgetComponent.defaultProps = {
description: null,
required: false,
@@ -301,10 +329,6 @@ DatetimeWidgetComponent.defaultProps = {
openDirection: 'down',
};
-export default compose(
- injectLazyLibs(['reactDates', 'moment']),
- connect((state) => ({
- lang: state.intl.locale,
- })),
- injectIntl,
-)(DatetimeWidgetComponent);
+export default injectLazyLibs(['reactDates', 'moment'])(
+ DatetimeWidgetComponent,
+);