Skip to content

Commit 4fad02f

Browse files
Merge pull request #1290 from RedisInsight/fe/feature/RI-3561_Virtual_list_for_Workbench
#RI-3561 - Handle big output in Workbench
2 parents e432e98 + 6df13a5 commit 4fad02f

File tree

20 files changed

+275
-64
lines changed

20 files changed

+275
-64
lines changed

redisinsight/ui/src/components/cli/components/cli-body/CliBody/styles.module.scss

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
@import '@elastic/eui/src/global_styling/mixins/helpers';
2-
@import '@elastic/eui/src/components/table/mixins';
3-
@import '@elastic/eui/src/global_styling/index';
1+
@import "@elastic/eui/src/global_styling/mixins/helpers";
2+
@import "@elastic/eui/src/components/table/mixins";
3+
@import "@elastic/eui/src/global_styling/index";
44

55
.section {
66
position: absolute;
@@ -52,4 +52,5 @@
5252

5353
:global(.cli-command-wrapper) {
5454
font: normal normal bold 14px/15px Inconsolata !important;
55+
white-space: pre-line;
5556
}

redisinsight/ui/src/components/query-card/QueryCard.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ const QueryCard = (props: Props) => {
186186
resultsMode={resultsMode}
187187
result={result}
188188
isNotStored={isNotStored}
189+
isFullScreen={isFullScreen}
189190
data-testid="group-mode-card"
190191
/>
191192
)}
@@ -215,6 +216,7 @@ const QueryCard = (props: Props) => {
215216
resultsMode={resultsMode}
216217
result={result}
217218
isNotStored={isNotStored}
219+
isFullScreen={isFullScreen}
218220
/>
219221
)}
220222
</>

redisinsight/ui/src/components/query-card/QueryCardCliDefaultResult/QueryCardCliDefaultResult.spec.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,4 @@ describe('QueryCardCliDefaultResult', () => {
99
it('should render', () => {
1010
expect(render(<QueryCardCliDefaultResult {...instance(mockedProps)} />)).toBeTruthy()
1111
})
12-
13-
it('Result element should render (nil) result', () => {
14-
const mockResult = [{
15-
response: '',
16-
status: 'success'
17-
}]
18-
19-
const { queryByTestId } = render(
20-
<QueryCardCliDefaultResult {...instance(mockedProps)} result={mockResult} />
21-
)
22-
23-
const resultEl = queryByTestId('query-cli-group-result')
24-
25-
expect(resultEl).toHaveTextContent('(nil)')
26-
})
2712
})
Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
import React from 'react'
2+
import cx from 'classnames'
23

3-
import { CommandExecutionResult } from 'uiSrc/slices/interfaces'
4-
import { cliParseTextResponse, CliPrefix, Maybe } from 'uiSrc/utils'
4+
import VirtualList from 'uiSrc/components/virtual-list'
5+
6+
import styles from './styles.module.scss'
57

68
export interface Props {
7-
query: string
8-
result: Maybe<CommandExecutionResult[]>
9+
items: (string | JSX.Element)[]
10+
isFullScreen?: boolean
911
}
1012

11-
const QueryCardCliGroupResult = (props: Props) => {
12-
const { result = [], query } = props
13+
export const MIN_ROWS_COUNT = 11
14+
export const MAX_CARD_HEIGHT = 210
15+
16+
const QueryCardCliDefaultResult = (props: Props) => {
17+
const { items = [], isFullScreen } = props
1318

1419
return (
15-
<div data-testid="query-cli-group-result">
16-
{result?.map(({ response, status }) =>
17-
cliParseTextResponse(response || '(nil)', query, status, CliPrefix.QueryCard))}
20+
<div
21+
className={cx(
22+
styles.container,
23+
'query-card-output-response-success',
24+
{ fullscreen: isFullScreen },
25+
)}
26+
data-testid="query-cli-card-result"
27+
>
28+
<VirtualList
29+
items={items}
30+
dynamicHeight={!isFullScreen ? { itemsCount: MIN_ROWS_COUNT, maxHeight: MAX_CARD_HEIGHT } : undefined}
31+
/>
1832
</div>
1933
)
2034
}
2135

22-
export default QueryCardCliGroupResult
36+
export default QueryCardCliDefaultResult
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@import "@elastic/eui/src/global_styling/mixins/helpers";
2+
@import "@elastic/eui/src/components/table/mixins";
3+
@import "@elastic/eui/src/global_styling/index";
4+
5+
.container {
6+
position: relative;
7+
width: 100%;
8+
display: flex;
9+
flex-grow: 1;
10+
height: 100%;
11+
}
12+
13+
.listContent {
14+
@include euiScrollBar;
15+
}

redisinsight/ui/src/components/query-card/QueryCardCliGroupResult/QueryCardCliGroupResult.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1+
import { flatten } from 'lodash'
12
import React from 'react'
23

34
import { CommandExecutionResult } from 'uiSrc/slices/interfaces'
45
import { cliParseCommandsGroupResult, Maybe } from 'uiSrc/utils'
6+
import QueryCardCliDefaultResult from '../QueryCardCliDefaultResult'
57

68
export interface Props {
79
result?: Maybe<CommandExecutionResult[]>
10+
isFullScreen?: boolean
811
}
912

1013
const QueryCardCliGroupResult = (props: Props) => {
11-
const { result = [] } = props
14+
const { result = [], isFullScreen } = props
1215
return (
1316
<div data-testid="query-cli-default-result" className="query-card-output-response-success">
14-
{result[0]?.response.map((item: any, index: number) =>
15-
cliParseCommandsGroupResult(item, index))}
17+
<QueryCardCliDefaultResult
18+
isFullScreen={isFullScreen}
19+
items={flatten(result?.[0]?.response.map((item: any) =>
20+
flatten(cliParseCommandsGroupResult(item))))}
21+
/>
1622
</div>
1723
)
1824
}

redisinsight/ui/src/components/query-card/QueryCardCliResultWrapper/QueryCardCliResultWrapper.spec.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react'
33
import { instance, mock } from 'ts-mockito'
44
import { cleanup, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
55
import { ResultsMode } from 'uiSrc/slices/interfaces/workbench'
6+
import { CommandExecutionStatus } from 'uiSrc/slices/interfaces/cli'
67
import QueryCardCliResultWrapper, { Props } from './QueryCardCliResultWrapper'
78
import QueryCardCliDefaultResult, { Props as QueryCardCliDefaultResultProps } from '../QueryCardCliDefaultResult'
89
import QueryCardCliGroupResult, { Props as QueryCardCliGroupResultProps } from '../QueryCardCliGroupResult'
@@ -28,13 +29,17 @@ jest.mock('uiSrc/services', () => ({
2829

2930
describe('QueryCardCliResultWrapper', () => {
3031
it('should render', () => {
31-
expect(render(<QueryCardCliResultWrapper {...instance(mockedProps)} />)).toBeTruthy()
32+
const mockResult = [{
33+
response: 'response',
34+
status: CommandExecutionStatus.Success
35+
}]
36+
expect(render(<QueryCardCliResultWrapper {...instance(mockedProps)} result={mockResult} />)).toBeTruthy()
3237
})
3338

3439
it('Result element should render with result prop', () => {
3540
const mockResult = [{
3641
response: 'response',
37-
status: 'success'
42+
status: CommandExecutionStatus.Success
3843
}]
3944

4045
render(
@@ -78,7 +83,7 @@ describe('QueryCardCliResultWrapper', () => {
7883
it('should render QueryCardCliDefaultResult when result.response is not array', () => {
7984
const mockResult = [{
8085
response: 'response',
81-
status: 'success'
86+
status: CommandExecutionStatus.Success
8287
}]
8388

8489
render(
@@ -97,10 +102,29 @@ describe('QueryCardCliResultWrapper', () => {
97102
})
98103

99104
it('should render warning', () => {
105+
const mockResult = [{
106+
response: 'response',
107+
status: CommandExecutionStatus.Success
108+
}]
100109
render(
101-
<QueryCardCliResultWrapper {...instance(mockedProps)} isNotStored />
110+
<QueryCardCliResultWrapper {...instance(mockedProps)} result={mockResult} isNotStored />
102111
)
103112

104113
expect(screen.queryByTestId('query-cli-warning')).toBeInTheDocument()
105114
})
115+
116+
it('Result element should render (nil) result', () => {
117+
const mockResult = [{
118+
response: '',
119+
status: CommandExecutionStatus.Success
120+
}]
121+
122+
const { queryByTestId } = render(
123+
<QueryCardCliResultWrapper {...instance(mockedProps)} result={mockResult} />
124+
)
125+
126+
const resultEl = queryByTestId('query-cli-card-result')
127+
128+
expect(resultEl).toHaveTextContent('(nil)')
129+
})
106130
})

redisinsight/ui/src/components/query-card/QueryCardCliResultWrapper/QueryCardCliResultWrapper.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { isArray } from 'lodash'
55

66
import { CommandExecutionResult } from 'uiSrc/slices/interfaces'
77
import { ResultsMode } from 'uiSrc/slices/interfaces/workbench'
8-
import { Maybe } from 'uiSrc/utils'
8+
import { formatToText, Maybe } from 'uiSrc/utils'
99

1010
import QueryCardCliDefaultResult from '../QueryCardCliDefaultResult'
1111
import QueryCardCliGroupResult from '../QueryCardCliGroupResult'
@@ -18,24 +18,30 @@ export interface Props {
1818
status?: string
1919
resultsMode?: ResultsMode
2020
isNotStored?: boolean
21+
isFullScreen?: boolean
2122
}
2223

2324
const QueryCardCliResultWrapper = (props: Props) => {
24-
const { result = [], query, loading, resultsMode, isNotStored } = props
25+
const { result = [], query, loading, resultsMode, isNotStored, isFullScreen } = props
2526

2627
return (
2728
<div className={cx('queryResultsContainer', styles.container)}>
2829
{!loading && (
29-
<div data-testid="query-cli-result">
30+
<div data-testid="query-cli-result" style={{ height: '100%' }}>
3031
{isNotStored && (
3132
<EuiText className={styles.alert} data-testid="query-cli-warning">
3233
<EuiIcon type="alert" className={styles.alertIcon} />
3334
The result is too big to be saved. It will be deleted after the application is closed.
3435
</EuiText>
3536
)}
3637
{resultsMode === ResultsMode.GroupMode && isArray(result[0]?.response)
37-
? <QueryCardCliGroupResult result={result} />
38-
: <QueryCardCliDefaultResult query={query} result={result} />}
38+
? <QueryCardCliGroupResult result={result} isFullScreen={isFullScreen} />
39+
: (
40+
<QueryCardCliDefaultResult
41+
isFullScreen={isFullScreen}
42+
items={formatToText(result[0].response || '(nil)', query).split('\n')}
43+
/>
44+
)}
3945
</div>
4046
)}
4147
{loading && (

redisinsight/ui/src/components/query-card/QueryCardCliResultWrapper/styles.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
overflow: auto;
1010
white-space: pre-wrap;
1111
word-break: break-all;
12+
position: relative;
1213

1314
font: normal normal normal 14px/17px Inconsolata;
1415
text-align: left;

redisinsight/ui/src/components/query-card/styles.module.scss

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
@import '@elastic/eui/src/global_styling/mixins/helpers';
2-
@import '@elastic/eui/src/components/table/mixins';
3-
@import '@elastic/eui/src/global_styling/index';
1+
@import "@elastic/eui/src/global_styling/mixins/helpers";
2+
@import "@elastic/eui/src/components/table/mixins";
3+
@import "@elastic/eui/src/global_styling/index";
44

55
.containerWrapper {
66
min-width: 420px;
@@ -38,6 +38,9 @@
3838

3939
.container {
4040
border-color: var(--tableLightestBorderColor);
41+
display: flex;
42+
flex-direction: column;
43+
height: 100%;
4144
}
4245

4346
&.isOpen .container {
@@ -61,8 +64,9 @@
6164
color: var(--cliOutputResponseColor) !important;
6265
}
6366

64-
:global(.fullscreen .query-card-output-response-success){
67+
:global(.fullscreen .query-card-output-response-success) {
6568
max-height: calc(100vh - 65px);
69+
display: flex;
6670
}
6771

6872
:global(.query-card-output-response-fail) {

0 commit comments

Comments
 (0)