From ed2534264db4aaf4cd6c2fc28e6fc98fe18e947e Mon Sep 17 00:00:00 2001 From: mufazalov Date: Wed, 18 Oct 2023 17:40:25 +0300 Subject: [PATCH 1/3] chore: add react-json-inspector types --- .../Tenant/Diagnostics/Describe/Describe.tsx | 5 ++--- .../Healthcheck/IssuesViewer/IssueTree.tsx | 1 - src/types/react-json-inspector.d.ts | 21 +++++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 src/types/react-json-inspector.d.ts diff --git a/src/containers/Tenant/Diagnostics/Describe/Describe.tsx b/src/containers/Tenant/Diagnostics/Describe/Describe.tsx index 3e4691797..f62174970 100644 --- a/src/containers/Tenant/Diagnostics/Describe/Describe.tsx +++ b/src/containers/Tenant/Diagnostics/Describe/Describe.tsx @@ -1,7 +1,6 @@ import {useCallback, useEffect, useState} from 'react'; import {shallowEqual, useDispatch} from 'react-redux'; import cn from 'bem-cn-lite'; -// @ts-ignore import JSONTree from 'react-json-inspector'; import 'react-json-inspector/json-inspector.css'; @@ -99,14 +98,14 @@ const Describe = ({tenant, type}: IDescribeProps) => { { + onClick={({path}) => { const newValue = !(expandMap.get(path) || false); expandMap.set(path, newValue); }} searchOptions={{ debounceTime: 300, }} - isExpanded={(keypath: string) => { + isExpanded={(keypath) => { return expandMap.get(keypath) || false; }} /> diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/IssuesViewer/IssueTree.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/IssuesViewer/IssueTree.tsx index b4f7fcbb9..9c6182067 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/IssuesViewer/IssueTree.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/IssuesViewer/IssueTree.tsx @@ -2,7 +2,6 @@ import {useCallback, useState} from 'react'; import cn from 'bem-cn-lite'; import _omit from 'lodash/omit'; -// @ts-ignore import JSONTree from 'react-json-inspector'; import {TreeView} from 'ydb-ui-components'; diff --git a/src/types/react-json-inspector.d.ts b/src/types/react-json-inspector.d.ts new file mode 100644 index 000000000..62b678c07 --- /dev/null +++ b/src/types/react-json-inspector.d.ts @@ -0,0 +1,21 @@ +declare module 'react-json-inspector' { + // This typing is sufficient for current use cases, but some types are incompelete + class JSONTree extends React.Component<{ + data?: object; + search?: boolean; + searchOptions?: { + debounceTime?: number; + }; + onClick?: ({path: string, key: string, value: object}) => void; + validateQuery?: (query: string) => boolean; + isExpanded?: (keypath: string) => boolean; + filterOptions?: { + cacheResults?: bool; + ignoreCase?: bool; + }; + query?: string; + verboseShowOriginal?: boolean; + className?: string; + }> {} + export default JSONTree; +} From aa407cce31d87b8f2cb5143e3ff62e5673cea28b Mon Sep 17 00:00:00 2001 From: mufazalov Date: Wed, 18 Oct 2023 17:43:48 +0300 Subject: [PATCH 2/3] refactor: migrate ExecuteResult to ts --- .../QueryExecutionStatus.tsx | 4 +- .../{ExecuteResult.js => ExecuteResult.tsx} | 80 ++++++++++++------- src/containers/Tenant/Query/Issues/Issues.tsx | 10 +-- .../Query/QueryDuration/QueryDuration.tsx | 2 +- .../utils/paneVisibilityToggleHelpers.tsx | 2 +- 5 files changed, 58 insertions(+), 40 deletions(-) rename src/containers/Tenant/Query/ExecuteResult/{ExecuteResult.js => ExecuteResult.tsx} (66%) diff --git a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx index 66d08ee4e..b703e3a2b 100644 --- a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx +++ b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx @@ -14,14 +14,14 @@ const b = cn('kv-query-execution-status'); interface QueryExecutionStatusProps { className?: string; - error?: AxiosError | Record; + error?: AxiosError | Record | string; } export const QueryExecutionStatus = ({className, error}: QueryExecutionStatusProps) => { let icon: ReactNode; let label: string; - if (error?.code === 'ECONNABORTED') { + if (typeof error === 'object' && error?.code === 'ECONNABORTED') { icon = ; label = 'Connection aborted'; } else { diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.js b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx similarity index 66% rename from src/containers/Tenant/Query/ExecuteResult/ExecuteResult.js rename to src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx index c46c6ce16..3175e3c3e 100644 --- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.js +++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx @@ -1,5 +1,5 @@ -import React, {useEffect, useState} from 'react'; -import {useDispatch, useSelector} from 'react-redux'; +import React, {type ReactNode, useEffect, useState} from 'react'; +import {useDispatch} from 'react-redux'; import cn from 'bem-cn-lite'; import JSONTree from 'react-json-inspector'; @@ -11,13 +11,15 @@ import EnableFullscreenButton from '../../../../components/EnableFullscreenButto import Fullscreen from '../../../../components/Fullscreen/Fullscreen'; import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus'; +import type {ValueOf} from '../../../../types/common'; +import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query'; import {disableFullscreen} from '../../../../store/reducers/fullscreen'; - import {prepareQueryError} from '../../../../utils/query'; +import {useTypedSelector} from '../../../../utils/hooks'; import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers'; -import ResultIssues from '../Issues/Issues'; +import {ResultIssues} from '../Issues/Issues'; import {QueryDuration} from '../QueryDuration/QueryDuration'; import './ExecuteResult.scss'; @@ -27,31 +29,51 @@ const b = cn('ydb-query-execute-result'); const resultOptionsIds = { result: 'result', stats: 'stats', -}; +} as const; + +type SectionID = ValueOf; const resultOptions = [ {value: resultOptionsIds.result, content: 'Result'}, {value: resultOptionsIds.stats, content: 'Stats'}, ]; -export function ExecuteResult(props) { - const [activeSection, setActiveSection] = useState(resultOptionsIds.result); - const isFullscreen = useSelector((state) => state.fullscreen); +interface ExecuteResultProps { + textResults: string; + result: ReactNode; + stats: IQueryResult['stats'] | undefined; + error: string | QueryErrorResponse | undefined; + copyDisabled?: boolean; + isResultsCollapsed?: boolean; + onCollapseResults: VoidFunction; + onExpandResults: VoidFunction; +} + +export function ExecuteResult({ + textResults, + result, + stats, + error, + copyDisabled, + isResultsCollapsed, + onCollapseResults, + onExpandResults, +}: ExecuteResultProps) { + const [activeSection, setActiveSection] = useState(resultOptionsIds.result); + const isFullscreen = useTypedSelector((state) => state.fullscreen); const dispatch = useDispatch(); useEffect(() => { return () => { dispatch(disableFullscreen()); }; - }, []); + }, [dispatch]); - const onSelectSection = (value) => { - setActiveSection(value); + const onSelectSection = (value: string) => { + setActiveSection(value as SectionID); }; const renderClipboardButton = () => { - const {textResults, copyDisabled} = props; - return ( { const content = ( true} className={b('inspector')} searchOptions={{ @@ -86,8 +108,6 @@ export function ExecuteResult(props) { }; const renderResult = () => { - const {result} = props; - return ( {result} @@ -101,11 +121,7 @@ export function ExecuteResult(props) { }; const renderIssues = () => { - const error = props.error; - - const hasIssues = error?.data?.issues && Array.isArray(error.data.issues); - - if (hasIssues) { + if (typeof error === 'object' && error?.data?.issues && Array.isArray(error.data.issues)) { return ( @@ -121,19 +137,23 @@ export function ExecuteResult(props) { } if (error) { - return
{prepareQueryError(error)}
; + const parsedError = typeof error === 'string' ? error : prepareQueryError(error); + + return
{parsedError}
; } + + return null; }; return (
- + - {props.stats && !props.error && ( + {stats && !error && ( - +
- {activeSection === resultOptionsIds.result && !props.error && renderResult()} - {activeSection === resultOptionsIds.stats && !props.error && renderStats()} + {activeSection === resultOptionsIds.result && !error && renderResult()} + {activeSection === resultOptionsIds.stats && !error && renderStats()} {renderIssues()}
diff --git a/src/containers/Tenant/Query/Issues/Issues.tsx b/src/containers/Tenant/Query/Issues/Issues.tsx index 50f92481a..4b7d7426e 100644 --- a/src/containers/Tenant/Query/Issues/Issues.tsx +++ b/src/containers/Tenant/Query/Issues/Issues.tsx @@ -21,10 +21,9 @@ const blockIssue = cn('kv-issue'); interface ResultIssuesProps { data: ErrorResponse | string; - className: string; } -export default function ResultIssues({data, className}: ResultIssuesProps) { +export function ResultIssues({data}: ResultIssuesProps) { const [showIssues, setShowIssues] = React.useState(false); const issues = typeof data === 'string' ? undefined : data?.issues; @@ -59,22 +58,21 @@ export default function ResultIssues({data, className}: ResultIssuesProps) { )} - {hasIssues && showIssues && } + {hasIssues && showIssues && } ); } interface IssuesProps { - className?: string; issues: IssueMessage[] | null | undefined; } -export function Issues({issues, className}: IssuesProps) { +export function Issues({issues}: IssuesProps) { const mostSevereIssue = issues?.reduce((result, issue) => { const severity = issue.severity ?? 10; return Math.min(result, severity); }, 10); return ( -
+
{issues?.map((issue, index) => ( ))} diff --git a/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx b/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx index e514dd4f6..1b067640f 100644 --- a/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +++ b/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx @@ -8,7 +8,7 @@ import i18n from '../i18n'; import './QueryDuration.scss'; interface QueryDurationProps { - duration?: string; + duration?: string | number; } const b = block('ydb-query-duration'); diff --git a/src/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx b/src/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx index e8dfc41cd..433fc0d00 100644 --- a/src/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx +++ b/src/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx @@ -67,7 +67,7 @@ export function paneVisibilityToggleReducerCreator(isPaneCollapsedKey: string) { interface ToggleButtonProps { onCollapse: VoidFunction; onExpand: VoidFunction; - isCollapsed: boolean; + isCollapsed?: boolean; initialDirection?: 'right' | 'left' | 'top' | 'bottom'; className?: string; } From f7a28830b4a3adc86e54ad8ede188c39c0c79012 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Wed, 18 Oct 2023 18:36:18 +0300 Subject: [PATCH 3/3] fixup! refactor: migrate ExecuteResult to ts --- .../QueryExecutionStatus/QueryExecutionStatus.tsx | 1 + .../Tenant/Query/ExecuteResult/ExecuteResult.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx index b703e3a2b..352d58780 100644 --- a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx +++ b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx @@ -14,6 +14,7 @@ const b = cn('kv-query-execution-status'); interface QueryExecutionStatusProps { className?: string; + // TODO: Remove Record when ECONNABORTED error case is fully typed error?: AxiosError | Record | string; } diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx index 3175e3c3e..cfe39c026 100644 --- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx @@ -121,7 +121,11 @@ export function ExecuteResult({ }; const renderIssues = () => { - if (typeof error === 'object' && error?.data?.issues && Array.isArray(error.data.issues)) { + if (!error) { + return null; + } + + if (typeof error === 'object' && error.data?.issues && Array.isArray(error.data.issues)) { return ( @@ -136,13 +140,9 @@ export function ExecuteResult({ ); } - if (error) { - const parsedError = typeof error === 'string' ? error : prepareQueryError(error); - - return
{parsedError}
; - } + const parsedError = typeof error === 'string' ? error : prepareQueryError(error); - return null; + return
{parsedError}
; }; return (