Skip to content

Commit ca23c14

Browse files
refactor: migrate ExecuteResult to ts (#561)
1 parent a65cfa1 commit ca23c14

File tree

8 files changed

+83
-45
lines changed

8 files changed

+83
-45
lines changed

src/components/QueryExecutionStatus/QueryExecutionStatus.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ const b = cn('kv-query-execution-status');
1414

1515
interface QueryExecutionStatusProps {
1616
className?: string;
17-
error?: AxiosError | Record<string, any>;
17+
// TODO: Remove Record<string, any> when ECONNABORTED error case is fully typed
18+
error?: AxiosError | Record<string, any> | string;
1819
}
1920

2021
export const QueryExecutionStatus = ({className, error}: QueryExecutionStatusProps) => {
2122
let icon: ReactNode;
2223
let label: string;
2324

24-
if (error?.code === 'ECONNABORTED') {
25+
if (typeof error === 'object' && error?.code === 'ECONNABORTED') {
2526
icon = <UiKitIcon data={questionIcon} size={16} />;
2627
label = 'Connection aborted';
2728
} else {

src/containers/Tenant/Diagnostics/Describe/Describe.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {useCallback, useEffect, useState} from 'react';
22
import {shallowEqual, useDispatch} from 'react-redux';
33
import cn from 'bem-cn-lite';
4-
// @ts-ignore
54
import JSONTree from 'react-json-inspector';
65
import 'react-json-inspector/json-inspector.css';
76

@@ -99,14 +98,14 @@ const Describe = ({tenant, type}: IDescribeProps) => {
9998
<JSONTree
10099
data={preparedDescribeData}
101100
className={b('tree')}
102-
onClick={({path}: {path: string}) => {
101+
onClick={({path}) => {
103102
const newValue = !(expandMap.get(path) || false);
104103
expandMap.set(path, newValue);
105104
}}
106105
searchOptions={{
107106
debounceTime: 300,
108107
}}
109-
isExpanded={(keypath: string) => {
108+
isExpanded={(keypath) => {
110109
return expandMap.get(keypath) || false;
111110
}}
112111
/>

src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/IssuesViewer/IssueTree.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {useCallback, useState} from 'react';
22
import cn from 'bem-cn-lite';
33
import _omit from 'lodash/omit';
44

5-
// @ts-ignore
65
import JSONTree from 'react-json-inspector';
76

87
import {TreeView} from 'ydb-ui-components';

src/containers/Tenant/Query/ExecuteResult/ExecuteResult.js renamed to src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, {useEffect, useState} from 'react';
2-
import {useDispatch, useSelector} from 'react-redux';
1+
import React, {type ReactNode, useEffect, useState} from 'react';
2+
import {useDispatch} from 'react-redux';
33
import cn from 'bem-cn-lite';
44
import JSONTree from 'react-json-inspector';
55

@@ -11,13 +11,15 @@ import EnableFullscreenButton from '../../../../components/EnableFullscreenButto
1111
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
1212
import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
1313

14+
import type {ValueOf} from '../../../../types/common';
15+
import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query';
1416
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
15-
1617
import {prepareQueryError} from '../../../../utils/query';
18+
import {useTypedSelector} from '../../../../utils/hooks';
1719

1820
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
1921

20-
import ResultIssues from '../Issues/Issues';
22+
import {ResultIssues} from '../Issues/Issues';
2123
import {QueryDuration} from '../QueryDuration/QueryDuration';
2224

2325
import './ExecuteResult.scss';
@@ -27,31 +29,51 @@ const b = cn('ydb-query-execute-result');
2729
const resultOptionsIds = {
2830
result: 'result',
2931
stats: 'stats',
30-
};
32+
} as const;
33+
34+
type SectionID = ValueOf<typeof resultOptionsIds>;
3135

3236
const resultOptions = [
3337
{value: resultOptionsIds.result, content: 'Result'},
3438
{value: resultOptionsIds.stats, content: 'Stats'},
3539
];
3640

37-
export function ExecuteResult(props) {
38-
const [activeSection, setActiveSection] = useState(resultOptionsIds.result);
39-
const isFullscreen = useSelector((state) => state.fullscreen);
41+
interface ExecuteResultProps {
42+
textResults: string;
43+
result: ReactNode;
44+
stats: IQueryResult['stats'] | undefined;
45+
error: string | QueryErrorResponse | undefined;
46+
copyDisabled?: boolean;
47+
isResultsCollapsed?: boolean;
48+
onCollapseResults: VoidFunction;
49+
onExpandResults: VoidFunction;
50+
}
51+
52+
export function ExecuteResult({
53+
textResults,
54+
result,
55+
stats,
56+
error,
57+
copyDisabled,
58+
isResultsCollapsed,
59+
onCollapseResults,
60+
onExpandResults,
61+
}: ExecuteResultProps) {
62+
const [activeSection, setActiveSection] = useState<SectionID>(resultOptionsIds.result);
63+
const isFullscreen = useTypedSelector((state) => state.fullscreen);
4064
const dispatch = useDispatch();
4165

4266
useEffect(() => {
4367
return () => {
4468
dispatch(disableFullscreen());
4569
};
46-
}, []);
70+
}, [dispatch]);
4771

48-
const onSelectSection = (value) => {
49-
setActiveSection(value);
72+
const onSelectSection = (value: string) => {
73+
setActiveSection(value as SectionID);
5074
};
5175

5276
const renderClipboardButton = () => {
53-
const {textResults, copyDisabled} = props;
54-
5577
return (
5678
<CopyToClipboard
5779
text={textResults}
@@ -65,7 +87,7 @@ export function ExecuteResult(props) {
6587
const renderStats = () => {
6688
const content = (
6789
<JSONTree
68-
data={props.stats}
90+
data={stats}
6991
isExpanded={() => true}
7092
className={b('inspector')}
7193
searchOptions={{
@@ -86,8 +108,6 @@ export function ExecuteResult(props) {
86108
};
87109

88110
const renderResult = () => {
89-
const {result} = props;
90-
91111
return (
92112
<React.Fragment>
93113
{result}
@@ -101,11 +121,11 @@ export function ExecuteResult(props) {
101121
};
102122

103123
const renderIssues = () => {
104-
const error = props.error;
105-
106-
const hasIssues = error?.data?.issues && Array.isArray(error.data.issues);
124+
if (!error) {
125+
return null;
126+
}
107127

108-
if (hasIssues) {
128+
if (typeof error === 'object' && error.data?.issues && Array.isArray(error.data.issues)) {
109129
return (
110130
<React.Fragment>
111131
<ResultIssues data={error.data} />
@@ -120,20 +140,20 @@ export function ExecuteResult(props) {
120140
);
121141
}
122142

123-
if (error) {
124-
return <div className={b('error')}>{prepareQueryError(error)}</div>;
125-
}
143+
const parsedError = typeof error === 'string' ? error : prepareQueryError(error);
144+
145+
return <div className={b('error')}>{parsedError}</div>;
126146
};
127147

128148
return (
129149
<React.Fragment>
130150
<div className={b('controls')}>
131151
<div className={b('controls-right')}>
132-
<QueryExecutionStatus error={props.error} />
152+
<QueryExecutionStatus error={error} />
133153

134-
{props.stats && !props.error && (
154+
{stats && !error && (
135155
<React.Fragment>
136-
<QueryDuration duration={props.stats?.DurationUs} />
156+
<QueryDuration duration={stats?.DurationUs} />
137157
<Divider />
138158
<RadioButton
139159
options={resultOptions}
@@ -147,16 +167,16 @@ export function ExecuteResult(props) {
147167
{renderClipboardButton()}
148168
<EnableFullscreenButton />
149169
<PaneVisibilityToggleButtons
150-
onCollapse={props.onCollapseResults}
151-
onExpand={props.onExpandResults}
152-
isCollapsed={props.isResultsCollapsed}
170+
onCollapse={onCollapseResults}
171+
onExpand={onExpandResults}
172+
isCollapsed={isResultsCollapsed}
153173
initialDirection="bottom"
154174
/>
155175
</div>
156176
</div>
157177
<div className={b('result')}>
158-
{activeSection === resultOptionsIds.result && !props.error && renderResult()}
159-
{activeSection === resultOptionsIds.stats && !props.error && renderStats()}
178+
{activeSection === resultOptionsIds.result && !error && renderResult()}
179+
{activeSection === resultOptionsIds.stats && !error && renderStats()}
160180
{renderIssues()}
161181
</div>
162182
</React.Fragment>

src/containers/Tenant/Query/Issues/Issues.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ const blockIssue = cn('kv-issue');
2121

2222
interface ResultIssuesProps {
2323
data: ErrorResponse | string;
24-
className: string;
2524
}
2625

27-
export default function ResultIssues({data, className}: ResultIssuesProps) {
26+
export function ResultIssues({data}: ResultIssuesProps) {
2827
const [showIssues, setShowIssues] = React.useState(false);
2928

3029
const issues = typeof data === 'string' ? undefined : data?.issues;
@@ -59,22 +58,21 @@ export default function ResultIssues({data, className}: ResultIssuesProps) {
5958
</Button>
6059
)}
6160
</div>
62-
{hasIssues && showIssues && <Issues issues={issues} className={className} />}
61+
{hasIssues && showIssues && <Issues issues={issues} />}
6362
</div>
6463
);
6564
}
6665

6766
interface IssuesProps {
68-
className?: string;
6967
issues: IssueMessage[] | null | undefined;
7068
}
71-
export function Issues({issues, className}: IssuesProps) {
69+
export function Issues({issues}: IssuesProps) {
7270
const mostSevereIssue = issues?.reduce((result, issue) => {
7371
const severity = issue.severity ?? 10;
7472
return Math.min(result, severity);
7573
}, 10);
7674
return (
77-
<div className={blockIssues(null, className)}>
75+
<div className={blockIssues(null)}>
7876
{issues?.map((issue, index) => (
7977
<Issue key={index} issue={issue} expanded={issue === mostSevereIssue} />
8078
))}

src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import i18n from '../i18n';
88
import './QueryDuration.scss';
99

1010
interface QueryDurationProps {
11-
duration?: string;
11+
duration?: string | number;
1212
}
1313

1414
const b = block('ydb-query-duration');

src/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export function paneVisibilityToggleReducerCreator(isPaneCollapsedKey: string) {
6767
interface ToggleButtonProps {
6868
onCollapse: VoidFunction;
6969
onExpand: VoidFunction;
70-
isCollapsed: boolean;
70+
isCollapsed?: boolean;
7171
initialDirection?: 'right' | 'left' | 'top' | 'bottom';
7272
className?: string;
7373
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
declare module 'react-json-inspector' {
2+
// This typing is sufficient for current use cases, but some types are incompelete
3+
class JSONTree extends React.Component<{
4+
data?: object;
5+
search?: boolean;
6+
searchOptions?: {
7+
debounceTime?: number;
8+
};
9+
onClick?: ({path: string, key: string, value: object}) => void;
10+
validateQuery?: (query: string) => boolean;
11+
isExpanded?: (keypath: string) => boolean;
12+
filterOptions?: {
13+
cacheResults?: bool;
14+
ignoreCase?: bool;
15+
};
16+
query?: string;
17+
verboseShowOriginal?: boolean;
18+
className?: string;
19+
}> {}
20+
export default JSONTree;
21+
}

0 commit comments

Comments
 (0)