From 0dc4e41a2b6ead88812ea2f2a8a1de3bbe3c2429 Mon Sep 17 00:00:00 2001 From: Raman-Luhach Date: Sun, 28 Jul 2024 22:16:44 +0530 Subject: [PATCH 1/2] Refactored RefactorObjectBrowserWidget --- packages/volto/news/6215.feature | 1 + .../manage/Widgets/ObjectBrowserWidget.jsx | 388 ++++++++---------- 2 files changed, 168 insertions(+), 221 deletions(-) create mode 100644 packages/volto/news/6215.feature diff --git a/packages/volto/news/6215.feature b/packages/volto/news/6215.feature new file mode 100644 index 0000000000..8437ff7bef --- /dev/null +++ b/packages/volto/news/6215.feature @@ -0,0 +1 @@ +Refactored ObjectBrowserWidget.jsx from class-based to functional-based component. @Raman-Luhach diff --git a/packages/volto/src/components/manage/Widgets/ObjectBrowserWidget.jsx b/packages/volto/src/components/manage/Widgets/ObjectBrowserWidget.jsx index 566928561c..9b300212b7 100644 --- a/packages/volto/src/components/manage/Widgets/ObjectBrowserWidget.jsx +++ b/packages/volto/src/components/manage/Widgets/ObjectBrowserWidget.jsx @@ -1,9 +1,4 @@ -/** - * ObjectBrowserWidget component. - * @module components/manage/Widgets/ObjectBrowserWidget - */ - -import React, { Component } from 'react'; +import React, { useState, useRef } from 'react'; import PropTypes from 'prop-types'; import { compose } from 'redux'; import { compact, includes, isArray, isEmpty, remove } from 'lodash'; @@ -49,63 +44,14 @@ const messages = defineMessages({ }, }); -/** - * ObjectBrowserWidget component class. - * @class ObjectBrowserWidget - * @extends Component - */ -export class ObjectBrowserWidgetComponent extends Component { - /** - * Property types. - * @property {Object} propTypes Property types. - * @static - */ - static propTypes = { - id: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - description: PropTypes.string, - mode: PropTypes.string, // link, image, multiple - return: PropTypes.string, // single, multiple - initialPath: PropTypes.string, - required: PropTypes.bool, - error: PropTypes.arrayOf(PropTypes.string), - value: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.object), - PropTypes.object, - ]), - onChange: PropTypes.func.isRequired, - openObjectBrowser: PropTypes.func.isRequired, - allowExternals: PropTypes.bool, - placeholder: PropTypes.string, - }; +const ObjectBrowserWidgetComponent = (props) => { + const [manualLinkInput, setManualLinkInput] = useState(''); + const [validURL, setValidURL] = useState(false); - /** - * Default properties - * @property {Object} defaultProps Default properties. - * @static - */ - static defaultProps = { - description: null, - required: false, - error: [], - value: [], - mode: 'multiple', - return: 'multiple', - initialPath: '', - allowExternals: false, - }; + const selectedItemsRef = useRef(null); + const placeholderRef = useRef(null); - state = { - manualLinkInput: '', - validURL: false, - }; - - constructor(props) { - super(props); - this.selectedItemsRef = React.createRef(); - this.placeholderRef = React.createRef(); - } - renderLabel(item) { + const renderLabel = (item) => { const href = item['@id']; return (
- {this.props.mode === 'multiple' && ( + {props.mode === 'multiple' && ( { event.preventDefault(); - this.removeItem(item); + removeItem(item); }} /> )} @@ -150,26 +96,24 @@ export class ObjectBrowserWidgetComponent extends Component { } /> ); - } + }; - removeItem = (item) => { - let value = [...this.props.value]; + const removeItem = (item) => { + let value = [...props.value]; remove(value, function (_item) { return _item['@id'] === item['@id']; }); - this.props.onChange(this.props.id, value); + props.onChange(props.id, value); }; - onChange = (item) => { + const handleChange = (item) => { let value = - this.props.mode === 'multiple' && this.props.value - ? [...this.props.value] - : []; + props.mode === 'multiple' && props.value ? [...props.value] : []; value = value.filter((item) => item != null); const maxSize = - this.props.widgetOptions?.pattern_options?.maximumSelectionSize || -1; + props.widgetOptions?.pattern_options?.maximumSelectionSize || -1; if (maxSize === 1 && value.length === 1) { - value = []; //enable replace of selected item with another value, if maxsize is 1 + value = []; } let exists = false; let index = -1; @@ -179,20 +123,10 @@ export class ObjectBrowserWidgetComponent extends Component { index = _index; } }); - //find(value, { - // '@id': flattenToAppURL(item['@id']), - // }); if (!exists) { - // add item - // Check if we want to filter the attributes of the selected item let resultantItem = item; - if (this.props.selectedItemAttrs) { - const allowedItemKeys = [ - ...this.props.selectedItemAttrs, - // Add the required attributes for the widget to work - '@id', - 'title', - ]; + if (props.selectedItemAttrs) { + const allowedItemKeys = [...props.selectedItemAttrs, '@id', 'title']; resultantItem = Object.keys(item) .filter((key) => allowedItemKeys.includes(key)) .reduce((obj, key) => { @@ -200,60 +134,57 @@ export class ObjectBrowserWidgetComponent extends Component { return obj; }, {}); } - // Add required @id field, just in case resultantItem = { ...resultantItem, '@id': item['@id'] }; value.push(resultantItem); - if (this.props.return === 'single') { - this.props.onChange(this.props.id, value[0]); + if (props.return === 'single') { + props.onChange(props.id, value[0]); } else { - this.props.onChange(this.props.id, value); + props.onChange(props.id, value); } } else { - //remove item value.splice(index, 1); - this.props.onChange(this.props.id, value); + props.onChange(props.id, value); } }; - onManualLinkInput = (e) => { - this.setState({ manualLinkInput: e.target.value }); - if (this.validateManualLink(e.target.value)) { - this.setState({ validURL: true }); + const onManualLinkInput = (e) => { + setManualLinkInput(e.target.value); + if (validateManualLink(e.target.value)) { + setValidURL(true); } else { - this.setState({ validURL: false }); + setValidURL(false); } }; - validateManualLink = (url) => { - if (this.props.allowExternals) { + const validateManualLink = (url) => { + if (props.allowExternals) { return isUrl(url); } else { return isInternalURL(url); } }; - onSubmitManualLink = () => { - if (this.validateManualLink(this.state.manualLinkInput)) { - if (isInternalURL(this.state.manualLinkInput)) { - const link = this.state.manualLinkInput; - // convert it into an internal on if possible - this.props + const onSubmitManualLink = () => { + if (validateManualLink(manualLinkInput)) { + if (isInternalURL(manualLinkInput)) { + const link = manualLinkInput; + props .searchContent( '/', { - 'path.query': flattenToAppURL(this.state.manualLinkInput), + 'path.query': flattenToAppURL(manualLinkInput), 'path.depth': '0', sort_on: 'getObjPositionInParent', metadata_fields: '_all', b_size: 1000, }, - `${this.props.block}-${this.props.mode}`, + `${props.block}-${props.mode}`, ) .then((resp) => { if (resp.items?.length > 0) { - this.onChange(resp.items[0]); + handleChange(resp.items[0]); } else { - this.props.onChange(this.props.id, [ + props.onChange(props.id, [ { '@id': flattenToAppURL(link), title: removeProtocol(link), @@ -262,22 +193,23 @@ export class ObjectBrowserWidgetComponent extends Component { } }); } else { - this.props.onChange(this.props.id, [ + props.onChange(props.id, [ { - '@id': normalizeUrl(this.state.manualLinkInput), - title: removeProtocol(this.state.manualLinkInput), + '@id': normalizeUrl(manualLinkInput), + title: removeProtocol(manualLinkInput), }, ]); } - this.setState({ validURL: true, manualLinkInput: '' }); + setValidURL(true); + setManualLinkInput(''); } }; - onKeyDownManualLink = (e) => { + const onKeyDownManualLink = (e) => { if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); - this.onSubmitManualLink(); + onSubmitManualLink(); } else if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); @@ -285,141 +217,154 @@ export class ObjectBrowserWidgetComponent extends Component { } }; - showObjectBrowser = (ev) => { + const showObjectBrowser = (ev) => { ev.preventDefault(); - this.props.openObjectBrowser({ - mode: this.props.mode, - currentPath: this.props.initialPath || this.props.location.pathname, + props.openObjectBrowser({ + mode: props.mode, + currentPath: props.initialPath || props.location.pathname, propDataName: 'value', onSelectItem: (url, item) => { - this.onChange(item); + handleChange(item); }, selectableTypes: - this.props.widgetOptions?.pattern_options?.selectableTypes || - this.props.selectableTypes, + props.widgetOptions?.pattern_options?.selectableTypes || + props.selectableTypes, maximumSelectionSize: - this.props.widgetOptions?.pattern_options?.maximumSelectionSize || - this.props.maximumSelectionSize, + props.widgetOptions?.pattern_options?.maximumSelectionSize || + props.maximumSelectionSize, }); }; - handleSelectedItemsRefClick = (e) => { - if (this.props.isDisabled) { + const handleSelectedItemsRefClick = (e) => { + if (props.isDisabled) { return; } if ( - e.target.contains(this.selectedItemsRef.current) || - e.target.contains(this.placeholderRef.current) + e.target.contains(selectedItemsRef.current) || + e.target.contains(placeholderRef.current) ) { - this.showObjectBrowser(e); + showObjectBrowser(e); } }; - /** - * Render method. - * @method render - * @returns {string} Markup for the component. - */ - render() { - const { id, description, fieldSet, value, mode, onChange, isDisabled } = - this.props; + const { id, description, fieldSet, value, mode, onChange, isDisabled, intl } = + props; - let items = compact(!isArray(value) && value ? [value] : value || []); + let items = compact(!isArray(value) && value ? [value] : value || []); - let icon = - mode === 'multiple' || items.length === 0 ? navTreeSVG : clearSVG; - let iconAction = - mode === 'multiple' || items.length === 0 - ? this.showObjectBrowser - : (e) => { - e.preventDefault(); - onChange(id, this.props.return === 'single' ? null : []); - }; + let icon = mode === 'multiple' || items.length === 0 ? navTreeSVG : clearSVG; + let iconAction = + mode === 'multiple' || items.length === 0 + ? showObjectBrowser + : (e) => { + e.preventDefault(); + onChange(id, props.return === 'single' ? null : []); + }; - return ( - +
-
- {items.map((item) => this.renderLabel(item))} + {items.map((item) => renderLabel(item))} - {items.length === 0 && this.props.mode === 'multiple' && ( -
- {this.props.placeholder ?? - this.props.intl.formatMessage(messages.placeholder)} -
- )} - {this.props.allowExternals && - items.length === 0 && - this.props.mode !== 'multiple' && ( - - )} -
- {this.state.manualLinkInput && isEmpty(items) && ( - - - - + {items.length === 0 && props.mode === 'multiple' && ( +
+ {props.placeholder ?? intl.formatMessage(messages.placeholder)} +
)} - {!this.state.manualLinkInput && ( + {props.allowExternals && + items.length === 0 && + props.mode !== 'multiple' && ( + + )} +
+ {manualLinkInput && isEmpty(items) && ( + - )} -
-
- ); - } -} + + + )} + {!manualLinkInput && ( + + )} +
+ + ); +}; + +ObjectBrowserWidgetComponent.propTypes = { + id: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + description: PropTypes.string, + mode: PropTypes.string, + return: PropTypes.string, + initialPath: PropTypes.string, + required: PropTypes.bool, + error: PropTypes.arrayOf(PropTypes.string), + value: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.object), + PropTypes.object, + ]), + onChange: PropTypes.func.isRequired, + openObjectBrowser: PropTypes.func.isRequired, + allowExternals: PropTypes.bool, + placeholder: PropTypes.string, +}; + +ObjectBrowserWidgetComponent.defaultProps = { + description: null, + required: false, + error: [], + value: [], + mode: 'multiple', + return: 'multiple', + initialPath: '', + allowExternals: false, +}; const ObjectBrowserWidgetMode = (mode) => compose( @@ -428,6 +373,7 @@ const ObjectBrowserWidgetMode = (mode) => withRouter, connect(null, { searchContent }), )((props) => ); + export { ObjectBrowserWidgetMode }; export default compose( injectIntl, From 5730ca86200e5ea74825e8c0a9c77988812ecda7 Mon Sep 17 00:00:00 2001 From: Raman-Luhach <150381737+Raman-Luhach@users.noreply.github.com> Date: Mon, 29 Jul 2024 00:58:39 +0530 Subject: [PATCH 2/2] Update packages/volto/news/6215.feature Co-authored-by: Steve Piercy --- packages/volto/news/6215.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/volto/news/6215.feature b/packages/volto/news/6215.feature index 8437ff7bef..4ddf832c0f 100644 --- a/packages/volto/news/6215.feature +++ b/packages/volto/news/6215.feature @@ -1 +1 @@ -Refactored ObjectBrowserWidget.jsx from class-based to functional-based component. @Raman-Luhach +Refactored `ObjectBrowserWidget` from class-based to functional-based component. @Raman-Luhach