From d00fc05e8844ced0747e4a942f21778ba942030b Mon Sep 17 00:00:00 2001 From: marlo-longley Date: Tue, 29 Oct 2024 16:22:18 -0500 Subject: [PATCH 1/2] Convert CompanionWindow to functional component --- src/components/CompanionWindow.js | 231 +++++++++++++++--------------- 1 file changed, 112 insertions(+), 119 deletions(-) diff --git a/src/components/CompanionWindow.js b/src/components/CompanionWindow.js index 8a31ce2fb..3b0d04e28 100644 --- a/src/components/CompanionWindow.js +++ b/src/components/CompanionWindow.js @@ -1,4 +1,4 @@ -import { Children, cloneElement, Component } from 'react'; +import { Children, cloneElement } from 'react'; import PropTypes from 'prop-types'; import { styled } from '@mui/material/styles'; import CloseIcon from '@mui/icons-material/CloseSharp'; @@ -23,17 +23,17 @@ const StyledCloseButton = styled(MiradorMenuButton, { name: 'CompanionWindow', s /** * CompanionWindow */ -export class CompanionWindow extends Component { +export function CompanionWindow(props) { /** */ - openInNewStyle() { - const { direction } = this.props; + const openInNewStyle = () => { + const { direction } = props; if (direction === 'rtl') return { transform: 'scale(-1, 1)' }; return {}; - } + }; /** */ - resizeHandles() { - const { direction, position } = this.props; + const resizeHandles = () => { + const { direction, position } = props; const positions = { ltr: { default: 'left', @@ -69,126 +69,119 @@ export class CompanionWindow extends Component { } return base; - } + }; + const { + ariaLabel, classes, paperClassName, onCloseClick, updateCompanionWindow, isDisplayed, + position, t, title, children, titleControls, size, + defaultSidebarPanelWidth, defaultSidebarPanelHeight, innerRef, + } = props; - /** - * render - * @return - */ - render() { - const { - ariaLabel, classes, paperClassName, onCloseClick, updateCompanionWindow, isDisplayed, - position, t, title, children, titleControls, size, - defaultSidebarPanelWidth, defaultSidebarPanelHeight, innerRef, - } = this.props; + const isBottom = (position === 'bottom' || position === 'far-bottom'); - const isBottom = (position === 'bottom' || position === 'far-bottom'); - - const childrenWithAdditionalProps = Children.map(children, (child) => { - if (!child) return null; - return cloneElement( - child, - { - parentactions: { - closeCompanionWindow: onCloseClick, - }, + const childrenWithAdditionalProps = Children.map(children, (child) => { + if (!child) return null; + return cloneElement( + child, + { + parentactions: { + closeCompanionWindow: onCloseClick, }, - ); - }); + }, + ); + }); - return ( - + - - - {title} - { - position === 'left' - ? updateCompanionWindow - && ( - { updateCompanionWindow({ position: 'right' }); }} - > - - - ) - : ( - <> - { - updateCompanionWindow && ( - { updateCompanionWindow({ position: position === 'bottom' ? 'right' : 'bottom' }); }} - > - - - ) - } - - - - - ) - } - { - titleControls && ( - + {title} + { + position === 'left' + ? updateCompanionWindow + && ( + { updateCompanionWindow({ position: 'right' }); }} > - {titleControls} - + + ) - } - - - {childrenWithAdditionalProps} - - - - ); - } + : ( + <> + { + updateCompanionWindow && ( + { updateCompanionWindow({ position: position === 'bottom' ? 'right' : 'bottom' }); }} + > + + + ) + } + + + + + ) + } + { + titleControls && ( + + {titleControls} + + ) + } + + + {childrenWithAdditionalProps} + + + + ); } CompanionWindow.propTypes = { @@ -224,7 +217,7 @@ CompanionWindow.defaultProps = { defaultSidebarPanelWidth: 235, innerRef: undefined, isDisplayed: false, - onCloseClick: () => {}, + onCloseClick: () => { }, paperClassName: '', position: null, size: {}, From 19c2694a37fc11be971e4677b110e0294eeebf13 Mon Sep 17 00:00:00 2001 From: marlo-longley Date: Tue, 29 Oct 2024 16:22:46 -0500 Subject: [PATCH 2/2] Remove react-sizeme and repalce with our own HOC --- __tests__/src/extend/withSize.test.js | 50 +++++++++++++++++++ package.json | 1 - setupJest.js | 10 ++-- src/containers/CompanionWindow.js | 4 +- .../WindowCanvasNavigationControls.js | 2 +- src/extend/withSize.js | 47 +++++++++++++++++ 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 __tests__/src/extend/withSize.test.js create mode 100644 src/extend/withSize.js diff --git a/__tests__/src/extend/withSize.test.js b/__tests__/src/extend/withSize.test.js new file mode 100644 index 000000000..ece8055b5 --- /dev/null +++ b/__tests__/src/extend/withSize.test.js @@ -0,0 +1,50 @@ +import { render, screen } from '@testing-library/react'; +import PropTypes from 'prop-types'; +import { withSize } from '../../../src/extend/withSize'; + +/** Mock ResizeObserver */ +class ResizeObserver { + /** */ + constructor(callback) { + this.callback = callback; + } + + /** */ + observe(element) { + // Fake a resize event + setTimeout(() => { + this.callback([{ contentRect: { height: 300, width: 400 } }]); + }, 0); + } + + /** */ + disconnect() { jest.fn(); } // eslint-disable-line +} + +// Replace the global ResizeObserver with the mock +global.ResizeObserver = ResizeObserver; + +/** */ +const TestComponent = ({ size }) => ( +
+ {size.width} + {size.height} +
+); + +TestComponent.propTypes = { + size: PropTypes.shape({ + height: PropTypes.number, + width: PropTypes.number, + }).isRequired, +}; + +const WrappedTestComponent = withSize()(TestComponent); + +test('it should render with size', async () => { + render(); + + // Assert that the updated size is reflected + expect(await screen.findByText(/400/)).toBeInTheDocument(); + expect(await screen.findByText(/300/)).toBeInTheDocument(); +}); diff --git a/package.json b/package.json index 7eff0528f..49006731e 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,6 @@ "react-redux": "^8.0.0 || ^9.0.0", "react-resize-observer": "^1.1.1", "react-rnd": "^10.1", - "react-sizeme": "^2.6.7 || ^3.0.0", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.5", "redux": "^5.0.0", diff --git a/setupJest.js b/setupJest.js index d5a066407..af9f272bc 100644 --- a/setupJest.js +++ b/setupJest.js @@ -1,18 +1,22 @@ /* eslint-disable import/no-extraneous-dependencies */ import fetchMock from 'jest-fetch-mock'; -import sizeMe from 'react-sizeme'; import i18next from 'i18next'; import { setupIntersectionMocking } from 'react-intersection-observer/test-utils'; import en from './src/locales/en/translation.json'; jest.setTimeout(10000); -sizeMe.noPlaceholders = true; - const { TextEncoder } = require('util'); global.TextEncoder = TextEncoder; +// Mock the browser's native ResizeObserver +global.ResizeObserver = jest.fn().mockImplementation(() => ({ + disconnect: jest.fn(), + observe: jest.fn(), + unobserve: jest.fn(), +})); + // Setup Jest to mock fetch fetchMock.enableMocks(); diff --git a/src/containers/CompanionWindow.js b/src/containers/CompanionWindow.js index 64a180644..4747d9c16 100644 --- a/src/containers/CompanionWindow.js +++ b/src/containers/CompanionWindow.js @@ -1,7 +1,7 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withSize } from 'react-sizeme'; +import { withSize } from '../extend/withSize'; import { withPlugins } from '../extend/withPlugins'; import { withRef } from '../extend/withRef'; import * as actions from '../state/actions'; @@ -46,8 +46,8 @@ const mapDispatchToProps = (dispatch, { windowId, id }) => ({ const enhance = compose( withRef(), - withTranslation(), withSize(), + withTranslation(), connect(mapStateToProps, mapDispatchToProps), withPlugins('CompanionWindow'), ); diff --git a/src/containers/WindowCanvasNavigationControls.js b/src/containers/WindowCanvasNavigationControls.js index ca5088f34..91b62e584 100644 --- a/src/containers/WindowCanvasNavigationControls.js +++ b/src/containers/WindowCanvasNavigationControls.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; -import { withSize } from 'react-sizeme'; +import { withSize } from '../extend/withSize'; import { withPlugins } from '../extend/withPlugins'; import { getShowZoomControlsConfig, getWorkspace } from '../state/selectors'; import { WindowCanvasNavigationControls } from '../components/WindowCanvasNavigationControls'; diff --git a/src/extend/withSize.js b/src/extend/withSize.js new file mode 100644 index 000000000..14f2d136e --- /dev/null +++ b/src/extend/withSize.js @@ -0,0 +1,47 @@ +/** This file was written to replace https://github.com/ctrlplusb/react-sizeme + * when its dependencies went out of date and is very much inspired by its code. + */ + +import { useEffect, useRef, useState } from 'react'; + +/** */ +export function withSize() { + return function WrapComponent(WrappedComponent) { + /** */ + const SizeAwareComponent = (props) => { + const [size, setSize] = useState({ height: undefined, width: undefined }); + const elementRef = useRef(null); + const observerRef = useRef(null); + + useEffect(() => { + /** */ + const handleResize = (entries) => { + for (const entry of entries) { + const { width, height } = entry.contentRect; + setSize({ height, width }); + } + }; + + observerRef.current = new ResizeObserver(handleResize); + + if (elementRef.current) { + observerRef.current.observe(elementRef.current); + } + + return () => { + if (observerRef.current) { + observerRef.current.disconnect(); + } + }; + }, []); + + return ( +
+ +
+ ); + }; + + return SizeAwareComponent; + }; +}