@parca/profile 0.14.30 → 0.14.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/package.json +2 -2
- package/src/IcicleGraph.tsx +6 -2
- package/src/ProfileView.tsx +5 -5
- package/src/ProfileViewWithData.tsx +2 -2
- package/src/TopTable.tsx +16 -8
- package/src/components/DiffLegend.tsx +1 -1
- package/src/components/ProfileShareButton/index.tsx +2 -2
- package/src/useDelayedLoader.ts +3 -3
- package/src/useQuery.tsx +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.14.31](https://github.com/parca-dev/parca/compare/ui-v0.14.30...ui-v0.14.31) (2022-08-18)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **ui:** address all problems reported by ESLint ([7f9c921](https://github.com/parca-dev/parca/commit/7f9c9217af49f2661a4363b9b17a9a06f1c7539b))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [0.14.30](https://github.com/parca-dev/parca/compare/ui-v0.14.29...ui-v0.14.30) (2022-08-17)
|
|
7
18
|
|
|
8
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.31",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@iconify/react": "^3.2.2",
|
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
"access": "public",
|
|
23
23
|
"registry": "https://registry.npmjs.org/"
|
|
24
24
|
},
|
|
25
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "ad5f13b18fb7c5dd70df3dc6b40477e44f839381"
|
|
26
26
|
}
|
package/src/IcicleGraph.tsx
CHANGED
|
@@ -111,7 +111,11 @@ function IcicleRect({
|
|
|
111
111
|
height={height - 1}
|
|
112
112
|
style={{
|
|
113
113
|
opacity:
|
|
114
|
-
|
|
114
|
+
currentSearchString !== undefined &&
|
|
115
|
+
currentSearchString !== '' &&
|
|
116
|
+
!isSearchMatch(currentSearchString, name)
|
|
117
|
+
? 0.5
|
|
118
|
+
: 1,
|
|
115
119
|
fill: color,
|
|
116
120
|
}}
|
|
117
121
|
/>
|
|
@@ -130,7 +134,7 @@ export function nodeLabel(node: FlamegraphNode): string {
|
|
|
130
134
|
if (node.meta === undefined) return '<unknown>';
|
|
131
135
|
const mapping = `${
|
|
132
136
|
node.meta?.mapping?.file !== undefined && node.meta?.mapping?.file !== ''
|
|
133
|
-
? '[' + getLastItem(node.meta.mapping.file) + '] '
|
|
137
|
+
? '[' + (getLastItem(node.meta.mapping.file) ?? '') + '] '
|
|
134
138
|
: ''
|
|
135
139
|
}`;
|
|
136
140
|
if (node.meta.function?.name !== undefined && node.meta.function?.name !== '')
|
package/src/ProfileView.tsx
CHANGED
|
@@ -70,7 +70,7 @@ export const useProfileVisState = (): ProfileVisState => {
|
|
|
70
70
|
const router = parseParams(window.location.search);
|
|
71
71
|
const currentViewFromURL = router.currentProfileView as string;
|
|
72
72
|
const [currentView, setCurrentView] = useState<VisualizationType>(
|
|
73
|
-
(currentViewFromURL as VisualizationType)
|
|
73
|
+
(currentViewFromURL as VisualizationType) ?? 'icicle'
|
|
74
74
|
);
|
|
75
75
|
|
|
76
76
|
return {currentView, setCurrentView};
|
|
@@ -98,13 +98,13 @@ export const ProfileView = ({
|
|
|
98
98
|
|
|
99
99
|
const isLoading = useMemo(() => {
|
|
100
100
|
if (currentView === 'icicle') {
|
|
101
|
-
return
|
|
101
|
+
return Boolean(flamegraphData?.loading);
|
|
102
102
|
}
|
|
103
103
|
if (currentView === 'table') {
|
|
104
|
-
return
|
|
104
|
+
return Boolean(topTableData?.loading);
|
|
105
105
|
}
|
|
106
106
|
if (currentView === 'both') {
|
|
107
|
-
return
|
|
107
|
+
return Boolean(flamegraphData?.loading) || Boolean(topTableData?.loading);
|
|
108
108
|
}
|
|
109
109
|
return false;
|
|
110
110
|
}, [currentView, flamegraphData?.loading, topTableData?.loading]);
|
|
@@ -176,7 +176,7 @@ export const ProfileView = ({
|
|
|
176
176
|
/>
|
|
177
177
|
) : null}
|
|
178
178
|
|
|
179
|
-
<Button color="neutral" onClick={downloadPProf}>
|
|
179
|
+
<Button color="neutral" onClick={void downloadPProf}>
|
|
180
180
|
Download pprof
|
|
181
181
|
</Button>
|
|
182
182
|
</div>
|
|
@@ -38,7 +38,7 @@ export const ProfileViewWithData = ({
|
|
|
38
38
|
response: flamegraphResponse,
|
|
39
39
|
error: flamegraphError,
|
|
40
40
|
} = useQuery(queryClient, profileSource, QueryRequest_ReportType.FLAMEGRAPH_UNSPECIFIED, {
|
|
41
|
-
skip: currentView
|
|
41
|
+
skip: currentView !== 'icicle' && currentView !== 'both',
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
const {
|
|
@@ -46,7 +46,7 @@ export const ProfileViewWithData = ({
|
|
|
46
46
|
response: topTableResponse,
|
|
47
47
|
error: topTableError,
|
|
48
48
|
} = useQuery(queryClient, profileSource, QueryRequest_ReportType.TOP, {
|
|
49
|
-
skip: currentView
|
|
49
|
+
skip: currentView !== 'table' && currentView !== 'both',
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
const sampleUnit = profileSource.ProfileType().sampleUnit;
|
package/src/TopTable.tsx
CHANGED
|
@@ -57,7 +57,7 @@ const useSortableData = (top?: Top, config = {key: 'cumulative', direction: 'des
|
|
|
57
57
|
}));
|
|
58
58
|
|
|
59
59
|
const sortedItems = React.useMemo(() => {
|
|
60
|
-
if (
|
|
60
|
+
if (items.length === 0) return;
|
|
61
61
|
|
|
62
62
|
const sortableItems = [...items];
|
|
63
63
|
if (sortConfig !== null) {
|
|
@@ -74,7 +74,7 @@ const useSortableData = (top?: Top, config = {key: 'cumulative', direction: 'des
|
|
|
74
74
|
return sortableItems;
|
|
75
75
|
}, [items, sortConfig]);
|
|
76
76
|
|
|
77
|
-
const requestSort = key => {
|
|
77
|
+
const requestSort = (key: string) => {
|
|
78
78
|
let direction = 'desc';
|
|
79
79
|
if (sortConfig != null && sortConfig.key === key && sortConfig.direction === 'desc') {
|
|
80
80
|
direction = 'asc';
|
|
@@ -89,7 +89,7 @@ export const RowLabel = (meta: TopNodeMeta | undefined): string => {
|
|
|
89
89
|
if (meta === undefined) return '<unknown>';
|
|
90
90
|
const mapping = `${
|
|
91
91
|
meta?.mapping?.file !== undefined && meta?.mapping?.file !== ''
|
|
92
|
-
? `[${getLastItem(meta.mapping.file)}]`
|
|
92
|
+
? `[${getLastItem(meta.mapping.file) ?? ''}]`
|
|
93
93
|
: ''
|
|
94
94
|
}`;
|
|
95
95
|
if (meta.function?.name !== undefined && meta.function?.name !== '')
|
|
@@ -112,7 +112,7 @@ export const TopTable = ({data: top, sampleUnit}: TopTableProps): JSX.Element =>
|
|
|
112
112
|
const total = top != null ? top.list.length : 0;
|
|
113
113
|
if (total === 0) return <>Profile has no samples</>;
|
|
114
114
|
|
|
115
|
-
const getClassNamesFor = name => {
|
|
115
|
+
const getClassNamesFor = (name: string) => {
|
|
116
116
|
if (sortConfig == null) {
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
@@ -138,7 +138,9 @@ export const TopTable = ({data: top, sampleUnit}: TopTableProps): JSX.Element =>
|
|
|
138
138
|
onClick={() => requestSort('name')}
|
|
139
139
|
>
|
|
140
140
|
Name
|
|
141
|
-
<span
|
|
141
|
+
<span
|
|
142
|
+
className={`inline-block align-middle ml-2 ${getClassNamesFor('name') ?? ''}`}
|
|
143
|
+
>
|
|
142
144
|
<Arrow direction={getClassNamesFor('name')} />
|
|
143
145
|
</span>
|
|
144
146
|
</th>
|
|
@@ -147,7 +149,9 @@ export const TopTable = ({data: top, sampleUnit}: TopTableProps): JSX.Element =>
|
|
|
147
149
|
onClick={() => requestSort('flat')}
|
|
148
150
|
>
|
|
149
151
|
Flat
|
|
150
|
-
<span
|
|
152
|
+
<span
|
|
153
|
+
className={`inline-block align-middle ml-2 ${getClassNamesFor('flat') ?? ''}`}
|
|
154
|
+
>
|
|
151
155
|
<Arrow direction={getClassNamesFor('flat')} />
|
|
152
156
|
</span>
|
|
153
157
|
</th>
|
|
@@ -157,7 +161,9 @@ export const TopTable = ({data: top, sampleUnit}: TopTableProps): JSX.Element =>
|
|
|
157
161
|
>
|
|
158
162
|
Cumulative
|
|
159
163
|
<span
|
|
160
|
-
className={`inline-block align-middle ml-2 ${
|
|
164
|
+
className={`inline-block align-middle ml-2 ${
|
|
165
|
+
getClassNamesFor('cumulative') ?? ''
|
|
166
|
+
}`}
|
|
161
167
|
>
|
|
162
168
|
<Arrow direction={getClassNamesFor('cumulative')} />
|
|
163
169
|
</span>
|
|
@@ -168,7 +174,9 @@ export const TopTable = ({data: top, sampleUnit}: TopTableProps): JSX.Element =>
|
|
|
168
174
|
onClick={() => requestSort('diff')}
|
|
169
175
|
>
|
|
170
176
|
Diff
|
|
171
|
-
<span
|
|
177
|
+
<span
|
|
178
|
+
className={`inline-block align-middle ml-2 ${getClassNamesFor('diff') ?? ''}`}
|
|
179
|
+
>
|
|
172
180
|
<Arrow direction={getClassNamesFor('diff')} />
|
|
173
181
|
</span>
|
|
174
182
|
</th>
|
|
@@ -59,7 +59,7 @@ const DiffLegend = () => {
|
|
|
59
59
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
60
60
|
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null);
|
|
61
61
|
|
|
62
|
-
const {styles, attributes
|
|
62
|
+
const {styles, attributes} = usePopper(referenceElement, popperElement, {
|
|
63
63
|
placement: 'auto-start',
|
|
64
64
|
strategy: 'absolute',
|
|
65
65
|
});
|
|
@@ -43,7 +43,7 @@ const ProfileShareModal = ({
|
|
|
43
43
|
const metadata = useGrpcMetadata();
|
|
44
44
|
const isFormDataValid = () => true;
|
|
45
45
|
|
|
46
|
-
const handleSubmit: () => void = async () => {
|
|
46
|
+
const handleSubmit: () => Promise<void> = async () => {
|
|
47
47
|
try {
|
|
48
48
|
setLoading(true);
|
|
49
49
|
const {response} = await queryClient.shareProfile(
|
|
@@ -89,7 +89,7 @@ const ProfileShareModal = ({
|
|
|
89
89
|
className="w-fit mt-4"
|
|
90
90
|
onClick={e => {
|
|
91
91
|
e.preventDefault();
|
|
92
|
-
handleSubmit();
|
|
92
|
+
void handleSubmit();
|
|
93
93
|
}}
|
|
94
94
|
disabled={loading || !isFormDataValid()}
|
|
95
95
|
type="submit"
|
package/src/useDelayedLoader.ts
CHANGED
|
@@ -18,10 +18,10 @@ interface DelayedLoaderOptions {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const useDelayedLoader = (isLoading = false, options?: DelayedLoaderOptions) => {
|
|
21
|
-
const {delay = 500} = options
|
|
21
|
+
const {delay = 500} = options ?? {};
|
|
22
22
|
const [isLoaderVisible, setIsLoaderVisible] = useState<boolean>(false);
|
|
23
23
|
useEffect(() => {
|
|
24
|
-
let showLoaderTimeout
|
|
24
|
+
let showLoaderTimeout: ReturnType<typeof setTimeout>;
|
|
25
25
|
if (isLoading && !isLoaderVisible) {
|
|
26
26
|
// if the request takes longer than half a second, show the loading icon
|
|
27
27
|
showLoaderTimeout = setTimeout(() => {
|
|
@@ -31,7 +31,7 @@ const useDelayedLoader = (isLoading = false, options?: DelayedLoaderOptions) =>
|
|
|
31
31
|
setIsLoaderVisible(false);
|
|
32
32
|
}
|
|
33
33
|
return () => clearTimeout(showLoaderTimeout);
|
|
34
|
-
}, [isLoading]);
|
|
34
|
+
}, [isLoading, isLoaderVisible, delay]);
|
|
35
35
|
|
|
36
36
|
return isLoaderVisible;
|
|
37
37
|
};
|
package/src/useQuery.tsx
CHANGED
|
@@ -35,7 +35,7 @@ export const useQuery = (
|
|
|
35
35
|
reportType: QueryRequest_ReportType,
|
|
36
36
|
options?: UseQueryOptions
|
|
37
37
|
): IQueryResult => {
|
|
38
|
-
const {skip = false} = options
|
|
38
|
+
const {skip = false} = options ?? {};
|
|
39
39
|
const [result, setResult] = useState<IQueryResult>({
|
|
40
40
|
response: null,
|
|
41
41
|
error: null,
|
|
@@ -60,7 +60,7 @@ export const useQuery = (
|
|
|
60
60
|
call.response
|
|
61
61
|
.then(response => setResult({response, error: null, isLoading: false}))
|
|
62
62
|
.catch(error => setResult({error, response: null, isLoading: false}));
|
|
63
|
-
}, [client, profileSource, metadata, reportType]);
|
|
63
|
+
}, [skip, client, profileSource, metadata, reportType]);
|
|
64
64
|
|
|
65
65
|
return result;
|
|
66
66
|
};
|