From 834240e5d4f17e0a9a0e63757dd4e72977330fa6 Mon Sep 17 00:00:00 2001 From: Sigridur Osp Green Sigurdardottir Date: Mon, 18 Jul 2022 07:50:36 +0100 Subject: [PATCH] refactor: SuggestionsOverlay.js functional component (#604) * SuggestionsOverlay converted to hooks * issue with crash over foreign characters fixed * changeset added * console log removed --- .changeset/poor-monkeys-doubt.md | 5 + src/SuggestionsOverlay.js | 235 ++++++++++++++----------------- 2 files changed, 113 insertions(+), 127 deletions(-) create mode 100644 .changeset/poor-monkeys-doubt.md diff --git a/.changeset/poor-monkeys-doubt.md b/.changeset/poor-monkeys-doubt.md new file mode 100644 index 00000000..b14a18a5 --- /dev/null +++ b/.changeset/poor-monkeys-doubt.md @@ -0,0 +1,5 @@ +--- +"react-mentions": patch +--- + +SuggestionsOverlay.js refactored to functional component diff --git a/src/SuggestionsOverlay.js b/src/SuggestionsOverlay.js index 8f950d56..154648df 100644 --- a/src/SuggestionsOverlay.js +++ b/src/SuggestionsOverlay.js @@ -1,4 +1,4 @@ -import React, { Component, Children } from 'react' +import React, { Children, useState, useEffect } from 'react' import PropTypes from 'prop-types' import { inline } from 'substyle' import { defaultStyle } from './utils' @@ -7,181 +7,162 @@ import { getSuggestionHtmlId } from './utils' import Suggestion from './Suggestion' import LoadingIndicator from './LoadingIndicator' -class SuggestionsOverlay extends Component { - static propTypes = { - id: PropTypes.string.isRequired, - suggestions: PropTypes.object.isRequired, - a11ySuggestionsListLabel: PropTypes.string, - focusIndex: PropTypes.number, - position: PropTypes.string, - left: PropTypes.number, - right: PropTypes.number, - top: PropTypes.number, - scrollFocusedIntoView: PropTypes.bool, - isLoading: PropTypes.bool, - isOpened: PropTypes.bool.isRequired, - onSelect: PropTypes.func, - ignoreAccents: PropTypes.bool, - containerRef: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ - current: - typeof Element === 'undefined' - ? PropTypes.any - : PropTypes.instanceOf(Element), - }), - ]), - - children: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.arrayOf(PropTypes.element), - ]).isRequired, - } - - static defaultProps = { - suggestions: {}, - onSelect: () => null, - } - - componentDidUpdate() { +function SuggestionsOverlay({ + id, + suggestions = {}, + a11ySuggestionsListLabel, + focusIndex, + position, + left, + right, + top, + scrollFocusedIntoView, + isLoading, + isOpened, + onSelect = () => null, + ignoreAccents, + containerRef, + children, + style, + customSuggestionsContainer, + onMouseDown, + onMouseEnter, +}) { + const [ulElement, setUlElement] = useState() + + useEffect(() => { if ( - !this.ulElement || - this.ulElement.offsetHeight >= this.ulElement.scrollHeight || - !this.props.scrollFocusedIntoView + !ulElement || + ulElement.offsetHeight >= ulElement.scrollHeight || + !scrollFocusedIntoView ) { return } + const scrollTop = ulElement.scrollTop - const scrollTop = this.ulElement.scrollTop - let { top, bottom } = this.ulElement.children[ - this.props.focusIndex - ].getBoundingClientRect() - const { top: topContainer } = this.ulElement.getBoundingClientRect() + let { top, bottom } = ulElement.children[focusIndex].getBoundingClientRect() + const { top: topContainer } = ulElement.getBoundingClientRect() top = top - topContainer + scrollTop bottom = bottom - topContainer + scrollTop if (top < scrollTop) { - this.ulElement.scrollTop = top - } else if (bottom > this.ulElement.offsetHeight) { - this.ulElement.scrollTop = bottom - this.ulElement.offsetHeight + ulElement.scrollTop = top + } else if (bottom > ulElement.offsetHeight) { + ulElement.scrollTop = bottom - ulElement.offsetHeight } - } - - render() { - const { - id, - a11ySuggestionsListLabel, - isOpened, - style, - onMouseDown, - containerRef, - position, - left, - right, - top, - } = this.props - - // do not show suggestions until there is some data - if (!isOpened) { - return null - } - - return ( -
- - - {this.renderLoadingIndicator()} -
- ) - } + }, []) - renderSuggestions() { - const {customSuggestionsContainer} = this.props; - const suggestions = Object.values(this.props.suggestions).reduce( + const renderSuggestions = () => { + const suggestionsToRender = Object.values(suggestions).reduce( (accResults, { results, queryInfo }) => [ ...accResults, ...results.map((result, index) => - this.renderSuggestion(result, queryInfo, accResults.length + index) + renderSuggestion(result, queryInfo, accResults.length + index) ), ], [] - ); + ) - if(customSuggestionsContainer) - return customSuggestionsContainer(suggestions); - else - return suggestions; + if (customSuggestionsContainer) + return customSuggestionsContainer(suggestionsToRender) + else return suggestionsToRender } - renderSuggestion(result, queryInfo, index) { - const isFocused = index === this.props.focusIndex + const renderSuggestion = (result, queryInfo, index) => { + const isFocused = index === focusIndex const { childIndex, query } = queryInfo - const { renderSuggestion } = Children.toArray(this.props.children)[ - childIndex - ].props - const { ignoreAccents } = this.props + const { renderSuggestion } = Children.toArray(children)[childIndex].props return ( this.select(result, queryInfo)} - onMouseEnter={() => this.handleMouseEnter(index)} + onClick={() => select(result, queryInfo)} + onMouseEnter={() => handleMouseEnter(index)} /> ) } - renderLoadingIndicator() { - if (!this.props.isLoading) { + const renderLoadingIndicator = () => { + if (!isLoading) { return } - return + return } - handleMouseEnter = (index, ev) => { - if (this.props.onMouseEnter) { - this.props.onMouseEnter(index) + const handleMouseEnter = (index, ev) => { + if (onMouseEnter) { + onMouseEnter(index) } } - select = (suggestion, queryInfo) => { - this.props.onSelect(suggestion, queryInfo) + const select = (suggestion, queryInfo) => { + onSelect(suggestion, queryInfo) } - setUlElement = (el) => { - this.ulElement = el + const getID = (suggestion) => { + if (typeof suggestion === 'string') { + return suggestion + } + return suggestion.id } -} -const getID = (suggestion) => { - if (typeof suggestion === 'string') { - return suggestion + if (!isOpened) { + return null } - return suggestion.id + return ( +
+
    + {renderSuggestions()} +
+ {renderLoadingIndicator()} +
+ ) +} + +SuggestionsOverlay.propTypes = { + id: PropTypes.string.isRequired, + suggestions: PropTypes.object.isRequired, + a11ySuggestionsListLabel: PropTypes.string, + focusIndex: PropTypes.number, + position: PropTypes.string, + left: PropTypes.number, + right: PropTypes.number, + top: PropTypes.number, + scrollFocusedIntoView: PropTypes.bool, + isLoading: PropTypes.bool, + isOpened: PropTypes.bool.isRequired, + onSelect: PropTypes.func, + ignoreAccents: PropTypes.bool, + customSuggestionsContainer: PropTypes.any, + containerRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ + current: + typeof Element === 'undefined' + ? PropTypes.any + : PropTypes.instanceOf(Element), + }), + ]), } const styled = defaultStyle({