diff --git a/packages/volto/news/6213.feature b/packages/volto/news/6213.feature new file mode 100644 index 0000000000..76fbceecdc --- /dev/null +++ b/packages/volto/news/6213.feature @@ -0,0 +1,2 @@ + +Refactor the `DatetimeWidget` component from a class component to a functional component. @Raman-Luhach diff --git a/packages/volto/src/components/manage/Widgets/DatetimeWidget.jsx b/packages/volto/src/components/manage/Widgets/DatetimeWidget.jsx index 546f57e22a..bfa4cfe6fb 100644 --- a/packages/volto/src/components/manage/Widgets/DatetimeWidget.jsx +++ b/packages/volto/src/components/manage/Widgets/DatetimeWidget.jsx @@ -1,12 +1,6 @@ -/** - * 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 } from '@plone/volto/components'; @@ -50,6 +44,7 @@ const PrevIcon = () => ( ); + const NextIcon = () => (
{ + const { + id, + resettable, + reactDates, + widgetOptions, + moment, + value, + onChange, + dateOnly, + widget, + noPastDates: propNoPastDates, + isDisabled, + } = 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]); - getDateOnly() { - return this.props.dateOnly || this.props.widget === 'date'; - } + const getInternalValue = () => { + return parseDateTime(toBackendLang(lang), value, undefined, moment.default); + }; + + const getDateOnly = () => { + return dateOnly || 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(), @@ -142,125 +122,100 @@ 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; - - return ( - -
+ const onFocusChange = ({ focused }) => setFocused(focused); + + const noPastDates = + propNoPastDates || widgetOptions?.pattern_options?.noPastDates; + const datetime = getInternalValue(); + const isDateOnly = getDateOnly(); + + return ( + +
+
+ false })} + onFocusChange={onFocusChange} + noBorder + displayFormat={moment.default + .localeData(toBackendLang(lang)) + .longDateFormat('L')} + navPrev={} + navNext={} + id={`${id}-date`} + placeholder={intl.formatMessage(messages.date)} + /> +
+ {!isDateOnly && (
- false })} - onFocusChange={this.onFocusChange} - noBorder - displayFormat={moment + } - navNext={} - id={`${id}-date`} - placeholder={intl.formatMessage(messages.date)} + .longDateFormat('LT')} + placeholder={intl.formatMessage(messages.time)} + focusOnOpen + placement="bottomRight" />
- {!dateOnly && ( -
- -
- )} - {resettable && ( - - )} -
-
- ); - } -} + )} + {resettable && ( + + )} +
+
+ ); +}; -/** - * Property types. - * @property {Object} propTypes Property types. - * @static - */ DatetimeWidgetComponent.propTypes = { id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, @@ -275,11 +230,6 @@ DatetimeWidgetComponent.propTypes = { resettable: PropTypes.bool, }; -/** - * Default properties. - * @property {Object} defaultProps Default properties. - * @static - */ DatetimeWidgetComponent.defaultProps = { description: null, required: false, @@ -290,10 +240,6 @@ DatetimeWidgetComponent.defaultProps = { resettable: true, }; -export default compose( - injectLazyLibs(['reactDates', 'moment']), - connect((state) => ({ - lang: state.intl.locale, - })), - injectIntl, -)(DatetimeWidgetComponent); +export default injectLazyLibs(['reactDates', 'moment'])( + DatetimeWidgetComponent, +);