Skip to content

Commit e93d1f0

Browse files
Merge branch 'feature/RI-4943_rework-tutorials' into fe/feature/RI-4997-telemetry
# Conflicts: # redisinsight/ui/src/telemetry/events.ts
2 parents 9bf1f89 + 3a415ee commit e93d1f0

File tree

78 files changed

+977
-314
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+977
-314
lines changed

redisinsight/api/src/__mocks__/custom-tutorial.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export const mockCustomTutorialManifestJson = {
7474
type: CustomTutorialManifestType.InternalLink,
7575
id: 'introduction',
7676
label: 'introduction',
77+
summary: 'Introduction summary',
7778
args: {
7879
path: '/ct-folder-1/ct-sub-folder-1/introduction.md',
7980
},

redisinsight/api/src/modules/custom-tutorial/models/custom-tutorial.manifest.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ export class CustomTutorialManifest {
6262
@IsNotEmpty()
6363
label: string;
6464

65+
@ApiProperty({ type: String })
66+
@IsOptional()
67+
@Expose()
68+
@IsString()
69+
@IsNotEmpty()
70+
summary?: string;
71+
6572
@ApiPropertyOptional({ type: CustomTutorialManifestArgs })
6673
@IsOptional()
6774
@Expose()
Lines changed: 37 additions & 0 deletions
Loading

redisinsight/ui/src/components/config/Config.spec.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { getWBTutorials } from 'uiSrc/slices/workbench/wb-tutorials'
2020
import { getContentRecommendations } from 'uiSrc/slices/recommendations/recommendations'
2121
import { getGuideLinks } from 'uiSrc/slices/content/guide-links'
2222
import { getWBCustomTutorials } from 'uiSrc/slices/workbench/wb-custom-tutorials'
23+
import { setCapability } from 'uiSrc/slices/app/context'
2324
import Config from './Config'
2425

2526
let store: typeof mockedStore
@@ -60,6 +61,7 @@ describe('Config', () => {
6061
it('should render', () => {
6162
render(<Config />)
6263
const afterRenderActions = [
64+
setCapability(),
6365
getServerInfo(),
6466
processCliClient(),
6567
getRedisCommands(),
@@ -97,6 +99,7 @@ describe('Config', () => {
9799
userSettingsSelector.mockImplementation(userSettingsSelectorMock)
98100
render(<Config />)
99101
const afterRenderActions = [
102+
setCapability(),
100103
getServerInfo(),
101104
processCliClient(),
102105
getRedisCommands(),

redisinsight/ui/src/components/config/Config.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { fetchCustomTutorials } from 'uiSrc/slices/workbench/wb-custom-tutorials
2929
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
3030
import { fetchContentRecommendations } from 'uiSrc/slices/recommendations/recommendations'
3131
import { fetchGuideLinksAction } from 'uiSrc/slices/content/guide-links'
32+
import { setCapability } from 'uiSrc/slices/app/context'
3233

3334
import favicon from 'uiSrc/assets/favicon.ico'
3435

@@ -42,6 +43,8 @@ const Config = () => {
4243
useEffect(() => {
4344
setFavicon(favicon)
4445

46+
dispatch(setCapability(localStorageService?.get(BrowserStorageItem.capability)))
47+
4548
dispatch(fetchServerInfo())
4649
dispatch(fetchUnsupportedCliCommandsAction())
4750
dispatch(fetchRedisCommandsInfo())

redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.test.tsx

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import {
77
} from 'uiSrc/slices/recommendations/recommendations'
88
import { fireEvent, screen, cleanup, mockedStore, render } from 'uiSrc/utils/test-utils'
99
import { MOCK_RECOMMENDATIONS } from 'uiSrc/constants/mocks/mock-recommendations'
10-
import { insightsPanelSelector } from 'uiSrc/slices/panels/insights'
11-
10+
import { changeSelectedTab, insightsPanelSelector, resetExplorePanelSearch, setExplorePanelIsPageOpen, toggleInsightsPanel } from 'uiSrc/slices/panels/insights'
1211
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
1312
import { Pages } from 'uiSrc/constants'
13+
import { connectedInstanceCDSelector } from 'uiSrc/slices/instances/instances'
14+
import { InsightsPanelTabs } from 'uiSrc/slices/interfaces/insights'
15+
import { getTutorialCapability } from 'uiSrc/utils'
16+
import { isShowCapabilityTutorialPopover } from 'uiSrc/services'
1417
import DatabaseSidePanels from './DatabaseSidePanels'
1518

1619
let store: typeof mockedStore
@@ -36,6 +39,16 @@ jest.mock('uiSrc/slices/instances/instances', () => ({
3639
connectionType: 'CLUSTER',
3740
provider: 'RE_CLOUD'
3841
}),
42+
connectedInstanceCDSelector: jest.fn().mockReturnValue({
43+
free: false,
44+
}),
45+
}))
46+
47+
jest.mock('uiSrc/slices/app/context', () => ({
48+
...jest.requireActual('uiSrc/slices/app/context'),
49+
appContextCapability: jest.fn().mockReturnValue({
50+
source: 'workbench RediSearch',
51+
}),
3952
}))
4053

4154
jest.mock('uiSrc/slices/recommendations/recommendations', () => ({
@@ -61,6 +74,16 @@ jest.mock('uiSrc/telemetry', () => ({
6174
sendEventTelemetry: jest.fn(),
6275
}))
6376

77+
jest.mock('uiSrc/utils', () => ({
78+
...jest.requireActual('uiSrc/utils'),
79+
getTutorialCapability: jest.fn().mockReturnValue({ tutorialPage: { id: 'id' }, telemetryName: 'searchAndQuery' }),
80+
}))
81+
82+
jest.mock('uiSrc/services', () => ({
83+
...jest.requireActual('uiSrc/services'),
84+
isShowCapabilityTutorialPopover: jest.fn(),
85+
}))
86+
6487
/**
6588
* DatabaseSidePanels tests
6689
*
@@ -89,7 +112,7 @@ describe('DatabaseSidePanels', () => {
89112

90113
(insightsPanelSelector as jest.Mock).mockReturnValue({
91114
isOpen: true,
92-
tabSelected: 'recommendations'
115+
tabSelected: 'tips'
93116
})
94117

95118
render(<DatabaseSidePanels />)
@@ -115,7 +138,7 @@ describe('DatabaseSidePanels', () => {
115138
it('should render recommendations count with totalUnread > 0', () => {
116139
(insightsPanelSelector as jest.Mock).mockReturnValue({
117140
isOpen: true,
118-
tabSelected: 'recommendations'
141+
tabSelected: 'tips'
119142
});
120143

121144
(recommendationsSelector as jest.Mock).mockImplementationOnce(() => ({
@@ -138,7 +161,7 @@ describe('DatabaseSidePanels', () => {
138161

139162
(insightsPanelSelector as jest.Mock).mockReturnValue({
140163
isOpen: true,
141-
tabSelected: 'recommendations'
164+
tabSelected: 'tips'
142165
})
143166

144167
render(<DatabaseSidePanels />)
@@ -151,11 +174,11 @@ describe('DatabaseSidePanels', () => {
151174
databaseId: 'instanceId',
152175
provider: 'RE_CLOUD',
153176
page: '/triggered-functions/libraries',
154-
tab: 'recommendations'
177+
tab: 'tips'
155178
},
156-
})
179+
});
157180

158-
sendEventTelemetry.mockRestore()
181+
(sendEventTelemetry as jest.Mock).mockRestore()
159182
})
160183

161184
it('should call proper telemetry events on change tab', () => {
@@ -166,7 +189,7 @@ describe('DatabaseSidePanels', () => {
166189

167190
(insightsPanelSelector as jest.Mock).mockReturnValue({
168191
isOpen: true,
169-
tabSelected: 'recommendations'
192+
tabSelected: 'tips'
170193
})
171194

172195
render(<DatabaseSidePanels />)
@@ -177,12 +200,12 @@ describe('DatabaseSidePanels', () => {
177200
event: TelemetryEvent.INSIGHTS_PANEL_TAB_CHANGED,
178201
eventData: {
179202
databaseId: 'instanceId',
180-
prevTab: 'recommendations',
203+
prevTab: 'tips',
181204
currentTab: 'explore',
182205
},
183-
})
206+
});
184207

185-
sendEventTelemetry.mockRestore()
208+
(sendEventTelemetry as jest.Mock).mockRestore()
186209
})
187210

188211
it('should call proper telemetry events on fullscreen', () => {
@@ -203,8 +226,42 @@ describe('DatabaseSidePanels', () => {
203226
databaseId: 'instanceId',
204227
state: 'open'
205228
},
206-
})
229+
});
230+
231+
(sendEventTelemetry as jest.Mock).mockRestore()
232+
})
207233

208-
sendEventTelemetry.mockRestore()
234+
describe('capability', () => {
235+
beforeEach(() => {
236+
(connectedInstanceCDSelector as jest.Mock).mockReturnValueOnce({ free: true });
237+
(isShowCapabilityTutorialPopover as jest.Mock).mockImplementation(() => true)
238+
})
239+
it('should call store actions', () => {
240+
(getTutorialCapability as jest.Mock).mockImplementation(() => ({
241+
tutorialPage: { args: { path: 'path' } }
242+
}))
243+
render(<DatabaseSidePanels />)
244+
245+
const expectedActions = [
246+
getRecommendations(),
247+
changeSelectedTab(InsightsPanelTabs.Explore),
248+
toggleInsightsPanel(true),
249+
]
250+
expect(store.getActions()).toEqual(expectedActions);
251+
252+
(getTutorialCapability as jest.Mock).mockRestore()
253+
})
254+
it('should call resetExplorePanelSearch if capability was not found', () => {
255+
render(<DatabaseSidePanels />)
256+
257+
const expectedActions = [
258+
getRecommendations(),
259+
resetExplorePanelSearch(),
260+
setExplorePanelIsPageOpen(false),
261+
changeSelectedTab(InsightsPanelTabs.Explore),
262+
toggleInsightsPanel(true),
263+
]
264+
expect(store.getActions()).toEqual(expectedActions)
265+
})
209266
})
210267
})

redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.tsx

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'
22
import cx from 'classnames'
33
import { EuiButtonIcon, EuiTab, EuiTabs, keys } from '@elastic/eui'
44
import { useDispatch, useSelector } from 'react-redux'
5+
import { useHistory, useLocation, useParams } from 'react-router-dom'
56

6-
import { useLocation, useParams } from 'react-router-dom'
7-
import { changeSelectedTab, insightsPanelSelector, toggleInsightsPanel } from 'uiSrc/slices/panels/insights'
7+
import { changeSelectedTab, insightsPanelSelector, resetExplorePanelSearch, setExplorePanelIsPageOpen, toggleInsightsPanel } from 'uiSrc/slices/panels/insights'
88
import { InsightsPanelTabs } from 'uiSrc/slices/interfaces/insights'
99
import { recommendationsSelector } from 'uiSrc/slices/recommendations/recommendations'
1010
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
11-
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
11+
import { connectedInstanceCDSelector, connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
1212
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
1313
import { FullScreen, OnboardingTour } from 'uiSrc/components'
14+
import { appContextCapability } from 'uiSrc/slices/app/context'
15+
import { getTutorialCapability } from 'uiSrc/utils'
16+
import { isShowCapabilityTutorialPopover } from 'uiSrc/services'
1417
import LiveTimeRecommendations from './panels/live-time-recommendations'
1518
import EnablementAreaWrapper from './panels/enablement-area'
1619

@@ -25,9 +28,12 @@ const DatabaseSidePanels = (props: Props) => {
2528
const { isOpen, tabSelected } = useSelector(insightsPanelSelector)
2629
const { data: { totalUnread } } = useSelector(recommendationsSelector)
2730
const { provider } = useSelector(connectedInstanceSelector)
31+
const { source: capabilitySource } = useSelector(appContextCapability)
32+
const { free = false } = useSelector(connectedInstanceCDSelector) ?? {}
2833

2934
const [isFullScreen, setIsFullScreen] = useState<boolean>(false)
3035

36+
const history = useHistory()
3137
const { pathname } = useLocation()
3238
const dispatch = useDispatch()
3339
const { instanceId } = useParams<{ instanceId: string }>()
@@ -52,6 +58,28 @@ const DatabaseSidePanels = (props: Props) => {
5258
pathnameRef.current = pathname
5359
}, [pathname, isFullScreen])
5460

61+
useEffect(() => {
62+
if (!capabilitySource || !isShowCapabilityTutorialPopover(free)) {
63+
return
64+
}
65+
66+
const tutorialCapabilityPath = getTutorialCapability(capabilitySource)?.tutorialPage?.args?.path || ''
67+
68+
// set 'guidPath' with the path to capability tutorial
69+
if (tutorialCapabilityPath) {
70+
const search = new URLSearchParams(window.location.search)
71+
search.set('guidePath', tutorialCapabilityPath)
72+
history.push({ search: search.toString() })
73+
} else {
74+
// reset explore if tutorial is not found
75+
dispatch(resetExplorePanelSearch())
76+
dispatch(setExplorePanelIsPageOpen(false))
77+
}
78+
79+
dispatch(changeSelectedTab(InsightsPanelTabs.Explore))
80+
dispatch(toggleInsightsPanel(true))
81+
}, [capabilitySource, free])
82+
5583
const handleEscFullScreen = (event: KeyboardEvent) => {
5684
if (event.key === keys.ESCAPE && isFullScreen) {
5785
handleFullScreen()
@@ -115,7 +143,7 @@ const DatabaseSidePanels = (props: Props) => {
115143
anchorWrapperClassName={styles.onboardingAnchorWrapper}
116144
fullSize
117145
>
118-
<span className={styles.tabName}>Explore Redis</span>
146+
<span className={styles.tabName}>Explore</span>
119147
</OnboardingTour>
120148
</EuiTab>
121149
<EuiTab
@@ -125,7 +153,7 @@ const DatabaseSidePanels = (props: Props) => {
125153
data-testid="recommendations-tab"
126154
>
127155
<>
128-
<span className={styles.tabName}>Redis Tips</span>
156+
<span className={styles.tabName}>Tips</span>
129157
{!!totalUnread && (
130158
<div
131159
className={styles.tabTotalUnread}

redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,12 @@ const EnablementArea = (props: Props) => {
9797
}, [search, tutorials, guides])
9898

9999
useEffect(() => {
100-
const manifestPath = new URLSearchParams(search).get('path')
101-
const guidePath = new URLSearchParams(search).get('guidePath')
102-
const contextManifestPath = new URLSearchParams(searchEAContext).get('path')
100+
const searchParams = new URLSearchParams(search)
101+
const searchContextParams = new URLSearchParams(searchEAContext)
102+
103+
const manifestPath = searchParams.get('path')
104+
const guidePath = searchParams.get('guidePath')
105+
const contextManifestPath = searchContextParams.get('path')
103106
const { manifest, prefixFolder } = getManifestByPath(manifestPath)
104107

105108
if (guidePath || (isEmpty(manifest) && !contextManifestPath)) {

0 commit comments

Comments
 (0)