From ede94f78e464b4cc265522e278c857905a365e74 Mon Sep 17 00:00:00 2001 From: Jonas Dyrlie Date: Tue, 16 Jul 2024 16:24:30 +0200 Subject: [PATCH] 12787 resizable testing feedback changes (#13154) * Add minimumsize to center columns on ux editor * Remove onhover effect for resizing handles * Use localStorage for collapsed state and preview default as opened * Add overflow auto for x axis on properties panel * Refactor function to reuse code * Add throttling to resizing to avoid multiple resizes per render cycle * Fix tests using userQuery in ux-editor * Alter test to include case for resizing last element in container * Remove unused code --- .../StudioResizableLayoutHandle.module.css | 4 ---- .../StudioResizableLayoutHandle.tsx | 2 +- .../hooks/useStudioResizableFunctions.test.ts | 6 +++-- .../hooks/useStudioResizableFunctions.ts | 15 ++++-------- .../useStudioResizableLayoutMouseMovement.ts | 20 +++++++++++----- frontend/packages/ux-editor/src/App.test.tsx | 3 +++ .../Properties/Properties.module.css | 1 + .../src/containers/FormDesigner.test.tsx | 8 ++++--- .../ux-editor/src/containers/FormDesigner.tsx | 23 +++++++++++++------ 9 files changed, 49 insertions(+), 33 deletions(-) diff --git a/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.module.css b/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.module.css index 279fdff723e..6d93b2949a2 100644 --- a/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.module.css +++ b/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.module.css @@ -10,10 +10,6 @@ position: absolute; } -.resizeHandle:hover:after { - background-color: rgba(0, 0, 0, 0.1); -} - .resizeHandleH { cursor: col-resize; } diff --git a/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.tsx b/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.tsx index af905c76a60..eab6a97d745 100644 --- a/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.tsx +++ b/frontend/libs/studio-components/src/components/StudioResizableLayout/StudioResizableLayoutHandle/StudioResizableLayoutHandle.tsx @@ -20,7 +20,7 @@ export const StudioResizableLayoutHandle = ({ const { resizeDelta, containerSize } = useStudioResizableLayoutContext(index); const { onMouseDown, isResizing } = useStudioResizableLayoutMouseMovement( orientation, - (delta, _) => { + (delta) => { resizeDelta(index, delta); }, ); diff --git a/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.test.ts b/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.test.ts index b70f2b50968..2806a37e6b6 100644 --- a/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.test.ts +++ b/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.test.ts @@ -9,7 +9,9 @@ describe('useStudioResizableLayoutFunctions', () => { beforeEach(() => { setContainerSize = jest.fn(); - elementRefs = { current: [document.createElement('div'), document.createElement('div')] }; + elementRefs = { + current: [document.createElement('div'), document.createElement('div')], + }; children = [ { props: { collapsed: false, minimumSize: 0, maximumSize: 100, collapsedSize: 0 } }, { props: { collapsed: false, minimumSize: 0, maximumSize: 100, collapsedSize: 0 } }, @@ -33,7 +35,7 @@ describe('useStudioResizableLayoutFunctions', () => { it('should call resizeTo with correct parameters when resizeDelta is called', () => { const { result } = renderFunctionsHook(elementRefs, children, setContainerSize); - result.current.resizeDelta(0, 100); + result.current.resizeDelta(1, 100); expect(setContainerSize).toHaveBeenCalled(); }); diff --git a/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.ts b/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.ts index 06f2dda76ef..1324754fc6e 100644 --- a/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.ts +++ b/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableFunctions.ts @@ -13,16 +13,6 @@ export const useStudioResizableLayoutFunctions = ( children: any[], setContainerSize: (index: number, size: number) => void, ): useResizableFunctionsReturnType => { - const getElementNeighbour = (index: number): StudioResizableLayoutArea => { - const neighbourIndex = elementRefs.current.length < index + 2 ? index - 1 : index + 1; - return new StudioResizableLayoutArea( - neighbourIndex, - elementRefs.current[neighbourIndex], - children[neighbourIndex], - orientation, - ); - }; - const getElement = (index: number): StudioResizableLayoutArea => { return new StudioResizableLayoutArea( index, @@ -32,6 +22,11 @@ export const useStudioResizableLayoutFunctions = ( ); }; + const getElementNeighbour = (index: number): StudioResizableLayoutArea => { + const neighbourIndex = elementRefs.current.length < index + 2 ? index - 1 : index + 1; + return getElement(neighbourIndex); + }; + const calculatePixelSizes = ( element: StudioResizableLayoutArea, neighbour: StudioResizableLayoutArea, diff --git a/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableLayoutMouseMovement.ts b/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableLayoutMouseMovement.ts index 65c8aaf9fe8..0d704dfd99f 100644 --- a/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableLayoutMouseMovement.ts +++ b/frontend/libs/studio-components/src/components/StudioResizableLayout/hooks/useStudioResizableLayoutMouseMovement.ts @@ -1,23 +1,30 @@ -import { useRef, useCallback, useState } from 'react'; +import { useRef, useCallback, useState, useEffect } from 'react'; import type { StudioResizableOrientation } from '../StudioResizableLayoutContainer/StudioResizableLayoutContainer'; export const useStudioResizableLayoutMouseMovement = ( orientation: StudioResizableOrientation, - onMousePosChange: (delta: number, position: number) => void, + onMousePosChange: (delta: number) => void, ): { onMouseDown: (event: React.MouseEvent) => () => void; isResizing: boolean; } => { const lastMousePosition = useRef(0); - const startMousePosition = useRef(0); const [isResizing, setIsResizing] = useState(false); + // throttle mouseMove events to avoid calculating new size before last rerender + const update = useRef(1); + const lastEventUpdate = useRef(0); + useEffect(() => { + update.current++; + }); + const mouseMove = useCallback( (event: MouseEvent): void => { + if (update.current === lastEventUpdate.current) return; + lastEventUpdate.current = update.current; const mousePos = orientation === 'horizontal' ? event.clientX : event.clientY; - const mouseTotalDelta = mousePos - startMousePosition.current; const mouseDelta = mousePos - lastMousePosition.current; - onMousePosChange(mouseDelta, mouseTotalDelta); + onMousePosChange(mouseDelta); lastMousePosition.current = mousePos; }, [orientation, onMousePosChange], @@ -25,6 +32,8 @@ export const useStudioResizableLayoutMouseMovement = ( const mouseUp = useCallback( (_: MouseEvent): void => { + update.current = 1; + lastEventUpdate.current = 0; setIsResizing(false); window.removeEventListener('mousemove', mouseMove); window.removeEventListener('mouseup', mouseUp); @@ -38,7 +47,6 @@ export const useStudioResizableLayoutMouseMovement = ( event.preventDefault(); setIsResizing(true); lastMousePosition.current = orientation === 'horizontal' ? event.clientX : event.clientY; - startMousePosition.current = lastMousePosition.current; window.addEventListener('mousemove', mouseMove); window.addEventListener('mouseup', mouseUp); }, diff --git a/frontend/packages/ux-editor/src/App.test.tsx b/frontend/packages/ux-editor/src/App.test.tsx index e00d19a05c1..d8fbe70acca 100644 --- a/frontend/packages/ux-editor/src/App.test.tsx +++ b/frontend/packages/ux-editor/src/App.test.tsx @@ -9,6 +9,8 @@ import type { AppContextProps } from './AppContext'; import ruleHandlerMock from './testing/ruleHandlerMock'; import { layoutSetsMock } from './testing/layoutSetsMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; +import { user as userMock } from 'app-shared/mocks/mocks'; +import { QueryKey } from 'app-shared/types/QueryKey'; const mockQueries: Partial = { getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('test')), @@ -24,6 +26,7 @@ const renderApp = ( appContextProps: Partial = {}, ) => { const queryClient = createQueryClientMock(); + queryClient.setQueryData([QueryKey.CurrentUser], [userMock]); return renderWithProviders(, { queries, appContextProps, diff --git a/frontend/packages/ux-editor/src/components/Properties/Properties.module.css b/frontend/packages/ux-editor/src/components/Properties/Properties.module.css index b7c626d65cf..dfcca284b18 100644 --- a/frontend/packages/ux-editor/src/components/Properties/Properties.module.css +++ b/frontend/packages/ux-editor/src/components/Properties/Properties.module.css @@ -1,6 +1,7 @@ .root { background: var(--fds-semantic-surface-neutral-subtle); flex: var(--properties-width-fraction); + overflow-x: auto; } .dataModelBindings { diff --git a/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx b/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx index 4bb3ba30717..1a0ba5c2bb9 100644 --- a/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx +++ b/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx @@ -20,6 +20,7 @@ import { formItemContextProviderMock } from '../testing/formItemContextMocks'; import { appContextMock } from '../testing/appContextMock'; import { app, org } from '@studio/testing/testids'; import userEvent from '@testing-library/user-event'; +import { user as userMock } from 'app-shared/mocks/mocks'; // Test data: const defaultTexts: ITextResources = { @@ -43,6 +44,7 @@ const render = () => { [], ); queryClient.setQueryData([QueryKey.TextResources, org, app], defaultTexts); + queryClient.setQueryData([QueryKey.CurrentUser], [userMock]); return renderWithProviders( { expect(screen.queryByText(textMock('ux_editor.loading_form_layout'))).not.toBeInTheDocument(), ); - await user.click(screen.getByTitle(textMock('ux_editor.open_preview'))); - expect(screen.getByTitle(textMock('ux_editor.close_preview'))).toBeInTheDocument(); - await user.click(screen.getByTitle(textMock('ux_editor.close_preview'))); expect(screen.getByTitle(textMock('ux_editor.open_preview'))).toBeInTheDocument(); + + await user.click(screen.getByTitle(textMock('ux_editor.open_preview'))); + expect(screen.getByTitle(textMock('ux_editor.close_preview'))).toBeInTheDocument(); }); }); diff --git a/frontend/packages/ux-editor/src/containers/FormDesigner.tsx b/frontend/packages/ux-editor/src/containers/FormDesigner.tsx index 63e3e56e810..854603b1357 100644 --- a/frontend/packages/ux-editor/src/containers/FormDesigner.tsx +++ b/frontend/packages/ux-editor/src/containers/FormDesigner.tsx @@ -9,7 +9,7 @@ import { useFormLayoutsQuery } from '../hooks/queries/useFormLayoutsQuery'; import { useFormLayoutSettingsQuery } from '../hooks/queries/useFormLayoutSettingsQuery'; import { useRuleModelQuery } from '../hooks/queries/useRuleModelQuery'; import { ErrorPage } from '../components/ErrorPage'; -import { StudioPageSpinner, StudioResizableLayout } from '@studio/components'; +import { StudioPageSpinner, StudioResizableLayout, useLocalStorage } from '@studio/components'; import { BASE_CONTAINER_ID } from 'app-shared/constants'; import { useRuleConfigQuery } from '../hooks/queries/useRuleConfigQuery'; import { useInstanceIdQuery, useUserQuery } from 'app-shared/hooks/queries'; @@ -63,8 +63,14 @@ export const FormDesigner = (): JSX.Element => { selectedFormLayoutSetName, ); const { handleEdit } = useFormItemContext(); - const [previewCollapsed, setPreviewCollapsed] = useState(true); - const [elementsCollapsed, setElementsCollapsed] = useState(false); + const [previewCollapsed, setPreviewCollapsed] = useLocalStorage( + `form-designer-main:previewCollapsed:${user.id}:${org}`, + false, + ); + const [elementsCollapsed, setElementsCollapsed] = useLocalStorage( + `form-designer-main:elementsCollapsed:${user.id}:${org}`, + false, + ); const [hidePreview, setHidePreview] = useState(false); const t = useText(); @@ -161,13 +167,16 @@ export const FormDesigner = (): JSX.Element => { > setElementsCollapsed((prev) => !prev)} + onCollapseToggle={() => setElementsCollapsed(!elementsCollapsed)} /> - + - setHidePreview(resizing)}> + setHidePreview(resizing)} + > { > setPreviewCollapsed((prev) => !prev)} + onCollapseToggle={() => setPreviewCollapsed(!previewCollapsed)} hidePreview={hidePreview} />