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

Feature/add response section #24

Merged
merged 10 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions src/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import { Dispatch, FC, SetStateAction } from 'react';
import EditorField from '@components/Editor/ui/EditorField';
import LineNumbers from '@components/Editor/ui/LineNumbers';
import useEditorSize from '@components/RequestEditor/lib/hooks/useEditorSize';
import cn from '@shared/lib/helpers/cn';
import useScrollbar from '@shared/lib/hooks/useScrollbar';

type EditorProps = {
editorState: string;
onChange: Dispatch<SetStateAction<string>> | ((value: string) => void);
className?: string;
};

const Editor: FC<EditorProps> = ({ onChange, editorState }) => {
const Editor: FC<EditorProps> = ({ onChange, editorState, className }) => {
const { editorRef, editorNumbersNum, editorNumRef } = useEditorSize();
const rootRef = useScrollbar<HTMLElement>();

return (
<article ref={rootRef} className="h-full w-full font-jetbrains_mono">
<article ref={rootRef} className={cn('h-full w-full font-jetbrains_mono', className)}>
<div className="flex w-full gap-4 py-7 pr-4">
<LineNumbers ref={editorNumRef} size={editorNumbersNum} />
<EditorField ref={editorRef} onChange={onChange} value={editorState} />
Expand Down
4 changes: 2 additions & 2 deletions src/components/Editor/lib/hooks/useEditorUrlState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import { UrlParams } from '@shared/lib/types/types';
*/
function useEditorUrlState(urlParam: UrlParams, initialState = '') {
const { readUrl, setUrl } = useUrl();
const urlState = readUrl(urlParam);
const urlState = readUrl(urlParam) ?? initialState;

useEffect(() => {
if (urlState === null) setUrl(urlParam, initialState);
if (urlState === initialState) setUrl(urlParam, initialState);
}, [initialState, setUrl, urlParam, urlState]);

const handleChange = useCallback(
Expand Down
4 changes: 2 additions & 2 deletions src/components/Editor/ui/EditorField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ const EditorField = forwardRef<HTMLDivElement, EditorFieldProps>(({ onChange, va
tabIndex={0}
aria-label="The text editor"
role="textbox"
contentEditable="plaintext-only"
contentEditable
onInput={handleInput}
className="h-fit w-full outline-none"
className="h-fit w-full whitespace-pre-wrap outline-none"
dangerouslySetInnerHTML={{ __html: defaultValue.current }}
/>
);
Expand Down
16 changes: 4 additions & 12 deletions src/components/EditorTools/EditorTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,23 @@ import { FC, RefObject } from 'react';

import EditorToolsField from '@components/EditorTools/ui/EditorToolsField';
import Header from '@components/EditorTools/ui/Header';
import urlParams from '@shared/constants/urlParams';
import cn from '@shared/lib/helpers/cn';
import useUrl from '@shared/lib/hooks/useUrl';

type EditorToolsProps = {
containerRef: RefObject<HTMLElement>;
};

const EditorTools: FC<EditorToolsProps> = ({ containerRef }) => {
const { readUrl } = useUrl();
const isExpanded = readUrl(urlParams.EXPANDED) === 'true';

return (
<article
data-testid="editor-tools-container"
<section
data-testid="editor-tools"
className={cn(
'grid h-full grid-rows-[min-content_auto] overflow-clip rounded-t-4xl bg-surface-container pt-4 duration-[inherit] ease-[inherit]',
{
'rounded-4xl': isExpanded,
},
'grid h-full grid-rows-[min-content_auto] overflow-clip rounded-4xl bg-surface-container pt-4 duration-[inherit] ease-[inherit]',
)}
>
<Header containerRef={containerRef} />
<EditorToolsField />
</article>
</section>
);
};

Expand Down
28 changes: 10 additions & 18 deletions src/components/EditorTools/lib/hooks/useExpand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,31 @@ import urlParams from '@shared/constants/urlParams';
import useUrl from '@shared/lib/hooks/useUrl';

const useExpand = (containerRef: RefObject<HTMLElement>) => {
const headerRef = useRef<HTMLHeadElement>(null);
const editorToolsTabsRef = useRef<HTMLHeadElement>(null);
const { readUrl } = useUrl();

const isExpanded = readUrl(urlParams.EXPANDED) === 'true';

useEffect(() => {
const container = containerRef?.current;
const header = headerRef?.current;
const requestContainer = containerRef?.current;
const editorToolsTabs = editorToolsTabsRef?.current;

if (!container || !header) return;
if (!requestContainer || !editorToolsTabs) return;

if (isExpanded) {
container.style.gridTemplateRows = '';
container.style.cssText = `
transition-timing-function: cubic-bezier(0.05, 0.7, 0.1, 1.0);
transition-duration: 400ms
`;
requestContainer.style.gridTemplateRows = '';
return;
}

const headerHeight = Number.parseInt(getComputedStyle(header).height, 10);
const containerPaddingTop = Number.parseInt(getComputedStyle(container).paddingTop, 10);
const parentContainerGap = Number.parseInt(getComputedStyle(container).gap, 10);
const headerHeight = Number.parseInt(getComputedStyle(editorToolsTabs).height, 10);
const containerPaddingTop = Number.parseInt(getComputedStyle(requestContainer).paddingTop, 10);
const parentContainerGap = Number.parseInt(getComputedStyle(requestContainer).gap, 10);
const collapsedHeight = `${headerHeight + containerPaddingTop + parentContainerGap}px`;

container.style.cssText = `
grid-template-rows: auto ${collapsedHeight};
transition-timing-function: cubic-bezier(0.3, 0.0, 0.8, 0.15);
transition-duration: 200ms
`;
requestContainer.style.gridTemplateRows = `auto ${collapsedHeight}`;
}, [containerRef, isExpanded]);

return headerRef;
return editorToolsTabsRef;
};

export default useExpand;
2 changes: 1 addition & 1 deletion src/components/EditorTools/ui/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const Header: FC<HeaderProps> = ({ containerRef }) => {
</Tabs>
<IconButton
data-testid="editor-tools-expand"
className={cn('rotate-180 duration-300 ease-[inherit]', {
className={cn('rotate-180 duration-[inherit] ease-[inherit]', {
'rotate-0': isExpanded,
})}
onClick={() => setUrl(urlParams.EXPANDED, !isExpanded)}
Expand Down
9 changes: 6 additions & 3 deletions src/components/RequestEditor/RequestEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ const RequestEditor = () => {
const [editorState, setEditorState] = useEditorUrlState(urlParams.QUERY, EDITOR_DEFAULT_VALUE);

return (
<article className="flex h-full w-full gap-4 overflow-y-hidden rounded-4xl bg-surface-container pl-6 pr-4">
<Editor editorState={editorState} onChange={setEditorState} />
<section
data-testid="editor-request"
className="relative flex h-full w-full gap-4 overflow-y-hidden rounded-4xl bg-surface-container pl-6"
>
<Editor className="pr-20" editorState={editorState} onChange={setEditorState} />
<Controls />
</article>
</section>
);
};

Expand Down
11 changes: 8 additions & 3 deletions src/components/RequestEditor/ui/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import FilledIconButton from '@shared/ui/FilledIconButton';
import Icon from '@shared/ui/Icon';

const Controls = () => {
const { readUrl } = useUrl();
const { readUrl, setUrl } = useUrl();

const handleCopyText = async () => {
const query = readUrl(urlParams.QUERY);
Expand All @@ -16,8 +16,13 @@ const Controls = () => {
alert('text copied');
};

const handleResponseOpen = () => {
const isResponseOpen = readUrl(urlParams.RESPONSE_OPEN) === 'true';
setUrl(urlParams.RESPONSE_OPEN, String(!isResponseOpen));
};

return (
<ul data-testid="controls" className="grid content-start justify-items-center pt-7">
<ul data-testid="controls" className="absolute right-6 top-7 grid content-start justify-items-center">
<li className="mb-3 flex items-center justify-center">
<Fab data-testid="fab" variant="primary">
<Icon slot="icon">play_arrow</Icon>
Expand All @@ -34,7 +39,7 @@ const Controls = () => {
</FilledIconButton>
</li>
<li className="flex h-12 w-12 items-center justify-center">
<FilledIconButton data-testid="open-response">
<FilledIconButton data-testid="open-response" onClick={handleResponseOpen}>
<Icon>info</Icon>
</FilledIconButton>
</li>
Expand Down
66 changes: 66 additions & 0 deletions src/components/ResponseViewer/ResponseViewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useCallback } from 'react';

import urlParams from '@shared/constants/urlParams';
import useScrollbar from '@shared/lib/hooks/useScrollbar';
import useUrl from '@shared/lib/hooks/useUrl';
import Icon from '@shared/ui/Icon';
import IconButton from '@shared/ui/IconButton';

const PLACEHOLDER_TEXT = `{
"errors": [
{
"message": "Syntax Error: te",
"locations": [
{
"line": 33,
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
"column": 1
}
]
}
]
}`;

const ResponseViewer = () => {
const rootRef = useScrollbar<HTMLDivElement>();
const { setUrl } = useUrl();

const handleClick = useCallback(
function handleClick() {
setUrl(urlParams.RESPONSE_OPEN, 'false');
},
[setUrl],
);

return (
<>
<IconButton data-testid="close-response" className="absolute right-4 top-4 z-10" onClick={handleClick}>
<Icon>close</Icon>
</IconButton>
<div
data-testid="response-viewer"
ref={rootRef}
className="h-full w-full justify-between overflow-y-scroll rounded-4xl bg-surface-container py-7 pl-7 pr-4"
>
<article className="h-fit w-fit pr-10">
<pre className="h-full w-full whitespace-break-spaces">{PLACEHOLDER_TEXT}</pre>
</article>
</div>
</>
);
};

export default ResponseViewer;
25 changes: 21 additions & 4 deletions src/layouts/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
import { useRef } from 'react';

import { Outlet } from 'react-router-dom';

import Footer from '@components/Footer/Footer';
import Header from '@components/Header/Header';
import Nav from '@components/Nav/Nav';
import urlParams from '@shared/constants/urlParams';
import cn from '@shared/lib/helpers/cn';
import useUrl from '@shared/lib/hooks/useUrl';

const MainLayout = () => {
const { readUrl } = useUrl();
const mainLayoutRef = useRef(null);

const isResponseOpen = readUrl(urlParams.RESPONSE_OPEN) === 'true';

return (
<main className="grid h-screen grid-cols-[384px_1fr_.5fr] grid-rows-[80px_1fr] px-4 pb-4">
<main
data-testid="main-layout"
ref={mainLayoutRef}
className={cn(
'grid h-screen origin-center transform-gpu grid-cols-[384px_1fr_0fr] grid-rows-[80px_1fr] gap-4 overflow-hidden px-4 pb-4 transition-all duration-200 ease-emphasized-accelerate',
{
'grid-cols-[384px_1fr_.6fr] duration-500 ease-emphasized-decelerate': isResponseOpen,
},
)}
>
<Header />
<Nav />
<section className="col-start-2 row-start-2 row-end-4">
<Outlet />
</section>
<Outlet context={mainLayoutRef} />
<Footer />
</main>
);
Expand Down
39 changes: 27 additions & 12 deletions src/pages/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,43 @@ import { useRef } from 'react';

import EditorTools from '@components/EditorTools/EditorTools';
import RequestEditor from '@components/RequestEditor/RequestEditor';
import ResponseViewer from '@components/ResponseViewer/ResponseViewer';
import urlParams from '@shared/constants/urlParams';
import cn from '@shared/lib/helpers/cn';
import useUrl from '@shared/lib/hooks/useUrl';

const MainPage = () => {
const editorContainerRef = useRef<HTMLDivElement>(null);
const { readUrl } = useUrl();

const isExpanded = readUrl(urlParams.EXPANDED) === 'true';
const isResponseOpen = readUrl(urlParams.RESPONSE_OPEN) === 'true';

return (
<div
ref={editorContainerRef}
className={cn(
'body-large grid h-full w-full grid-rows-[auto_40%] items-end gap-4 overflow-clip transition-all duration-200 ease-emphasized-accelerate',
{
'grid-rows-[auto_64px]': !isExpanded,
},
)}
>
<RequestEditor />
<EditorTools containerRef={editorContainerRef} />
</div>
<>
<div
ref={editorContainerRef}
className={cn(
'body-large col-start-2 row-start-2 row-end-4 grid h-full w-full grid-rows-[auto_40%] items-end gap-4 overflow-clip transition-all duration-200 ease-emphasized-accelerate',
{
'duration-400 ease-emphasized-decelerate': isExpanded,
},
)}
>
<RequestEditor />
<EditorTools containerRef={editorContainerRef} />
</div>
<section
className={cn(
'relative col-start-3 row-start-2 row-end-4 scale-95 overflow-hidden opacity-0 transition-all duration-200',
{
'scale-100 opacity-100 duration-500': isResponseOpen,
},
)}
>
<ResponseViewer />
</section>
</>
);
};

Expand Down
1 change: 1 addition & 0 deletions src/shared/constants/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import urlParams from '@shared/constants/urlParams';
const QUERY_PARAMS_INIT = {
[urlParams.EXPANDED]: 'true',
[urlParams.VARIABLES_TAB]: 'true',
[urlParams.RESPONSE_OPEN]: 'true',
};

export default QUERY_PARAMS_INIT;
1 change: 1 addition & 0 deletions src/shared/constants/urlParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const urlParams = {
HEADERS: 'headers',
VARIABLES_TAB: 'tab',
EXPANDED: 'expanded',
RESPONSE_OPEN: 'response',
} as const;

export default urlParams;
2 changes: 1 addition & 1 deletion src/shared/lib/hooks/useScrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { DeepPartial } from '@shared/lib/types/types';
const config: DeepPartial<Options> = {
scrollbars: {
visibility: 'auto',
autoHide: 'scroll',
autoHide: 'leave',
autoHideDelay: 500,
},
};
Expand Down
Loading