Skip to content

Commit ac8d699

Browse files
Merge pull request #348 from RedisInsight/feature/RI-985
#RI-985, #RI-2460
2 parents c8e1912 + 7ea3080 commit ac8d699

File tree

11 files changed

+259
-95
lines changed

11 files changed

+259
-95
lines changed
Lines changed: 8 additions & 0 deletions
Loading

redisinsight/ui/src/components/database-list-modules/DatabaseListModules.tsx

Lines changed: 86 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
/* eslint-disable sonarjs/no-nested-template-literals */
12
import React, { useContext } from 'react'
2-
import { EuiButton, EuiButtonIcon, EuiToolTip } from '@elastic/eui'
3+
import { EuiButtonIcon, EuiIcon, EuiTextColor, EuiToolTip } from '@elastic/eui'
34
import cx from 'classnames'
45

56
import {
@@ -31,16 +32,14 @@ import { RedisModuleDto } from 'apiSrc/modules/instances/dto/database-instance.d
3132
import styles from './styles.module.scss'
3233

3334
export interface Props {
34-
modules: RedisModuleDto[];
35-
inCircle?: boolean;
36-
dark?: boolean;
35+
modules: RedisModuleDto[]
36+
inCircle?: boolean
37+
dark?: boolean
38+
highlight?: boolean
39+
maxViewModules?: number
40+
tooltipTitle?: React.ReactNode
3741
}
3842

39-
interface ITooltipProps {
40-
icon: any;
41-
content: any;
42-
abbreviation?: string;
43-
}
4443
export const modulesDefaultInit = {
4544
[RedisDefaultModules.AI]: {
4645
iconDark: RedisAIDark,
@@ -79,68 +78,104 @@ export const modulesDefaultInit = {
7978
},
8079
}
8180

82-
const DatabaseListModules = React.memo(({ modules, inCircle }: Props) => {
81+
const DatabaseListModules = React.memo(({ modules, inCircle, highlight, tooltipTitle, maxViewModules }: Props) => {
8382
const { theme } = useContext(ThemeContext)
8483

84+
const mainContent = []
85+
8586
const handleCopy = (text = '') => {
8687
navigator?.clipboard?.writeText(text)
8788
}
8889

89-
const Tooltip = ({ icon, content, abbreviation }: ITooltipProps) => (
90-
<>
91-
<EuiToolTip
92-
content={content}
93-
position="top"
94-
display="inlineBlock"
95-
anchorClassName={cx({ [styles.anchorCircleIcon]: inCircle })}
96-
>
97-
{icon ? (
98-
<EuiButtonIcon
99-
iconType={icon}
100-
className={cx(styles.icon, { [styles.circle]: inCircle })}
101-
onClick={() => handleCopy(content)}
102-
data-testid={`${content}_module`}
103-
aria-labelledby={`${content}_module`}
104-
/>
105-
) : (
106-
<EuiButton
107-
className={cx(styles.icon, { [styles.circle]: inCircle })}
108-
onClick={() => handleCopy(content)}
109-
data-testid={`${content}_module`}
110-
aria-labelledby={`${content}_module`}
111-
>
112-
{abbreviation}
113-
</EuiButton>
114-
)}
115-
</EuiToolTip>
116-
</>
117-
)
118-
119-
const modulesRender = modules?.map(({ name: propName, semanticVersion = '', version = '' }) => {
90+
const newModules = modules?.map(({ name: propName, semanticVersion = '', version = '' }) => {
12091
const moduleName = modulesDefaultInit[propName]?.text || propName
12192

12293
const { abbreviation = '', name = moduleName } = getModule(moduleName)
12394

12495
const moduleAlias = truncateText(name, 50)
12596
// eslint-disable-next-line sonarjs/no-nested-template-literals
126-
const content = `${moduleAlias}${semanticVersion || version ? ` v. ${semanticVersion || version}` : ''}`
12797
let icon = modulesDefaultInit[propName]?.[theme === Theme.Dark ? 'iconDark' : 'iconLight']
98+
const content = `${moduleAlias}${semanticVersion || version ? ` v. ${semanticVersion || version}` : ''}`
12899

129100
if (!icon && !abbreviation) {
130101
icon = theme === Theme.Dark ? UnknownDark : UnknownLight
131102
}
132103

133-
return (
134-
<Tooltip
135-
key={moduleName}
136-
icon={icon}
137-
abbreviation={abbreviation}
138-
content={content}
139-
/>
140-
)
104+
mainContent.push({ icon, content, abbreviation })
105+
106+
return {
107+
moduleName,
108+
icon,
109+
abbreviation,
110+
content
111+
}
141112
})
142113

143-
return <>{modulesRender}</>
114+
// set count of hidden modules
115+
if (maxViewModules && newModules.length > maxViewModules) {
116+
newModules.length = maxViewModules
117+
newModules.push({
118+
icon: null,
119+
content: '',
120+
moduleName: '',
121+
abbreviation: `+${modules.length - maxViewModules}`
122+
})
123+
}
124+
125+
const Content = mainContent.map(({ icon, content, abbreviation = '' }) => (
126+
<div className={styles.tooltipItem}>
127+
{!!icon && (<EuiIcon type={icon} style={{ marginRight: 10 }} />)}
128+
{!icon && (
129+
<EuiTextColor
130+
className={cx(styles.icon, styles.abbr)}
131+
style={{ marginRight: 10 }}
132+
>
133+
{abbreviation}
134+
</EuiTextColor>
135+
)}
136+
{!!content && (<EuiTextColor className={cx(styles.tooltipItemText)}>{content}</EuiTextColor>)}
137+
<br />
138+
</div>
139+
))
140+
141+
return (
142+
<div className={cx(styles.container, {
143+
[styles.highlight]: highlight,
144+
[styles.containerCircle]: inCircle,
145+
})}
146+
>
147+
<EuiToolTip
148+
position="bottom"
149+
title={tooltipTitle ?? undefined}
150+
display="inlineBlock"
151+
content={Content}
152+
>
153+
<>
154+
{newModules.map(({ icon, content, abbreviation, moduleName }) => (
155+
icon ? (
156+
<EuiButtonIcon
157+
iconType={icon}
158+
className={cx(styles.icon, { [styles.circle]: inCircle })}
159+
onClick={() => handleCopy(content)}
160+
data-testid={`${content}_module`}
161+
aria-labelledby={`${content}_module`}
162+
key={moduleName}
163+
/>
164+
) : (
165+
<EuiTextColor
166+
className={cx(styles.icon, styles.abbr, { [styles.circle]: inCircle })}
167+
onClick={() => handleCopy(content)}
168+
data-testid={`${content}_module`}
169+
aria-labelledby={`${content}_module`}
170+
key={moduleName}
171+
>
172+
{abbreviation}
173+
</EuiTextColor>
174+
)))}
175+
</>
176+
</EuiToolTip>
177+
</div>
178+
)
144179
})
145180

146181
export default DatabaseListModules
Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,54 @@
1-
.circle {
2-
background-color: var(--moduleBackgroundColor);
3-
border: none !important;
4-
width: 28px !important;
5-
height: 28px !important;
6-
border-radius: 14px !important;
7-
8-
&:hover,
9-
&:focus,
10-
&:focus-within {
11-
background-color: var(--moduleBackgroundColor) !important;
1+
.container {
2+
height: 24px;
3+
line-height: 20px;
4+
5+
&.highlight {
6+
background-color: var(--hoverInListColorLight);
7+
border-radius: 12px;
8+
padding-left: 6px;
9+
padding-right: 6px;
1210
}
13-
}
1411

15-
button.icon {
16-
width: 28px !important;
17-
min-width: 28px !important;
18-
max-width: 28px !important;
19-
height: 28px !important;
20-
* {
21-
padding: 0 !important;
12+
&.containerCircle {
13+
height: 28px;
2214
}
23-
:global(.euiButton__text) {
24-
font-size: 13px !important;
15+
16+
.circle {
17+
background-color: var(--moduleBackgroundColor);
18+
border: none !important;
19+
width: 28px !important;
20+
max-width: 28px !important;
21+
height: 27px !important;
22+
border-radius: 50% !important;
23+
margin-right: 14px;
24+
padding: 4px;
25+
26+
&:hover,
27+
&:focus,
28+
&:focus-within {
29+
background-color: var(--moduleBackgroundColor) !important;
30+
}
2531
}
2632
}
2733

28-
.anchorCircleIcon {
29-
margin-right: 14px;
34+
.icon {
35+
margin-right: 4px;
36+
}
37+
38+
.icon img {
39+
width: 18px !important;
40+
max-width: 18px !important;
41+
height: 16px !important;
42+
}
43+
44+
.tooltipItem:not(:last-of-type) {
45+
padding-bottom: 6px;
46+
}
47+
48+
.tooltipItemText {
49+
vertical-align: text-top;
50+
}
51+
52+
.abbr {
53+
vertical-align: text-top;
3054
}

redisinsight/ui/src/pages/home/components/DatabasesListComponent/DatabasesListWrapper.tsx

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
EuiButton,
33
EuiButtonIcon,
4+
EuiIcon,
45
EuiPopover,
56
EuiTableFieldDataColumnType,
67
EuiText,
@@ -9,7 +10,7 @@ import {
910
} from '@elastic/eui'
1011
import { formatDistanceToNow } from 'date-fns'
1112
import { capitalize } from 'lodash'
12-
import React, { useEffect, useState } from 'react'
13+
import React, { useContext, useEffect, useState } from 'react'
1314
import { useDispatch, useSelector } from 'react-redux'
1415
import { useHistory, useLocation } from 'react-router-dom'
1516
import cx from 'classnames'
@@ -28,9 +29,13 @@ import {
2829
import { resetKeys } from 'uiSrc/slices/keys'
2930
import { PageNames, Pages } from 'uiSrc/constants'
3031
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
32+
import { ThemeContext } from 'uiSrc/contexts/themeContext'
3133
import { formatLongName, getDbIndex, Nullable, replaceSpaces } from 'uiSrc/utils'
3234
import { appContextSelector, setAppContextInitialState } from 'uiSrc/slices/app/context'
3335
import { resetCliHelperSettings, resetCliSettingsAction } from 'uiSrc/slices/cli/cli-settings'
36+
import DatabaseListModules, { ModulesListType } from 'uiSrc/components/database-list-modules/DatabaseListModules'
37+
import RediStackSVG from 'uiSrc/assets/img/modules/RediStack.svg'
38+
import { RedisModuleDto } from 'apiSrc/modules/instances/dto/database-instance.dto'
3439
import DatabasesList from './DatabasesList/DatabasesList'
3540

3641
import styles from './styles.module.scss'
@@ -52,6 +57,7 @@ const DatabasesListWrapper = ({
5257
const dispatch = useDispatch()
5358
const history = useHistory()
5459
const { search } = useLocation()
60+
const { theme } = useContext(ThemeContext)
5561

5662
const { contextInstanceId, lastPage } = useSelector(appContextSelector)
5763
const instances = useSelector(instancesSelector)
@@ -208,7 +214,7 @@ const DatabasesListWrapper = ({
208214
truncateText: true,
209215
'data-test-subj': 'database-alias-column',
210216
sortable: ({ name }) => name?.toLowerCase(),
211-
width: '50%',
217+
width: '30%',
212218
render: function InstanceCell(name: string = '', { id, db }: Instance) {
213219
const cellContent = replaceSpaces(name.substring(0, 200))
214220
return (
@@ -238,23 +244,11 @@ const DatabasesListWrapper = ({
238244
)
239245
},
240246
},
241-
{
242-
field: 'connectionType',
243-
className: 'column_type',
244-
name: 'Connection Type',
245-
dataType: 'string',
246-
sortable: true,
247-
width: '180px',
248-
truncateText: true,
249-
hideForMobile: true,
250-
render: (cellData: ConnectionType) =>
251-
CONNECTION_TYPE_DISPLAY[cellData] || capitalize(cellData),
252-
},
253247
{
254248
field: 'host',
255249
className: 'column_host',
256250
name: 'Host:Port',
257-
width: '18%',
251+
width: '35%',
258252
dataType: 'string',
259253
truncateText: true,
260254
sortable: ({ host, port }) => `${host}:${port}`,
@@ -279,6 +273,40 @@ const DatabasesListWrapper = ({
279273
)
280274
},
281275
},
276+
{
277+
field: 'connectionType',
278+
className: 'column_type',
279+
name: 'Connection Type',
280+
dataType: 'string',
281+
sortable: true,
282+
width: '180px',
283+
truncateText: true,
284+
hideForMobile: true,
285+
render: (cellData: ConnectionType) =>
286+
CONNECTION_TYPE_DISPLAY[cellData] || capitalize(cellData),
287+
},
288+
{
289+
field: 'modules',
290+
className: 'column_modules',
291+
name: 'Modules',
292+
width: '150px',
293+
dataType: 'string',
294+
render: (cellData, { modules = [], port, isRediStack }: Instance) => {
295+
return (
296+
<DatabaseListModules
297+
highlight={isRediStack}
298+
modules={modules}
299+
maxViewModules={3}
300+
tooltipTitle={isRediStack ? (
301+
<>
302+
<EuiIcon type={RediStackSVG} className={styles.redistackIcon} />
303+
<span style={{ verticalAlign: 'middle' }}>Redis Stack</span>
304+
</>
305+
) : ''}
306+
/>
307+
)
308+
},
309+
},
282310
{
283311
field: 'lastConnection',
284312
className: 'column_lastConnection',
@@ -316,7 +344,7 @@ const DatabasesListWrapper = ({
316344
]
317345

318346
const columnsHideForTablet = ['connectionType']
319-
const columnsHideForEditing = ['connectionType']
347+
const columnsHideForEditing = ['connectionType', 'modules']
320348
const columnsTablet = columnsFull.filter(
321349
({ field = '' }) => columnsHideForTablet.indexOf(field) === -1
322350
)

0 commit comments

Comments
 (0)