Skip to content

Commit 69c2888

Browse files
Merge pull request #778 from RedisInsight/feature/RI-2988_pubsub_debug_mode
#RI-2988 - PubSub debug mode
2 parents a414e85 + 907c1cf commit 69c2888

File tree

5 files changed

+206
-58
lines changed

5 files changed

+206
-58
lines changed
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import React from 'react'
2-
import { instance, mock } from 'ts-mockito'
2+
import { mock } from 'ts-mockito'
33
import { render } from 'uiSrc/utils/test-utils'
44

55
import MessagesList, { Props } from './MessagesList'
66

7-
const mockedProps = mock<Props>()
7+
const mockedProps = {
8+
...mock<Props>(),
9+
height: 20,
10+
width: 20
11+
}
812

913
describe('MessagesList', () => {
1014
it('should render', () => {
1115
expect(
12-
render(<MessagesList {...instance(mockedProps)} />)
16+
render(<MessagesList {...mockedProps} />)
1317
).toBeTruthy()
1418
})
1519
})

redisinsight/ui/src/pages/pubSub/components/messages-list/MessagesList/MessagesList.tsx

Lines changed: 118 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,149 @@
1-
import React, { useEffect, useRef } from 'react'
2-
import { CellMeasurer, CellMeasurerCache, List, ListRowProps } from 'react-virtualized'
3-
import AutoSizer from 'react-virtualized-auto-sizer'
1+
import React, { useCallback, useEffect, useRef, useState } from 'react'
2+
import { ListChildComponentProps, ListOnScrollProps, VariableSizeList as List } from 'react-window'
3+
import { EuiButton, EuiIcon } from '@elastic/eui'
44

55
import { getFormatDateTime } from 'uiSrc/utils'
66
import { IMessage } from 'apiSrc/modules/pub-sub/interfaces/message.interface'
7+
78
import styles from './styles.module.scss'
89

910
export interface Props {
1011
items: IMessage[]
12+
width: number
13+
height: number
1114
}
1215

1316
const PROTRUDING_OFFSET = 2
17+
const MIN_ROW_HEIGHT = 30
1418

1519
const MessagesList = (props: Props) => {
16-
const { items = [] } = props
17-
18-
const cache = new CellMeasurerCache({
19-
defaultHeight: 17,
20-
fixedWidth: true,
21-
fixedHeight: false
22-
})
20+
const { items = [], width = 0, height = 0 } = props
2321

22+
const [showAnchor, setShowAnchor] = useState<boolean>(false)
2423
const listRef = useRef<List>(null)
24+
const followRef = useRef<boolean>(true)
25+
const hasMountedRef = useRef<boolean>(false)
26+
const rowHeights = useRef<{ [key: number]: number }>({})
27+
const outerRef = useRef<HTMLDivElement>(null)
28+
29+
useEffect(() => {
30+
scrollToBottom()
31+
}, [])
2532

2633
useEffect(() => {
27-
clearCacheAndUpdate()
34+
if (items.length > 0 && followRef.current) {
35+
setTimeout(() => {
36+
scrollToBottom()
37+
}, 0)
38+
}
2839
}, [items])
2940

30-
const clearCacheAndUpdate = () => {
31-
listRef?.current?.scrollToRow(items.length - 1)
41+
useEffect(() => {
42+
if (followRef.current) {
43+
setTimeout(() => {
44+
scrollToBottom()
45+
}, 0)
46+
}
47+
}, [width, height])
48+
49+
const getRowHeight = (index: number) => (
50+
rowHeights.current[index] > MIN_ROW_HEIGHT ? (rowHeights.current[index] + 2) : MIN_ROW_HEIGHT
51+
)
52+
53+
const setRowHeight = (index: number, size: number) => {
54+
listRef.current?.resetAfterIndex(0)
55+
rowHeights.current = { ...rowHeights.current, [index]: size }
56+
}
57+
58+
const scrollToBottom = () => {
59+
listRef.current?.scrollToItem(items.length - 1, 'end')
3260
requestAnimationFrame(() => {
33-
listRef?.current?.scrollToRow(items.length - 1)
61+
listRef.current?.scrollToItem(items.length - 1, 'end')
3462
})
3563
}
3664

37-
const rowRenderer = ({ parent, index, key, style }: ListRowProps) => {
38-
const { time = 0, channel = '', message = '' } = items[index]
65+
// TODO: delete after manual tests
66+
// const scrollToBottomReserve = () => {
67+
// const { scrollHeight = 0, offsetHeight = 0 } = outerRef.current || {}
68+
69+
// listRef.current?.scrollTo(scrollHeight - offsetHeight)
70+
// requestAnimationFrame(() => {
71+
// listRef.current?.scrollTo(scrollHeight - offsetHeight)
72+
// })
73+
// }
74+
75+
const handleAnchorClick = () => {
76+
scrollToBottom()
77+
}
78+
79+
const handleScroll = useCallback((e: ListOnScrollProps) => {
80+
if (!hasMountedRef.current) {
81+
hasMountedRef.current = true
82+
return
83+
}
84+
85+
if (e.scrollUpdateWasRequested === false) {
86+
followRef.current = false
87+
setShowAnchor(true)
88+
}
89+
90+
if (!outerRef.current) {
91+
return
92+
}
93+
94+
if (e.scrollOffset + outerRef.current.offsetHeight === outerRef.current.scrollHeight) {
95+
followRef.current = true
96+
setShowAnchor(false)
97+
}
98+
}, [])
99+
100+
const Row = ({ index, style }: ListChildComponentProps) => {
101+
const rowRef = useRef<HTMLDivElement>(null)
102+
103+
useEffect(() => {
104+
if (rowRef.current) {
105+
setRowHeight(index, rowRef.current?.clientHeight)
106+
}
107+
}, [rowRef])
108+
109+
const { channel, message, time } = items[index]
110+
39111
return (
40-
<CellMeasurer
41-
cache={cache}
42-
columnIndex={0}
43-
key={key}
44-
parent={parent}
45-
rowIndex={index}
46-
>
47-
{({ registerChild, measure }) => (
48-
<div onLoad={measure} className={styles.item} ref={registerChild} style={style}>
49-
<div className={styles.time}>{getFormatDateTime(time)}</div>
50-
<div className={styles.channel}>{channel}</div>
51-
<div className={styles.message}>{message}</div>
52-
</div>
53-
)}
54-
</CellMeasurer>
112+
<div style={style} className={styles.item} data-testid={`row-${index}`}>
113+
<div className={styles.time}>{getFormatDateTime(time)}</div>
114+
<div className={styles.channel}>{channel}</div>
115+
<div className={styles.message} ref={rowRef}>{message}</div>
116+
</div>
55117
)
56118
}
57119

58120
return (
59121
<>
60-
<div className={styles.header} data-testid="messages-list">
61-
<div className={styles.time}>Timestamp</div>
62-
<div className={styles.channel}>Channel</div>
63-
<div className={styles.message}>Message</div>
64-
</div>
65-
<AutoSizer>
66-
{({ width, height }) => (
67-
<List
68-
ref={listRef}
69-
width={width - PROTRUDING_OFFSET}
70-
height={height - PROTRUDING_OFFSET}
71-
rowCount={items.length}
72-
rowHeight={cache.rowHeight}
73-
rowRenderer={rowRenderer}
74-
overscanRowCount={30}
75-
className={styles.listWrapper}
76-
deferredMeasurementCache={cache}
77-
/>
78-
)}
79-
</AutoSizer>
122+
<List
123+
height={height}
124+
itemCount={items.length}
125+
itemSize={getRowHeight}
126+
ref={listRef}
127+
width={width - PROTRUDING_OFFSET}
128+
className={styles.listContent}
129+
outerRef={outerRef}
130+
onScroll={handleScroll}
131+
overscanCount={30}
132+
>
133+
{Row}
134+
</List>
135+
{showAnchor && (
136+
<EuiButton
137+
fill
138+
color="secondary"
139+
className={styles.anchorBtn}
140+
onClick={handleAnchorClick}
141+
data-testid="messages-list-anchor-btn"
142+
>
143+
New messages
144+
<EuiIcon type="sortDown" />
145+
</EuiButton>
146+
)}
80147
</>
81148
)
82149
}

redisinsight/ui/src/pages/pubSub/components/messages-list/MessagesList/styles.module.scss

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
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+
15
.time,
26
.channel,
37
.message {
@@ -26,10 +30,14 @@
2630
.message {
2731
width: calc(100% - 372px);
2832
color: var(--htmlColor);
29-
word-break: break-all;
33+
word-break: break-word;
3034
}
3135

3236
.header {
37+
width: 100%;
38+
display: flex;
39+
height: 24px;
40+
3341
.time,
3442
.channel,
3543
.message {
@@ -38,3 +46,49 @@
3846
color: var(--htmlColor);
3947
}
4048
}
49+
50+
.wrapperContainer {
51+
display: flex;
52+
flex-direction: column;
53+
height: 100%;
54+
width: 100%;
55+
}
56+
57+
.listContainer {
58+
height: 100%;
59+
width: 100%;
60+
padding-top: 14px;
61+
padding-right: 6px;
62+
display: flex;
63+
flex-direction: column;
64+
overflow: hidden;
65+
}
66+
67+
.listContent {
68+
@include euiScrollBar;
69+
}
70+
71+
.anchorBtn {
72+
position: absolute;
73+
z-index: 10;
74+
bottom: 10px;
75+
right: 28px;
76+
width: 149px !important;
77+
height: 36px !important;
78+
79+
box-shadow: 0px 3px 6px #00000099 !important;
80+
border-radius: 18px !important;
81+
82+
:global(.euiButton__text) {
83+
padding-left: 6px;
84+
font-size: 12px !important;
85+
font-weight: normal !important;
86+
}
87+
88+
svg {
89+
margin-left: 4px;
90+
margin-bottom: 1px;
91+
width: 20px;
92+
height: 20px;
93+
}
94+
}

redisinsight/ui/src/pages/pubSub/components/messages-list/MessagesListWrapper.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,40 @@
11
import React from 'react'
22
import { useSelector } from 'react-redux'
3+
import AutoSizer from 'react-virtualized-auto-sizer'
4+
35
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
46
import { pubSubSelector } from 'uiSrc/slices/pubsub/pubsub'
57
import EmptyMessagesList from './EmptyMessagesList'
68
import MessagesList from './MessagesList'
79

10+
import styles from './MessagesList/styles.module.scss'
11+
812
const MessagesListWrapper = () => {
913
const { messages = [], isSubscribed } = useSelector(pubSubSelector)
1014
const { connectionType } = useSelector(connectedInstanceSelector)
1115

1216
return (
1317
<>
14-
{(messages.length > 0 || isSubscribed) && <MessagesList items={messages} />}
18+
{(messages.length > 0 || isSubscribed) && (
19+
<div className={styles.wrapperContainer}>
20+
<div className={styles.header} data-testid="messages-list">
21+
<div className={styles.time}>Timestamp</div>
22+
<div className={styles.channel}>Channel</div>
23+
<div className={styles.message}>Message</div>
24+
</div>
25+
<div className={styles.listContainer}>
26+
<AutoSizer>
27+
{({ width, height }) => (
28+
<MessagesList
29+
items={messages}
30+
width={width}
31+
height={height}
32+
/>
33+
)}
34+
</AutoSizer>
35+
</div>
36+
</div>
37+
)}
1538
{messages.length === 0 && !isSubscribed && <EmptyMessagesList connectionType={connectionType} />}
1639
</>
1740
)

redisinsight/ui/src/pages/pubSub/styles.module.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434

3535
.tableWrapper {
3636
width: 100%;
37-
height: calc(100% - 135px);
38-
padding: 18px 0 18px 18px;
37+
height: calc(100% - 125px);
38+
padding: 18px 0 0 18px;
3939

4040
:global(.ReactVirtualized__Grid) {
4141
@include euiScrollBar;

0 commit comments

Comments
 (0)