Skip to content

Commit 361f74c

Browse files
authored
Merge pull request #1830 from RedisInsight/fe/feature/RI-4259_recommendations_ordering
#RI-4259 - add custom recommendations sorting
2 parents 7a6ebac + 9c08998 commit 361f74c

File tree

3 files changed

+160
-12
lines changed

3 files changed

+160
-12
lines changed

redisinsight/ui/src/pages/databaseAnalysis/components/recommendations-view/Recommendations.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useContext } from 'react'
22
import { useSelector } from 'react-redux'
33
import { useParams } from 'react-router-dom'
4-
import { isNull, sortBy } from 'lodash'
4+
import { isNull } from 'lodash'
55
import {
66
EuiAccordion,
77
EuiPanel,
@@ -17,12 +17,14 @@ import { RecommendationVoting } from 'uiSrc/pages/databaseAnalysis/components'
1717
import { dbAnalysisSelector } from 'uiSrc/slices/analytics/dbAnalysis'
1818
import recommendationsContent from 'uiSrc/constants/dbAnalysisRecommendations.json'
1919
import { Theme } from 'uiSrc/constants'
20+
import { Vote } from 'uiSrc/constants/recommendations'
2021
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
2122
import RediStackDarkMin from 'uiSrc/assets/img/modules/redistack/RediStackDark-min.svg'
2223
import RediStackLightMin from 'uiSrc/assets/img/modules/redistack/RediStackLight-min.svg'
2324
import NoRecommendationsDark from 'uiSrc/assets/img/icons/recommendations_dark.svg'
2425
import NoRecommendationsLight from 'uiSrc/assets/img/icons/recommendations_light.svg'
25-
import { renderContent, renderBadges, renderBadgesLegend } from './utils'
26+
27+
import { renderContent, renderBadges, renderBadgesLegend, sortRecommendations } from './utils'
2628
import styles from './styles.module.scss'
2729

2830
const Recommendations = () => {
@@ -44,9 +46,6 @@ const Recommendations = () => {
4446

4547
const onRedisStackClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => event.stopPropagation()
4648

47-
const sortedRecommendations = sortBy(recommendations, ({ name }) =>
48-
(recommendationsContent[name]?.redisStack ? -1 : 0))
49-
5049
const renderButtonContent = (redisStack: boolean, title: string, badges: string[], id: string) => (
5150
<EuiFlexGroup className={styles.accordionButton} responsive={false} alignItems="center" justifyContent="spaceBetween">
5251
<EuiFlexGroup alignItems="center">
@@ -112,14 +111,14 @@ const Recommendations = () => {
112111
{renderBadgesLegend()}
113112
</div>
114113
<div className={styles.recommendationsContainer}>
115-
{sortedRecommendations.map(({ name, params, vote }) => {
114+
{sortRecommendations(recommendations).map(({ name, params, vote }) => {
116115
const {
117116
id = '',
118117
title = '',
119118
content = '',
120119
badges = [],
121120
redisStack = false
122-
} = recommendationsContent[name]
121+
} = recommendationsContent[name as keyof typeof recommendationsContent]
123122

124123
return (
125124
<div key={id} className={styles.recommendation}>
@@ -138,7 +137,7 @@ const Recommendations = () => {
138137
{renderContent(content, params)}
139138
</EuiPanel>
140139
</EuiAccordion>
141-
<RecommendationVoting vote={vote} name={name} />
140+
<RecommendationVoting vote={vote as Vote} name={name} />
142141
</div>
143142
)
144143
})}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { render, screen } from 'uiSrc/utils/test-utils'
2+
import { sortRecommendations, replaceVariables, renderBadgesLegend, renderBadges } from './utils'
3+
4+
const sortRecommendationsTests = [
5+
{
6+
input: [],
7+
expected: []
8+
},
9+
{
10+
input: [
11+
{ name: 'luaScript' },
12+
{ name: 'bigSets' },
13+
{ name: 'searchIndexes' },
14+
{ name: 'redisSearch' },
15+
],
16+
expected: [
17+
{ name: 'redisSearch' },
18+
{ name: 'searchIndexes' },
19+
{ name: 'bigSets' },
20+
{ name: 'luaScript' },
21+
]
22+
},
23+
{
24+
input: [
25+
{ name: 'luaScript' },
26+
{ name: 'bigSets' },
27+
{ name: 'redisSearch' },
28+
],
29+
expected: [
30+
{ name: 'redisSearch' },
31+
{ name: 'bigSets' },
32+
{ name: 'luaScript' },
33+
]
34+
},
35+
{
36+
input: [
37+
{ name: 'luaScript' },
38+
{ name: 'bigSets' },
39+
{ name: 'searchIndexes' },
40+
{ name: 'redisSearch' },
41+
{ name: 'useSmallerKeys' },
42+
{ name: 'RTS' },
43+
],
44+
expected: [
45+
{ name: 'redisSearch' },
46+
{ name: 'searchIndexes' },
47+
{ name: 'bigSets' },
48+
{ name: 'RTS' },
49+
{ name: 'luaScript' },
50+
{ name: 'useSmallerKeys' },
51+
]
52+
},
53+
]
54+
55+
const replaceVariablesTests = [
56+
{ input: ['value'], expected: 'value' },
57+
// eslint-disable-next-line no-template-curly-in-string
58+
{ input: ['some ${0} text ${1}', ['foo', 'bar'], { foo: '7', bar: 'bar' }], expected: 'some 7 text bar' },
59+
{ input: ['value'], expected: 'value' },
60+
{ input: ['value'], expected: 'value' },
61+
]
62+
63+
describe('renderBadgesLegend', () => {
64+
const renderedBadgesLegend = renderBadgesLegend()
65+
render(renderedBadgesLegend)
66+
67+
expect(screen.queryByTestId('badges-legend')).toBeInTheDocument()
68+
})
69+
70+
describe('sortRecommendations', () => {
71+
test.each(sortRecommendationsTests)(
72+
'%j',
73+
({ input, expected }) => {
74+
const result = sortRecommendations(input)
75+
expect(result).toEqual(expected)
76+
}
77+
)
78+
})
79+
80+
describe('renderBadges', () => {
81+
it('should render "code_changes" badge', () => {
82+
const renderedBadges = renderBadges(['code_changes'])
83+
render(renderedBadges)
84+
85+
expect(screen.queryByTestId('code_changes')).toBeInTheDocument()
86+
})
87+
88+
it('should render "configuration_changes" badge', () => {
89+
const renderedBadges = renderBadges(['configuration_changes'])
90+
render(renderedBadges)
91+
92+
expect(screen.queryByTestId('configuration_changes')).toBeInTheDocument()
93+
expect(screen.queryByTestId('upgrade')).not.toBeInTheDocument()
94+
expect(screen.queryByTestId('code_changes')).not.toBeInTheDocument()
95+
})
96+
97+
it('should render "code_changes" badge', () => {
98+
const renderedBadges = renderBadges(['code_changes'])
99+
render(renderedBadges)
100+
101+
expect(screen.queryByTestId('code_changes')).toBeInTheDocument()
102+
expect(screen.queryByTestId('configuration_changes')).not.toBeInTheDocument()
103+
expect(screen.queryByTestId('upgrade')).not.toBeInTheDocument()
104+
})
105+
106+
it('should render "upgrade" badge', () => {
107+
const renderedBadges = renderBadges(['upgrade'])
108+
render(renderedBadges)
109+
110+
expect(screen.queryByTestId('upgrade')).toBeInTheDocument()
111+
expect(screen.queryByTestId('configuration_changes')).not.toBeInTheDocument()
112+
expect(screen.queryByTestId('code_changes')).not.toBeInTheDocument()
113+
})
114+
115+
it('should render all badges', () => {
116+
const renderedBadges = renderBadges(['upgrade', 'configuration_changes', 'code_changes'])
117+
render(renderedBadges)
118+
119+
expect(screen.queryByTestId('upgrade')).toBeInTheDocument()
120+
expect(screen.queryByTestId('configuration_changes')).toBeInTheDocument()
121+
expect(screen.queryByTestId('code_changes')).toBeInTheDocument()
122+
})
123+
})
124+
125+
describe('replaceVariables', () => {
126+
test.each(replaceVariablesTests)(
127+
'%j',
128+
({ input, expected }) => {
129+
// @ts-ignore
130+
const result = replaceVariables(...input)
131+
expect(result).toEqual(expected)
132+
}
133+
)
134+
})

redisinsight/ui/src/pages/databaseAnalysis/components/recommendations-view/utils.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import { isString, isArray } from 'lodash'
2+
import { isString, isArray, sortBy } from 'lodash'
33
import {
44
EuiTextColor,
55
EuiToolTip,
@@ -10,13 +10,15 @@ import {
1010
} from '@elastic/eui'
1111
import { SpacerSize } from '@elastic/eui/src/components/spacer/spacer'
1212
import cx from 'classnames'
13+
import recommendationsContent from 'uiSrc/constants/dbAnalysisRecommendations.json'
1314
import { ReactComponent as CodeIcon } from 'uiSrc/assets/img/code-changes.svg'
1415
import { ReactComponent as ConfigurationIcon } from 'uiSrc/assets/img/configuration-changes.svg'
1516
import { ReactComponent as UpgradeIcon } from 'uiSrc/assets/img/upgrade.svg'
17+
import { Recommendation } from 'apiSrc/modules/database-analysis/models/recommendation'
1618

1719
import styles from './styles.module.scss'
1820

19-
interface IContentElement {
21+
export interface IContentElement {
2022
id: string
2123
type: string
2224
value: any[] | any
@@ -61,8 +63,8 @@ export const renderBadgesLegend = () => (
6163
</EuiFlexGroup>
6264
)
6365

64-
const replaceVariables = (value: any[] | any, parameter: string[], params: any) => (
65-
parameter && isString(value) ? value.replace(/\$\{\d}/i, (matched) => {
66+
export const replaceVariables = (value: any[] | any, parameter: string[], params: any) => (
67+
parameter && isString(value) ? value.replace(/\$\{\d}/g, (matched) => {
6668
const parameterIndex: string = matched.substring(
6769
matched.indexOf('{') + 1,
6870
matched.lastIndexOf('}')
@@ -109,3 +111,16 @@ const renderContentElement = ({ id, type, value: jsonValue, parameter }: IConten
109111

110112
export const renderContent = (elements: IContentElement[], params: any) => (
111113
elements?.map((item) => renderContentElement(item, params)))
114+
115+
export const sortRecommendations = (recommendations: Recommendation[]) => sortBy(recommendations, ({ name }) => {
116+
if (name === 'redisSearch') {
117+
return -3
118+
}
119+
if (name === 'searchIndexes') {
120+
return -2
121+
}
122+
if (recommendationsContent[name as keyof typeof recommendationsContent]?.redisStack) {
123+
return -1
124+
}
125+
return 0
126+
})

0 commit comments

Comments
 (0)