Skip to content

Commit

Permalink
Copy All Button for Response (#145)
Browse files Browse the repository at this point in the history
* copy all button

* fix auto-scroll

* styling adjustments

* add css hashPrefix

* update test snapshots
  • Loading branch information
yuyi-sl committed Aug 22, 2024
1 parent 44e5b69 commit 0aa23d7
Show file tree
Hide file tree
Showing 33 changed files with 222 additions and 59 deletions.
1 change: 1 addition & 0 deletions config/rollup/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const postcssPlugin = (options = {}) => (
autoModules: false,
modules: {
generateScopedName: '[name]__[local]___[hash:base64:5]',
hashPrefix: 'prefix',
},
...options,
})
Expand Down
7 changes: 3 additions & 4 deletions src/Components/NetworkTable/NetworkTable.styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
border-top: $border;
}


.network-table-header {
display: flex;
width: 100%;
Expand All @@ -27,13 +26,14 @@
width: 12%;
align-self: center;
padding-right: $size-xs-s;
min-width: $table-column-min-width;

&:last-child {
padding-right: 0;
}

&.show-waterfall {
width: 9%;
width: 10%;
}

&.file {
Expand All @@ -44,8 +44,7 @@
}
}

&.domain,
&.waterfall{
&.domain {
width: 15%;
}
}
Expand Down
16 changes: 12 additions & 4 deletions src/Components/NetworkTable/NetworkTableBody.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import NetworkTableRow from './NetworkTableRow';
import { TABLE_ENTRY_HEIGHT } from '../../constants';
import { useResizeObserver } from '../../hooks/useResizeObserver';
import Styles from './NetworkTable.styles.scss';
import { useTheme } from '../../state/theme/Context';

/* eslint no-underscore-dangle: 0 */

Expand Down Expand Up @@ -41,19 +42,26 @@ const NetworkTableBody = ({ height }) => {
actions,
callbacks,
} = useNetwork();
const { enableAutoScroll } = useTheme();
const numberOfNewEntries = state.get('numberOfNewEntries');
const data = state.get('data');
const totalNetworkTime = state.get('totalNetworkTime');
const selectedReqIndex = state.get('selectedReqIndex');

const ref = useRef(null);
const { elementDims } = useResizeObserver(ref);
const { elementDims } = useResizeObserver(ref?.current?._outerRef || ref?.current);

useEffect(() => actions.setTableHeaderWidth(elementDims.width), [elementDims]);

useEffect(() => {
const outerRef = ref?.current?._outerRef;
if (outerRef.scrollTop + outerRef.offsetHeight + TABLE_ENTRY_HEIGHT >= outerRef.scrollHeight) {
ref.current._outerRef.scrollTop = outerRef.scrollHeight;
if (enableAutoScroll && ref?.current?._outerRef) {
const outerRef = ref?.current?._outerRef;
const needToScroll = outerRef.scrollTop +
outerRef.offsetHeight +
(numberOfNewEntries * TABLE_ENTRY_HEIGHT) >= outerRef.scrollHeight;
if (needToScroll) {
ref.current._outerRef.scrollTop = outerRef.scrollHeight;
}
}
}, [data, ref]);

Expand Down
5 changes: 1 addition & 4 deletions src/Components/NetworkTable/TimeChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ const TimeChart = ({
placement="left"
title={<TimeChartTooltip data={timings} />}
>
<svg
{...TIME_CHART_SVG_PROPS}
className={Styles['time-chart']}
>
<svg {...TIME_CHART_SVG_PROPS}>
<g>
{chartAttributes.map((chartProps) => (
<rect
Expand Down
4 changes: 0 additions & 4 deletions src/Components/NetworkTable/TimeChart.styles.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
@import "./../../styles/variables.scss";

.time-chart {
height: 15px;
}

.time-chart-tooltip {
background-color: $white-100 !important;
overflow: hidden !important;
Expand Down
48 changes: 48 additions & 0 deletions src/Components/ReqDetail/CopyAllButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';

import Button from '../Common/Button';
import Styles from './CopyAllButton.styles.scss';
import IconCopy from '../../icons/IconCopy';
import IconCheckMark from '../../icons/IconCheckMark';

const CopyAllButton = ({ text }) => {
const [isCopied, setIsCopied] = useState(false);
const timeoutRef = useRef(null);

const copy = () => {
navigator.clipboard.writeText(text);
setIsCopied(true);

if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}

timeoutRef.current = setTimeout(() => {
setIsCopied(false);
}, 5000);
};

return (
<Button
className={Styles['copy-button']}
onClick={copy}
variant="text"
>
{isCopied ?
<IconCheckMark className={Styles['copy-icon']} /> :
<IconCopy className={Styles['copy-icon']} />}
<span className={Styles['copy-text']}>{isCopied ? 'Copied!' : 'Copy All'}</span>
</Button>
);
};

CopyAllButton.propTypes = {
text: PropTypes.object,
};

CopyAllButton.defaultProps = {
text: 'testi testi',
};

export default CopyAllButton;
17 changes: 17 additions & 0 deletions src/Components/ReqDetail/CopyAllButton.styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@import "./../../styles/variables";

.copy-button {
height: $filter-button-height;
color: $brand-blue;
font-size: $font-size-h6;

.copy-text {
padding-left: $size-xs;
}

.copy-icon {
fill: $brand-blue;
height: $size-s;
width: $size-s;
}
}
8 changes: 6 additions & 2 deletions src/Components/ReqDetail/Response.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';

import Styles from './Response.styles.scss';
import CopyAllButton from './CopyAllButton';

const NoResponseText = () => (
<h4 className={Styles['no-response']}>This request has no response data available.</h4>
Expand All @@ -16,9 +17,12 @@ const Response = ({ data }) => {

return (
<div className={Styles['response-content']}>
<pre className={Styles['log-body-colorless']}>
<div className={Styles['copy-button']}>
<CopyAllButton text={content} />
</div>
<span className={Styles['response-body']}>
{content}
</pre>
</span>
</div>
);
};
Expand Down
12 changes: 12 additions & 0 deletions src/Components/ReqDetail/Response.styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,16 @@
.response-content {
font-size: $font-size-small;
width: 100%;
display: flex;
flex-direction: column;

.copy-button {
align-self: end;
}

.response-body {
font-family: monospace;
white-space: pre-wrap;
word-break: break-all;
}
}
4 changes: 2 additions & 2 deletions src/Components/ReqDetail/headers/General.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { GENERAL_HEADERS } from '../../../constants';
const General = ({ data }) => (
<div className={Styles['header-detail']}>
{Object.entries(GENERAL_HEADERS).map(([dataKey, { key, name }]) => (
<p
<div
key={dataKey}
className={Styles['info-row']}
>
Expand All @@ -17,7 +17,7 @@ const General = ({ data }) => (
<span className={Styles['info-value']}>
{key === 'status' && data.error ? data.error : data[key]}
</span>
</p>
</div>
))}
</div>
);
Expand Down
1 change: 1 addition & 0 deletions src/Containers/MainContainer.styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
height: 100%;
width: 100%;
overflow: auto;
overflow-x: hidden;
outline: none;
}
2 changes: 1 addition & 1 deletion src/Containers/NetworkTableContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const NetworkTableContainer = () => {
if (ref?.current) {
setTableBodyHeight(ref.current.clientHeight - TABLE_HEADER_HEIGHT);
}
}, [ref]);
}, [ref, actualData]);

if (error) {
return (
Expand Down
1 change: 1 addition & 0 deletions src/Containers/ReqDetailContainer.styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,6 @@ $tab-height: 35px;
padding: $size-s $size-m;
height: calc(100% - #{$tab-height});
overflow: auto;
outline: none;
}
}
8 changes: 5 additions & 3 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,15 @@ export const TIMINGS = {
};

export const TIME_CHART_SVG_PROPS = {
viewBox: '0 0 250 20',
height: '15',
viewBox: '0 0 200 20',
version: '1.1',
preserveAspectRatio: 'xMinYMin meet',
};

export const TIME_CHART_DEFAULT_PROPS = {
height: 16,
y: 3.5,
height: 30,
y: 0,
};

export const ROW_ID_PREFIX = 'network-viewer-table-row-';
Expand Down Expand Up @@ -293,6 +294,7 @@ export const NETWORK_VIEWER_DEFAULT_OPTIONS = {
showPauseResume: false,
showTimeline: false,
showWaterfall: true,
enableAutoScroll: false,
};

export const PAYLOAD_CAPTIONS = Object.freeze({
Expand Down
16 changes: 7 additions & 9 deletions src/hooks/useResizeObserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,23 @@ export const useResizeObserver = (elementRef) => {
height: 0,
});
useEffect(() => {
const element = elementRef.current;

const onResize = () => {
if (element._outerRef) {
if (elementRef) {
setElementDims({
width: element._outerRef.clientWidth,
height: element._outerRef.clientHeight,
width: elementRef.clientWidth,
height: elementRef.clientHeight,
});
}
};
const resizeObserver = new ResizeObserver(onResize);

if (element._outerRef) {
resizeObserver.observe(element._outerRef);
if (elementRef) {
resizeObserver.observe(elementRef);
}

return () => {
if (element._outerRef) {
resizeObserver.unobserve(element._outerRef);
if (elementRef) {
resizeObserver.unobserve(elementRef);
}
};
}, [elementRef]);
Expand Down
28 changes: 28 additions & 0 deletions src/icons/IconCheckMark.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';

const IconCheckMark = ({ className }) => (
<svg
className={className}
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M23.5789 2.30868C24.0956 2.75821 24.1435 3.53404 23.6857 4.04153L7.48717 22L0.313554 14.0321C-0.143724 13.5242 -0.0951526 12.7485 0.42204 12.2994C0.939233 11.8503 1.7292 11.898 2.18648 12.4059L7.48892 18.2954L21.8143 2.41361C22.2721 1.90612 23.0621 1.85914 23.5789 2.30868Z"
fillRule="evenodd"
/>
</svg>
);

IconCheckMark.propTypes = {
className: PropTypes.string,
};

IconCheckMark.defaultProps = {
className: '',
};

export default IconCheckMark;
28 changes: 28 additions & 0 deletions src/icons/IconCopy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';

const IconCopy = ({ className }) => (
<svg
className={className}
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M14.5 0C15.3284 0 16 0.671573 16 1.5V4H22C23.1046 4 24 4.89543 24 6V22C24 23.1046 23.1046 24 22 24H10C8.89543 24 8 23.1046 8 22V20H1.5C0.671572 20 0 19.3284 0 18.5V5.34711C0 4.93755 0.167468 4.5458 0.463528 4.2628L4.4882 0.415691C4.76731 0.148892 5.13856 0 5.52467 0H14.5ZM22 6V22H10V11.2H14.9999C15.2652 11.2 15.5195 11.0946 15.707 10.9071C15.8946 10.7195 15.9999 10.4652 15.9999 10.2V6H22ZM13.9999 6H13.7252L10.3775 9.19997H13.9999V6Z"
fillRule="evenodd"
/>
</svg>
);

IconCopy.propTypes = {
className: PropTypes.string,
};

IconCopy.defaultProps = {
className: '',
};

export default IconCopy;
5 changes: 5 additions & 0 deletions src/state/network/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const initialState = new Map({
showReqDetail: false,
reqDetail: null,
tableHeaderWidth: '100%',
numberOfNewEntries: 0,
});

const reducer = (state = initialState, {
Expand Down Expand Up @@ -58,11 +59,15 @@ const reducer = (state = initialState, {
search: state.get('search'),
});

const prevData = state.get('data');
const numberOfNewEntries = filteredData.size - prevData.size;

newState
.set('error', null)
.set('rawData', payload)
.set('data', filteredData)
.set('actualData', sortedData)
.set('numberOfNewEntries', numberOfNewEntries)
.set('totalNetworkTime', totalNetworkTime)
.set('dataSummary', new Map({
totalRequests,
Expand Down
Loading

0 comments on commit 0aa23d7

Please sign in to comment.