Skip to content

Commit

Permalink
feat(frontend): Resizable Panels for the Test run (#3694)
Browse files Browse the repository at this point in the history
* feat(frontend): Resizable Panels for the Test run

* feat(frontend): Resizable Panels for the Test run

* feat(frontend): Final touches

* feat(frontend): fixing onboarding

* feat(frontend): fixing onboarding
  • Loading branch information
xoscar authored Mar 1, 2024
1 parent 7a44578 commit 96200e1
Show file tree
Hide file tree
Showing 28 changed files with 526 additions and 543 deletions.
27 changes: 10 additions & 17 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@
"react-flow-renderer": "10.3.7",
"react-joyride": "2.5.3",
"react-redux": "7.2.6",
"react-resizable-panels": "2.0.11",
"react-router-dom": "6.2.1",
"react-scripts": "5.0.1",
"react-spaces": "0.3.8",
"react-syntax-highlighter": "15.5.0",
"react-virtualized-auto-sizer": "1.0.22",
"react-window": "1.8.10",
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/ResizablePanels/FillPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Spaces from 'react-spaces';
import { Panel } from 'react-resizable-panels';

const FillPanel: React.FC = ({children}) => <Spaces.Fill style={{overflow: 'scroll'}}>{children}</Spaces.Fill>;
const FillPanel: React.FC = ({children}) => <Panel>{children}</Panel>;

export default FillPanel;
28 changes: 28 additions & 0 deletions web/src/components/ResizablePanels/Handle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {PanelResizeHandle} from 'react-resizable-panels';
import {DoubleLeftOutlined, DoubleRightOutlined} from '@ant-design/icons';

import * as S from './ResizablePanels.styled';

interface IProps {
isOpen: boolean;
onToggle(): void;
placement?: 'left' | 'right';
}

const Handle = ({onToggle, isOpen, placement = 'left'}: IProps) => {
return (
<PanelResizeHandle className="panel-handle">
<S.ButtonContainer $placement={placement}>
<S.ToggleButton
onClick={e => {
e.stopPropagation();
onToggle();
}}
icon={isOpen ? <DoubleLeftOutlined /> : <DoubleRightOutlined />}
/>
</S.ButtonContainer>
</PanelResizeHandle>
);
};

export default Handle;
54 changes: 5 additions & 49 deletions web/src/components/ResizablePanels/LeftPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,14 @@
import * as Spaces from 'react-spaces';
import {useLayoutEffect} from 'react';
import {noop} from 'lodash';
import useResizablePanel, {TPanel, TSize} from '../ResizablePanels/hooks/useResizablePanel';
import Splitter from '../ResizablePanels/Splitter';
import {TPanel} from '../ResizablePanels/hooks/useResizablePanel';
import Panel from './Panel';

interface IProps {
panel: TPanel;
order?: number;
children(size: TSize): React.ReactNode;
tooltip?: string;
isToolTipVisible?: boolean;
children: React.ReactNode;
onOpen?(): void;
dataTour?: string;
}

const LeftPanel = ({
panel,
order = 1,
tooltip,
isToolTipVisible = false,
children,
onOpen = noop,
dataTour,
}: IProps) => {
const {size, toggle, onStopResize} = useResizablePanel({panel});

useLayoutEffect(() => {
if (size.isOpen) onOpen();
}, [onOpen, size.isOpen]);

return (
<Spaces.LeftResizable
onResizeEnd={newSize => onStopResize(newSize)}
minimumSize={size.minSize}
maximumSize={size.maxSize}
size={size.size}
key={size.name}
order={order}
handleRender={props => (
<Splitter
{...props}
name={size.name}
isOpen={size.isOpen}
onClick={toggle}
tooltip={tooltip}
isToolTipVisible={isToolTipVisible}
tooltipPlacement="right"
dataTour={dataTour}
/>
)}
>
{children(size)}
</Spaces.LeftResizable>
);
const LeftPanel = (props: IProps) => {
return <Panel {...props} />;
};

export default LeftPanel;
39 changes: 39 additions & 0 deletions web/src/components/ResizablePanels/Panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {Panel as ResizablePanel} from 'react-resizable-panels';
import {useCallback, useLayoutEffect} from 'react';
import {noop} from 'lodash';
import useResizablePanel, {TPanel} from './hooks/useResizablePanel';
import * as S from './ResizablePanels.styled';
import Handle from './Handle';

interface IProps {
panel: TPanel;
children: React.ReactNode;
onOpen?(): void;
handlePlacement?: 'left' | 'right';
}

const Panel = ({onOpen = noop, panel, children, handlePlacement = 'right'}: IProps) => {
const {size, onStopResize, onChange, ref} = useResizablePanel({panel});

useLayoutEffect(() => {
if (size.isOpen) onOpen();
}, [onOpen, size.isOpen]);

const toggle = useCallback(() => {
onChange(size.isOpen ? size.closeSize() : size.openSize());
}, [onChange, size]);

return (
<>
{handlePlacement === 'left' && <Handle placement={handlePlacement} isOpen={!size.isOpen} onToggle={toggle} />}
<ResizablePanel ref={ref} maxSize={size.maxSize()} defaultSize={size.openSize()} onResize={onStopResize}>
<S.PanelContainer $isOpen={size.isOpen} onClick={() => !size.isOpen && onChange(size.openSize())}>
{children}
</S.PanelContainer>
</ResizablePanel>
{handlePlacement === 'right' && <Handle placement={handlePlacement} isOpen={size.isOpen} onToggle={toggle} />}
</>
);
};

export default Panel;
86 changes: 55 additions & 31 deletions web/src/components/ResizablePanels/ResizablePanels.styled.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,41 @@
import {Button} from 'antd';
import styled, {createGlobalStyle, css} from 'styled-components';
import {withPulseAnimation} from '../PulseButton';

export const GlobalStyle = createGlobalStyle`
.spaces-resize-handle {
border-left: 1px solid ${({theme}) => theme.color.borderLight};
z-index: 10;
}
.splitter {
.ant-tooltip-arrow-content:before,
.ant-tooltip-inner {
background: ${({theme}) => theme.color.primary};
color: ${({theme}) => theme.color.white};
.panel-handle:has(button:hover) {
* {
cursor: pointer !important;
}
}
`;

export const ButtonContainer = styled.div`
position: absolute;
right: -15px;
top: 48px;
z-index: 100;
`;
export const ButtonContainer = styled.div<{$placement: 'left' | 'right'}>`
position: relative;
width: 4px;
height: 100%;
background: ${({theme}) => theme.color.white};
export const SplitterButton = styled(Button)<{$isPulsing: boolean}>`
&& {
border: 3px solid ${({theme}) => theme.color.primaryLight};
background-clip: padding-box;
> span {
font-size: ${({theme}) => theme.size.md};
}
:hover {
background: ${({theme}) => theme.color.primaryHover};
}
${({theme, $isPulsing}) => $isPulsing && withPulseAnimation(theme)}
`;
${({$placement}) =>
($placement === 'left' &&
css`
border-left: 1px solid ${({theme}) => theme.color.borderLight};
export const SplitterContainer = styled.div``;
:hover {
border-left: 1px solid ${({theme}) => theme.color.primaryHover};
}
`) ||
css`
border-right: 1px solid ${({theme}) => theme.color.borderLight};
:hover {
border-right: 1px solid ${({theme}) => theme.color.primaryHover};
}
`}
`;

export const PanelContainer = styled.div<{$isOpen: boolean}>`
background-color: ${({theme}) => theme.color.white};
Expand All @@ -51,11 +50,36 @@ export const PanelContainer = styled.div<{$isOpen: boolean}>`
}
${({$isOpen}) =>
$isOpen &&
($isOpen &&
css`
> div {
opacity: 1;
pointer-events: auto;
}
`) ||
css`
> div {
opacity: 1;
pointer-events: auto;
:hover {
background: ${({theme}) => theme.color.primaryHover};
}
cursor: pointer;
`}
`;

export const ToggleButton = styled(Button).attrs({
shape: 'circle',
type: 'primary',
onMouseDown: (e: React.MouseEvent) => e.stopPropagation(),
})`
&& {
position: absolute;
top: 60px;
left: -14px;
z-index: 1030;
border: 3px solid ${({theme}) => theme.color.primaryLight};
background-clip: padding-box;
> span {
font-size: ${({theme}) => theme.size.md};
}
}
`;
12 changes: 8 additions & 4 deletions web/src/components/ResizablePanels/ResizablePanels.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import * as Spaces from 'react-spaces';
import {PanelGroup} from 'react-resizable-panels';
import * as S from './ResizablePanels.styled';

const ResizablePanels: React.FC = ({children}) => {
interface IProps {
saveId?: string;
}

const ResizablePanels: React.FC<IProps> = ({children, saveId}) => {
return (
<>
<S.GlobalStyle />
<Spaces.Fixed height="100%" width="100vw">
<PanelGroup autoSaveId={saveId} direction="horizontal">
{children}
</Spaces.Fixed>
</PanelGroup>
</>
);
};
Expand Down
37 changes: 6 additions & 31 deletions web/src/components/ResizablePanels/RightPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,14 @@
import * as Spaces from 'react-spaces';
import Splitter from '../ResizablePanels/Splitter';
import useResizablePanel, {TPanel, TSize} from '../ResizablePanels/hooks/useResizablePanel';
import {TPanel} from '../ResizablePanels/hooks/useResizablePanel';
import Panel from './Panel';

interface IProps {
panel: TPanel;
order?: number;
children(size: TSize): React.ReactNode;
tooltip?: string;
children: React.ReactNode;
onOpen?(): void;
}

const RightPanel = ({panel, order = 1, children, tooltip}: IProps) => {
const {size, toggle, onStopResize} = useResizablePanel({panel});

return (
<Spaces.RightResizable
onResizeEnd={newSize => onStopResize(newSize)}
minimumSize={size.minSize}
maximumSize={size.maxSize}
size={size.size}
key={size.name}
order={order}
handleRender={props => (
<Splitter
{...props}
name={size.name}
isOpen={!size.isOpen}
onClick={() => toggle()}
tooltip={!size.isOpen ? tooltip : ''}
tooltipPlacement="left"
/>
)}
>
{children(size)}
</Spaces.RightResizable>
);
const RightPanel = (props: IProps) => {
return <Panel handlePlacement="left" {...props} />;
};

export default RightPanel;
Loading

0 comments on commit 96200e1

Please sign in to comment.