From 08c4e073ea7881c49d1c72b8315309bc7bc6049b Mon Sep 17 00:00:00 2001 From: Alyssa Wang Date: Wed, 7 Feb 2024 18:38:54 -0500 Subject: [PATCH 1/3] convert codeblock to collapsible panel --- .../RequestDetailModal/CodeBlock.tsx | 48 +++++++++++------ .../RequestDetailModal/RequestDetailModal.tsx | 38 ++++++------- .../components/RequestDetailModal/styles.tsx | 3 +- .../TestListItem/RequestList.tsx | 41 ++------------ .../src/components/_common/CollapseButton.tsx | 54 +++++++++++++++++++ client/src/components/_common/CopyButton.tsx | 53 ++++++++++++++++++ 6 files changed, 160 insertions(+), 77 deletions(-) create mode 100644 client/src/components/_common/CollapseButton.tsx create mode 100644 client/src/components/_common/CopyButton.tsx diff --git a/client/src/components/RequestDetailModal/CodeBlock.tsx b/client/src/components/RequestDetailModal/CodeBlock.tsx index 81cf695ac..97b454e55 100644 --- a/client/src/components/RequestDetailModal/CodeBlock.tsx +++ b/client/src/components/RequestDetailModal/CodeBlock.tsx @@ -1,32 +1,50 @@ -import { Container, Paper } from '@mui/material'; import React, { FC } from 'react'; -import useStyles from './styles'; +import { Box, Card, CardContent, CardHeader, Collapse, Divider } from '@mui/material'; import { RequestHeader } from '~/models/testSuiteModels'; +import CopyButton from '~/components/_common/CopyButton'; import { formatBodyIfJSON } from './helpers'; +import useStyles from './styles'; +import CollapseButton from '../_common/CollapseButton'; export interface CodeBlockProps { body?: string | null; + collapsedState?: boolean; headers?: RequestHeader[] | null | undefined; + title: string; } -const CodeBlock: FC = ({ body, headers }) => { +const CodeBlock: FC = ({ body, collapsedState = false, headers, title }) => { const { classes } = useStyles(); + const [collapsed, setCollapsed] = React.useState(collapsedState); if (body && body.length > 0) { return ( - -
-          
-            {formatBodyIfJSON(body, headers)}
-          
-        
-
+ + + + + + } + /> + + + +
+              
+                {formatBodyIfJSON(body, headers)}
+              
+            
+
+
+
); } else { return null; diff --git a/client/src/components/RequestDetailModal/RequestDetailModal.tsx b/client/src/components/RequestDetailModal/RequestDetailModal.tsx index e59d55a57..997045e87 100644 --- a/client/src/components/RequestDetailModal/RequestDetailModal.tsx +++ b/client/src/components/RequestDetailModal/RequestDetailModal.tsx @@ -7,15 +7,15 @@ import { DialogContent, DialogTitle, Divider, - IconButton, Typography, } from '@mui/material'; import { Request } from '~/models/testSuiteModels'; -import { ContentCopy, Input } from '@mui/icons-material'; +import { Input } from '@mui/icons-material'; import CustomTooltip from '~/components/_common/CustomTooltip'; import CodeBlock from './CodeBlock'; import HeaderTable from './HeaderTable'; import useStyles from './styles'; +import CopyButton from '../_common/CopyButton'; export interface RequestDetailModalProps { request?: Request; @@ -30,19 +30,9 @@ const RequestDetailModal: FC = ({ modalVisible, usedRequest, }) => { - const [copySuccess, setCopySuccess] = React.useState(false); const { classes } = useStyles(); const timestamp = request?.timestamp ? new Date(request?.timestamp) : null; - const copyTextClick = async (text: string) => { - await navigator.clipboard.writeText(text).then(() => { - setCopySuccess(true); - setTimeout(() => { - setCopySuccess(false); - }, 2000); // 2 second delay - }); - }; - const usedRequestIcon = ( @@ -64,15 +54,7 @@ const RequestDetailModal: FC = ({ {request?.url} - {request?.url && ( - - - void copyTextClick(request.url)}> - - - - - )} + {request?.url && } → {request?.status} @@ -105,14 +87,24 @@ const RequestDetailModal: FC = ({ {timestamp && {timestamp.toLocaleString()}} - + Response - + diff --git a/client/src/components/RequestDetailModal/styles.tsx b/client/src/components/RequestDetailModal/styles.tsx index 8ebf2b707..e2e80fe28 100644 --- a/client/src/components/RequestDetailModal/styles.tsx +++ b/client/src/components/RequestDetailModal/styles.tsx @@ -18,10 +18,9 @@ export default makeStyles()((_theme: Theme) => ({ fontWeight: 600, }, codeblock: { - width: '100%', overflow: 'auto', fontSize: 'small', - marginTop: '10px', + marginTop: '16px', }, code: { textWrap: 'wrap', diff --git a/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx b/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx index d3675d715..e0d2d2d8c 100644 --- a/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx +++ b/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx @@ -2,7 +2,6 @@ import React, { FC } from 'react'; import { Box, Button, - IconButton, Typography, TableRow, TableCell, @@ -11,13 +10,14 @@ import { TableBody, TableHead, } from '@mui/material'; -import { ContentCopy, Input, SaveAlt } from '@mui/icons-material'; +import { Input, SaveAlt } from '@mui/icons-material'; import { Request } from '~/models/testSuiteModels'; import { getRequestDetails } from '~/api/RequestsApi'; import RequestDetailModal from '~/components/RequestDetailModal/RequestDetailModal'; import CustomTooltip from '~/components/_common/CustomTooltip'; import { useSnackbar } from 'notistack'; import useStyles from './styles'; +import CopyButton from '~/components/_common/CopyButton'; interface RequestListProps { resultId: string; @@ -30,7 +30,6 @@ const RequestList: FC = ({ requests, resultId, updateRequest, const { classes } = useStyles(); const { enqueueSnackbar } = useSnackbar(); const [showDetails, setShowDetails] = React.useState(false); - const [copySuccess, setCopySuccess] = React.useState({}); const [detailedRequest, setDetailedRequest] = React.useState(); const headerTitles = ['Type', 'URL', 'Status']; @@ -55,16 +54,6 @@ const RequestList: FC = ({ requests, resultId, updateRequest, } }; - const copyTextClick = async (text: string) => { - await navigator.clipboard.writeText(text).then(() => { - setCopySuccess({ ...copySuccess, [text]: true }); - setTimeout(() => { - // Reset map instead of setting false to avoid async bug - setCopySuccess({}); - }, 2000); // 2 second delay - }); - }; - const renderReferenceIcon = (request: Request) => { if (request.result_id !== resultId) { return ( @@ -109,7 +98,7 @@ const RequestList: FC = ({ requests, resultId, updateRequest, role="none" aria-hidden="true" aria-label="header-request-direction" - > + /> {view === 'run' && ( = ({ requests, resultId, updateRequest, {request.url} - - void copyTextClick(request.url)} - > - - - + diff --git a/client/src/components/_common/CollapseButton.tsx b/client/src/components/_common/CollapseButton.tsx new file mode 100644 index 000000000..5c9e10c28 --- /dev/null +++ b/client/src/components/_common/CollapseButton.tsx @@ -0,0 +1,54 @@ +import React, { FC, useEffect } from 'react'; +import { IconButton, IconButtonPropsSizeOverrides } from '@mui/material'; +import { OverridableStringUnion } from '@mui/types'; +import { ExpandLess, ExpandMore } from '@mui/icons-material'; +import CustomTooltip from '~/components/_common/CustomTooltip'; + +export interface CollapseButtonProps { + setCollapsed: (collapsed: boolean) => void; + size?: OverridableStringUnion<'small' | 'large' | 'medium', IconButtonPropsSizeOverrides>; + startState?: boolean; + view?: string; +} + +const CollapseButton: FC = ({ + setCollapsed: setParentCollapsed, + size, + startState = false, + view, +}) => { + const [collapsed, setCollapsed] = React.useState(startState); + + useEffect(() => { + setParentCollapsed(collapsed); + }, [collapsed]); + + return ( + <> + + setCollapsed(!collapsed)} + > + {collapsed ? : } + + + + ); +}; + +export default CollapseButton; diff --git a/client/src/components/_common/CopyButton.tsx b/client/src/components/_common/CopyButton.tsx new file mode 100644 index 000000000..ed1a7a509 --- /dev/null +++ b/client/src/components/_common/CopyButton.tsx @@ -0,0 +1,53 @@ +import React, { FC } from 'react'; +import { Box, IconButton, IconButtonPropsSizeOverrides } from '@mui/material'; +import { OverridableStringUnion } from '@mui/types'; +import { ContentCopy } from '@mui/icons-material'; +import CustomTooltip from '~/components/_common/CustomTooltip'; + +export interface CopyButtonProps { + copyText: string; + size?: OverridableStringUnion<'small' | 'large' | 'medium', IconButtonPropsSizeOverrides>; + view?: string; +} + +const CopyButton: FC = ({ copyText, size, view }) => { + const [copySuccess, setCopySuccess] = React.useState({}); // Use map for lists of copiable items + + const copyTextClick = async (text: string) => { + await navigator.clipboard.writeText(text).then(() => { + setCopySuccess({ ...copySuccess, [text]: true }); + setTimeout(() => { + // Reset map instead of setting false to avoid async bug + setCopySuccess({}); + }, 2000); // 2 second delay + }); + }; + return ( + + + void copyTextClick(copyText)} + > + + + + + ); +}; + +export default CopyButton; From 754e08eb81efab4803c7b699af79f60598c96deb Mon Sep 17 00:00:00 2001 From: Alyssa Wang Date: Thu, 8 Feb 2024 11:38:17 -0500 Subject: [PATCH 2/3] make prop optional --- client/src/components/RequestDetailModal/CodeBlock.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/RequestDetailModal/CodeBlock.tsx b/client/src/components/RequestDetailModal/CodeBlock.tsx index 97b454e55..e8accf997 100644 --- a/client/src/components/RequestDetailModal/CodeBlock.tsx +++ b/client/src/components/RequestDetailModal/CodeBlock.tsx @@ -11,7 +11,7 @@ export interface CodeBlockProps { body?: string | null; collapsedState?: boolean; headers?: RequestHeader[] | null | undefined; - title: string; + title?: string; } const CodeBlock: FC = ({ body, collapsedState = false, headers, title }) => { @@ -22,7 +22,7 @@ const CodeBlock: FC = ({ body, collapsedState = false, headers, return ( From 42ce6150d2aa4eb5276c42886c48c51b7d9b5a0d Mon Sep 17 00:00:00 2001 From: Alyssa Wang Date: Fri, 23 Feb 2024 15:08:45 -0500 Subject: [PATCH 3/3] remove dead code --- .../RequestDetailModal/CodeBlock.tsx | 2 +- .../RequestDetailModal/RequestDetailModal.tsx | 4 +-- .../src/components/_common/CollapseButton.tsx | 34 +++++-------------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/client/src/components/RequestDetailModal/CodeBlock.tsx b/client/src/components/RequestDetailModal/CodeBlock.tsx index e8accf997..b0c27751a 100644 --- a/client/src/components/RequestDetailModal/CodeBlock.tsx +++ b/client/src/components/RequestDetailModal/CodeBlock.tsx @@ -1,11 +1,11 @@ import React, { FC } from 'react'; import { Box, Card, CardContent, CardHeader, Collapse, Divider } from '@mui/material'; import { RequestHeader } from '~/models/testSuiteModels'; +import CollapseButton from '~/components/_common/CollapseButton'; import CopyButton from '~/components/_common/CopyButton'; import { formatBodyIfJSON } from './helpers'; import useStyles from './styles'; -import CollapseButton from '../_common/CollapseButton'; export interface CodeBlockProps { body?: string | null; diff --git a/client/src/components/RequestDetailModal/RequestDetailModal.tsx b/client/src/components/RequestDetailModal/RequestDetailModal.tsx index 997045e87..651c0e470 100644 --- a/client/src/components/RequestDetailModal/RequestDetailModal.tsx +++ b/client/src/components/RequestDetailModal/RequestDetailModal.tsx @@ -91,7 +91,7 @@ const RequestDetailModal: FC = ({ body={request.request_body} collapsedState={true} headers={request.request_headers} - title="Full Request" + title="Request Body" /> @@ -103,7 +103,7 @@ const RequestDetailModal: FC = ({ body={request.response_body} collapsedState={true} headers={request.response_headers} - title="Full Response" + title="Response Body" /> diff --git a/client/src/components/_common/CollapseButton.tsx b/client/src/components/_common/CollapseButton.tsx index 5c9e10c28..579a0943c 100644 --- a/client/src/components/_common/CollapseButton.tsx +++ b/client/src/components/_common/CollapseButton.tsx @@ -8,14 +8,12 @@ export interface CollapseButtonProps { setCollapsed: (collapsed: boolean) => void; size?: OverridableStringUnion<'small' | 'large' | 'medium', IconButtonPropsSizeOverrides>; startState?: boolean; - view?: string; } const CollapseButton: FC = ({ setCollapsed: setParentCollapsed, size, startState = false, - view, }) => { const [collapsed, setCollapsed] = React.useState(startState); @@ -24,30 +22,16 @@ const CollapseButton: FC = ({ }, [collapsed]); return ( - <> - + setCollapsed(!collapsed)} > - setCollapsed(!collapsed)} - > - {collapsed ? : } - - - + {collapsed ? : } + + ); };