Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12787 resizable layout component #13044

Merged
merged 47 commits into from
Jul 11, 2024
Merged
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
a4c422e
PoC resizable layout
Jondyr Jun 19, 2024
56ec604
Alter `FormDesigner` view to test out resizable layout
Jondyr Jun 19, 2024
ac8454e
Remove commented code
Jondyr Jun 19, 2024
ce56af3
Undo deletion of css rule
Jondyr Jun 25, 2024
9b43d1f
Move `LayoutSet` selector to own toolbar similar to `SchemaEditor`
Jondyr Jun 25, 2024
eb58be2
Refactor `ResizableLayout` implementation
Jondyr Jun 25, 2024
823884f
Make preview collapsing also collapse the resizable layout element
Jondyr Jun 25, 2024
ee9552b
Refactor css and use negative inset to increase grab area of handles
Jondyr Jun 25, 2024
727a53a
Update layout on minimumSize change
Jondyr Jun 25, 2024
8074bc2
Add storybook entry for Resizable Layout
Jondyr Jun 26, 2024
d479f5f
Refactor `StudioResizableLayout`
Jondyr Jun 27, 2024
3241dc1
Add toolbar and add state to block iframe preview
Jondyr Jun 27, 2024
84f1ec9
Update ResizableLayout storybook entry
Jondyr Jun 27, 2024
90a6060
Move localstorage hook to studio-components `hook` folder
Jondyr Jun 28, 2024
8498b7e
Remove unnecessary ref proxying with imperativehandle
Jondyr Jun 28, 2024
f0aa555
Remove layoutId in favor of just using localStorageContextKey
Jondyr Jun 28, 2024
bbaacbb
Add test for resizing and keyboard presses
Jondyr Jun 28, 2024
93ae303
Hide left side when container size is small, to avoid overlapping han…
Jondyr Jun 28, 2024
d4f9060
Remove unused code
Jondyr Jun 28, 2024
08e9b74
Revert "Make preview collapsing also collapse the resizable layout el…
Jondyr Jun 28, 2024
2bd8ab0
Revert accidental v3 form editor edits
Jondyr Jun 28, 2024
249bcfb
Remove layoutId from storybook entry and unnecessary imports
Jondyr Jun 28, 2024
ea4fbaf
Remove accordion top border for first item in list
Jondyr Jul 1, 2024
45574f9
Add maximumSize and move collapsing to css rules
Jondyr Jul 2, 2024
c13ea5c
Update preview test
Jondyr Jul 2, 2024
96f57e6
Make components left menu collapsible
Jondyr Jul 2, 2024
27777b6
Fix export of localStorage hooks for tests
Jondyr Jul 2, 2024
30b19af
Fix elements test
Jondyr Jul 2, 2024
9509cbe
Fix preview test collapsing
Jondyr Jul 2, 2024
352e75e
Add test for collapsing elements view
Jondyr Jul 2, 2024
6cf2aec
Use exact matching to avoid potential false hits in playwright getbyrole
Jondyr Jul 2, 2024
8f7c22f
Add exact matching to other cases with false hits
Jondyr Jul 2, 2024
1275262
Add tests for resizablelayoutfunction and mousemovementhook
Jondyr Jul 2, 2024
7cd17d5
Fix event type in mouse movement test
Jondyr Jul 4, 2024
308c898
Update resizablelayout tests to cover more cases
Jondyr Jul 4, 2024
5967c71
Add test case for horizontal and vertical layout configuration
Jondyr Jul 4, 2024
c3b0792
Merge branch 'main' into 12787-resizable-layout-component
Jondyr Jul 10, 2024
c3030fb
Remove unused css file
Jondyr Jul 10, 2024
ec74e2c
Revert "Remove unused css file"
Jondyr Jul 10, 2024
388670d
Remove unused css rules and fix css toolbar issue
Jondyr Jul 10, 2024
ae4d2f0
Simplify valid child checker
Jondyr Jul 10, 2024
5b6d773
Add tests for collapsing components and preview in ux editor
Jondyr Jul 10, 2024
a0a6792
Add org and user to `localStorageContext` for datamodel `resizableLay…
Jondyr Jul 10, 2024
6f15724
Mock serviceContext for changed tests
Jondyr Jul 10, 2024
6421dc0
Merge branch 'main' into 12787-resizable-layout-component
Jondyr Jul 11, 2024
7f1b23e
Merge branch 'main' into 12787-resizable-layout-component
Jondyr Jul 11, 2024
7e00bf6
Update designsystemet import
Jondyr Jul 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add maximumSize and move collapsing to css rules
Jondyr committed Jul 2, 2024
commit 45574f9c3c5403db2778c05587b7e6277decafa9
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CSSProperties, ReactElement } from 'react';
import React, { Children, useEffect, useRef } from 'react';
import classes from './StudioResizableLayoutContainer.module.css';
import { type StudioResizableLayoutElementProps } from '../StudioResizableLayoutElement/StudioResizableLayoutElement';
@@ -10,24 +11,24 @@ export type StudioResizableOrientation = 'horizontal' | 'vertical';
export type StudioResizableLayoutContainerProps = {
localStorageContext?: string;
orientation: StudioResizableOrientation;
style?: React.CSSProperties;
style?: CSSProperties;

children: React.ReactElement<StudioResizableLayoutElementProps>[];
children: ReactElement<StudioResizableLayoutElementProps>[];
};

const StudioResizableLayoutContainer = ({
children,
orientation,
localStorageContext = 'default',
style,
}: StudioResizableLayoutContainerProps): React.ReactElement => {
}: StudioResizableLayoutContainerProps): ReactElement => {
const elementRefs = useRef<(HTMLDivElement | null)[]>([]);
useEffect(() => {
elementRefs.current = elementRefs.current.slice(0, getValidChildren(children).length);
}, [children]);

const { containerSizes, setContainerSizes } = useTrackContainerSizes(localStorageContext);
const { resizeTo, resizeDelta, collapse } = useStudioResizableLayoutFunctions(
const { resizeTo, resizeDelta } = useStudioResizableLayoutFunctions(
orientation,
elementRefs,
getValidChildren(children),
@@ -47,7 +48,7 @@ const StudioResizableLayoutContainer = ({

return (
<StudioResizableLayoutContext.Provider
value={{ resizeDelta, resizeTo, collapse, orientation, containerSizes }}
value={{ resizeDelta, resizeTo, orientation, containerSizes }}
>
<div
className={classes.root}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { forwardRef, useEffect } from 'react';
import type { ReactElement } from 'react';
import React, { forwardRef } from 'react';
import classes from './StudioResizableLayoutElement.module.css';
import { useStudioResizableLayoutContext } from '../hooks/useStudioResizableLayoutContext';
import { StudioResizableLayoutHandle } from '../StudioResizableLayoutHandle/StudioResizableLayoutHandle';

export type StudioResizableLayoutElementProps = {
minimumSize?: number;
maximumSize?: number;
collapsedSize?: number;
collapsed?: boolean;
style?: React.CSSProperties;
@@ -24,33 +26,29 @@ const StudioResizableLayoutElement = forwardRef<HTMLDivElement, StudioResizableL
{
index,
minimumSize = 0,
maximumSize,
collapsedSize,
collapsed,
children,
hasNeighbour = false,
style,
onResizing,
}: StudioResizableLayoutElementProps,
ref,
) => {
const { resizeTo, collapse, orientation, containerSize } =
useStudioResizableLayoutContext(index);

useEffect(() => {
if (collapsed) {
collapse(index);
} else {
resizeTo(index, minimumSize);
}
// disable linter as we only want to run this effect if the collapsed prop changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [collapsed]);
): ReactElement => {
const { orientation, containerSize } = useStudioResizableLayoutContext(index);

return (
<>
<div
data-testid='resizablelayoutelement'
className={classes.container}
style={{ ...style, flexGrow: containerSize }}
style={{
...style,
flexGrow: containerSize,
maxWidth: collapsed ? collapsedSize : maximumSize,
minWidth: collapsed ? collapsedSize : minimumSize,
}}
ref={ref}
>
{children}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReactElement } from 'react';
import React, { useEffect } from 'react';
import { useStudioResizableLayoutContext } from '../hooks/useStudioResizableLayoutContext';
import type { StudioResizableOrientation } from '../StudioResizableLayoutContainer/StudioResizableLayoutContainer';
@@ -15,7 +16,7 @@ export const StudioResizableLayoutHandle = ({
orientation,
index,
onResizing,
}: StudioResizableLayoutHandleProps) => {
}: StudioResizableLayoutHandleProps): ReactElement => {
const { resizeDelta, containerSize } = useStudioResizableLayoutContext(index);
const { onMouseDown, isResizing } = useStudioResizableLayoutMouseMovement(
orientation,
Original file line number Diff line number Diff line change
@@ -27,6 +27,10 @@ export class StudioResizableLayoutArea {
return this.reactElement.props.minimumSize || 0;
}

public get maximumSize() {
return this.reactElement.props.maximumSize || Number.MAX_SAFE_INTEGER;
}

public get collapsedSize() {
return this.reactElement.props.collapsedSize || 0;
}
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@ export type StudioResizableLayoutContextProps = {
containerSizes: number[];
resizeDelta: (index: number, size: number) => void;
resizeTo: (index: number, size: number) => void;
collapse: (index: number) => void;
};

export const StudioResizableLayoutContext =
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const useKeyboardControls = (
onResize: (delta: number) => void,
): { onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void } => {
const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
if (event.shiftKey) {
onResize(-50);
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import { StudioResizableLayoutArea } from '../classes/StudioResizableLayoutEleme
type useResizableFunctionsReturnType = {
resizeTo: (index: number, size: number) => void;
resizeDelta: (index: number, size: number) => void;
collapse: (index: number) => void;
};

export const useStudioResizableLayoutFunctions = (
@@ -14,7 +13,7 @@ export const useStudioResizableLayoutFunctions = (
children: any[],
setContainerSize: (index: number, size: number) => void,
): useResizableFunctionsReturnType => {
const getElementNeighbour = (index: number) => {
const getElementNeighbour = (index: number): StudioResizableLayoutArea => {
const neighbourIndex = elementRefs.current.length < index + 2 ? index - 1 : index + 1;
return new StudioResizableLayoutArea(
neighbourIndex,
@@ -24,7 +23,7 @@ export const useStudioResizableLayoutFunctions = (
);
};

const getElement = (index: number) => {
const getElement = (index: number): StudioResizableLayoutArea => {
return new StudioResizableLayoutArea(
index,
elementRefs.current[index],
@@ -37,14 +36,11 @@ export const useStudioResizableLayoutFunctions = (
element: StudioResizableLayoutArea,
neighbour: StudioResizableLayoutArea,
newSize: number,
) => {
): { newSize: number; neighbourNewSize: number } => {
const totalSize = element.size + neighbour.size;
if (element.minimumSize > newSize) {
newSize = element.minimumSize;
}
if (neighbour.minimumSize > totalSize - newSize) {
newSize = totalSize - neighbour.minimumSize;
}
if (element.maximumSize < newSize) newSize = element.maximumSize;
if (element.minimumSize > newSize) newSize = element.minimumSize;
if (neighbour.minimumSize > totalSize - newSize) newSize = totalSize - neighbour.minimumSize;
const neighbourNewSize = totalSize - newSize;
return { newSize, neighbourNewSize };
};
@@ -54,34 +50,19 @@ export const useStudioResizableLayoutFunctions = (
neighbour: StudioResizableLayoutArea,
resizeTo: number,
ignoreMinimumSize: boolean = false,
) => {
): { containerFlexGrow: number; neighbourFlexGrow: number } => {
const totalPixelSize = element.size + neighbour.size;
const { newSize, neighbourNewSize } = ignoreMinimumSize
? { newSize: resizeTo, neighbourNewSize: totalPixelSize - resizeTo }
: calculatePixelSizes(element, neighbour, resizeTo);

const totalFlexGrow = element.flexGrow + neighbour.flexGrow;
const containerFlexGrow = totalFlexGrow * (newSize / totalPixelSize);
const neighbourFlexGrow = totalFlexGrow * (neighbourNewSize / totalPixelSize);
const containerFlexGrow = (newSize / totalPixelSize) * totalFlexGrow;
const neighbourFlexGrow = (neighbourNewSize / totalPixelSize) * totalFlexGrow;
return { containerFlexGrow, neighbourFlexGrow };
};

const forceSize = (index: number, size: number) => {
const element = getElement(index);
const neighbour = getElementNeighbour(index);

const { containerFlexGrow, neighbourFlexGrow } = calculateFlexGrow(
element,
neighbour,
size,
true,
);

setContainerSize(index, containerFlexGrow);
setContainerSize(neighbour.index, neighbourFlexGrow);
};

const resizeTo = (index: number, size: number) => {
const resizeTo = (index: number, size: number): void => {
const element = getElement(index);
const neighbour = getElementNeighbour(index);

@@ -95,15 +76,10 @@ export const useStudioResizableLayoutFunctions = (
setContainerSize(neighbour.index, neighbourFlexGrow);
};

const resizeDelta = (index: number, size: number) => {
const resizeDelta = (index: number, size: number): void => {
const element = getElement(index);
resizeTo(index, element.size + size);
};

const collapse = (index: number) => {
const element = getElement(index);
forceSize(index, element.collapsedSize);
};

return { resizeTo, resizeDelta, collapse };
return { resizeTo, resizeDelta };
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { useContext } from 'react';
import type { StudioResizableLayoutContextProps } from '../context/StudioResizableLayoutContext';
import { StudioResizableLayoutContext } from '../context/StudioResizableLayoutContext';

export const useStudioResizableLayoutContext = (index: number) => {
const { containerSizes, orientation, resizeDelta, resizeTo, collapse } = useContext(
interface useStudioResizableLayoutContextReturnType
extends Omit<StudioResizableLayoutContextProps, 'containerSizes'> {
containerSize: number;
}

export const useStudioResizableLayoutContext = (
index: number,
): useStudioResizableLayoutContextReturnType => {
const { containerSizes, orientation, resizeDelta, resizeTo } = useContext(
StudioResizableLayoutContext,
);
const containerSize = containerSizes[index];
return { containerSize, orientation, resizeDelta, resizeTo, collapse };
return { containerSize, orientation, resizeDelta, resizeTo };
};
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ export const useStudioResizableLayoutMouseMovement = (
const [isResizing, setIsResizing] = useState(false);

const mouseMove = useCallback(
(event: MouseEvent) => {
(event: MouseEvent): void => {
const mousePos = orientation === 'horizontal' ? event.clientX : event.clientY;
const mouseTotalDelta = mousePos - startMousePosition.current;
const mouseDelta = mousePos - lastMousePosition.current;
@@ -24,7 +24,7 @@ export const useStudioResizableLayoutMouseMovement = (
);

const mouseUp = useCallback(
(_: MouseEvent) => {
(_: MouseEvent): void => {
setIsResizing(false);
window.removeEventListener('mousemove', mouseMove);
window.removeEventListener('mouseup', mouseUp);
@@ -33,7 +33,7 @@ export const useStudioResizableLayoutMouseMovement = (
);

const onMouseDown = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
(event: React.MouseEvent<HTMLDivElement>): (() => void) => {
if (event.button !== 0) return;
event.preventDefault();
setIsResizing(true);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useLocalStorage } from '../../../hooks/useLocalStorage';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';

export const useTrackContainerSizes = (localStorageContextKey: string) => {
const [containerSizes, setContainerSizes] = useState<number[]>([]);
@@ -8,7 +8,8 @@ export const useTrackContainerSizes = (localStorageContextKey: string) => {
`studio:resizable-layout:${localStorageContextKey}`,
containerSizes,
);
useEffect(() => {

useMemo(() => {
setContainerSizes(value);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
background-color: #f7f7f7;
overflow-x: clip;
overflow-y: auto;
width: 100%;
}

.typeInfo {
Original file line number Diff line number Diff line change
@@ -9,9 +9,10 @@ import { DragAndDropTree } from 'app-shared/components/DragAndDropTree';
import { useMoveProperty } from './hooks/useMoveProperty';
import { useAddReference } from './hooks/useAddReference';
import { NodePanel } from '../NodePanel';
import { StudioResizableLayout } from '@studio/components';

export const SchemaEditor = () => {
const { schemaModel, selectedTypePointer } = useSchemaEditorAppContext();
const { schemaModel, selectedTypePointer, selectedNodePointer } = useSchemaEditorAppContext();
const moveProperty = useMoveProperty();
const addReference = useAddReference();

@@ -20,23 +21,33 @@ export const SchemaEditor = () => {
const selectedType = selectedTypePointer && schemaModel.getNode(selectedTypePointer);

return (
<>
<DragAndDropTree.Provider
onAdd={addReference}
onMove={moveProperty}
rootId={ROOT_POINTER}
itemId={selectedTypePointer ?? null}
>
<aside className={classes.inspector}>
<TypesInspector schemaItems={definitions} />
</aside>
<div className={classes.editor}>
<NodePanel pointer={selectedType?.pointer} />
</div>
</DragAndDropTree.Provider>
<aside className={classes.inspector}>
<SchemaInspector />
</aside>
</>
<DragAndDropTree.Provider
onAdd={addReference}
onMove={moveProperty}
rootId={ROOT_POINTER}
itemId={selectedTypePointer ?? null}
>
<StudioResizableLayout.Container orientation='horizontal' localStorageContext='datamodel'>
<StudioResizableLayout.Element minimumSize={100} maximumSize={280}>
<aside className={classes.inspector}>
<TypesInspector schemaItems={definitions} />
</aside>
</StudioResizableLayout.Element>
<StudioResizableLayout.Element>
<div className={classes.editor}>
<NodePanel pointer={selectedType?.pointer} />
</div>
</StudioResizableLayout.Element>
<StudioResizableLayout.Element
minimumSize={300}
collapsed={!selectedNodePointer}
collapsedSize={180}
>
<aside className={classes.inspector}>
<SchemaInspector />
</aside>
</StudioResizableLayout.Element>
</StudioResizableLayout.Container>
</DragAndDropTree.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
display: flex;
flex-direction: column;
height: 100%;
width: 500px;
}

.root :global(.MuiAutocomplete-input) {
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.root {
box-sizing: border-box;
padding: 24px 24px;
width: 280px;
min-height: 100%;
background: rgba(224, 224, 224, 0.3);
border-right: 1px solid var(--fds-semantic-border-neutral-subtle);
Loading