Skip to content

Commit 96e18c2

Browse files
authored
Merge pull request #4087 from RedisInsight/fe/feature/RI-6320-add-feature-flag
#RI-6320 - add feature flag for cloud db in the list of dbs
2 parents bbc294c + da5e4f9 commit 96e18c2

File tree

13 files changed

+249
-14
lines changed

13 files changed

+249
-14
lines changed

redisinsight/ui/src/constants/featureFlags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export enum FeatureFlags {
77
disabledByEnv = 'disabledByEnv',
88
rdi = 'redisDataIntegration',
99
hashFieldExpiration = 'hashFieldExpiration',
10+
enhancedCloudUI = 'enhancedCloudUI',
1011
}

redisinsight/ui/src/pages/home/HomePage.spec.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react'
22
import { render, screen } from 'uiSrc/utils/test-utils'
3+
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
34
import HomePage from './HomePage'
45

56
jest.mock('uiSrc/slices/panels/sidePanels', () => ({
@@ -9,6 +10,24 @@ jest.mock('uiSrc/slices/panels/sidePanels', () => ({
910
}),
1011
}))
1112

13+
jest.mock('uiSrc/slices/content/create-redis-buttons', () => ({
14+
...jest.requireActual('uiSrc/slices/content/create-redis-buttons'),
15+
contentSelector: jest.fn().mockReturnValue({
16+
data: {
17+
cloud_list_of_databases: {}
18+
}
19+
}),
20+
}))
21+
22+
jest.mock('uiSrc/slices/app/features', () => ({
23+
...jest.requireActual('uiSrc/slices/app/features'),
24+
appFeatureFlagsFeaturesSelector: jest.fn().mockReturnValue({
25+
enhancedCloudUI: {
26+
flag: false
27+
}
28+
}),
29+
}))
30+
1231
/**
1332
* HomePage tests
1433
*
@@ -30,4 +49,26 @@ describe('HomePage', () => {
3049

3150
expect(screen.getByTestId('side-panels-insights')).toBeInTheDocument()
3251
})
52+
53+
it('should not render free cloud db with feature flag disabled', async () => {
54+
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
55+
enhancedCloudUI: {
56+
flag: false
57+
}
58+
})
59+
await render(<HomePage />)
60+
61+
expect(screen.queryByTestId('db-row_create-free-cloud-db')).not.toBeInTheDocument()
62+
})
63+
64+
it('should render free cloud db with feature flag enabled', async () => {
65+
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
66+
enhancedCloudUI: {
67+
flag: true
68+
}
69+
})
70+
await render(<HomePage />)
71+
72+
expect(screen.getByTestId('db-row_create-free-cloud-db')).toBeInTheDocument()
73+
})
3374
})

redisinsight/ui/src/pages/home/HomePage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useDispatch, useSelector } from 'react-redux'
88
import { clusterSelector, resetDataRedisCluster, resetInstancesRedisCluster, } from 'uiSrc/slices/instances/cluster'
99
import { Nullable, setTitle } from 'uiSrc/utils'
1010
import { HomePageTemplate } from 'uiSrc/templates'
11-
import { BrowserStorageItem } from 'uiSrc/constants'
11+
import { BrowserStorageItem, FeatureFlags } from 'uiSrc/constants'
1212
import { resetKeys } from 'uiSrc/slices/browser/keys'
1313
import { resetCliHelperSettings, resetCliSettingsAction } from 'uiSrc/slices/cli/cli-settings'
1414
import { resetRedisearchKeysData } from 'uiSrc/slices/browser/redisearch'
@@ -41,6 +41,7 @@ import DatabasePanelDialog from './components/database-panel-dialog'
4141

4242
import './styles.scss'
4343
import styles from './styles.module.scss'
44+
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
4445

4546
enum OpenDialogName {
4647
AddDatabase = 'add',
@@ -58,6 +59,7 @@ const HomePage = () => {
5859
const { instance: sentinelInstance } = useSelector(sentinelSelector)
5960
const { action, dbConnection } = useSelector(appRedirectionSelector)
6061
const { data: createDbContent } = useSelector(contentSelector)
62+
const { [FeatureFlags.enhancedCloudUI]: enhancedCloudUIFeature } = useSelector(appFeatureFlagsFeaturesSelector)
6163

6264
const {
6365
loading,
@@ -73,7 +75,7 @@ const HomePage = () => {
7375

7476
const { contextInstanceId } = useSelector(appContextSelector)
7577

76-
const predefinedInstances = createDbContent?.cloud_list_of_databases ? [
78+
const predefinedInstances = enhancedCloudUIFeature?.flag && createDbContent?.cloud_list_of_databases ? [
7779
{ id: CREATE_CLOUD_DB_ID, ...createDbContent.cloud_list_of_databases } as Instance
7880
] : []
7981
const isInstanceExists = instances.length > 0 || predefinedInstances.length > 0

redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ describe('DatabasesListWrapper', () => {
120120

121121
expect(store.getActions()).toEqual([
122122
setSSOFlow(OAuthSocialAction.Create),
123-
setSocialDialogState(OAuthSocialSource.ListOfDatabases)
123+
setSocialDialogState(OAuthSocialSource.DatabaseConnectionList)
124124
])
125125
})
126126

redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {
22
Criteria,
33
EuiButtonIcon,
44
EuiIcon,
5-
EuiLink, EuiResizeObserver,
5+
EuiLink,
6+
EuiResizeObserver,
67
EuiTableFieldDataColumnType,
78
EuiText,
89
EuiTextColor,
@@ -53,7 +54,7 @@ import { setSSOFlow } from 'uiSrc/slices/instances/cloud'
5354
import { setSocialDialogState } from 'uiSrc/slices/oauth/cloud'
5455
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
5556
import { getUtmExternalLink } from 'uiSrc/utils/links'
56-
import { CREATE_CLOUD_DB_ID } from 'uiSrc/pages/home/constants'
57+
import { CREATE_CLOUD_DB_ID, HELP_LINKS } from 'uiSrc/pages/home/constants'
5758
import styles from './styles.module.scss'
5859

5960
export interface Props {
@@ -243,10 +244,19 @@ const DatabasesListWrapper = (props: Props) => {
243244
const handleClickFreeDb = () => {
244245
if (cloudSsoFeature?.flag) {
245246
dispatch(setSSOFlow(OAuthSocialAction.Create))
246-
dispatch(setSocialDialogState(OAuthSocialSource.ListOfDatabases))
247+
dispatch(setSocialDialogState(OAuthSocialSource.DatabaseConnectionList))
248+
sendEventTelemetry({
249+
event: TelemetryEvent.CLOUD_FREE_DATABASE_CLICKED,
250+
eventData: { source: OAuthSocialSource.DatabaseConnectionList },
251+
})
247252
return
248253
}
249254

255+
sendEventTelemetry({
256+
event: HELP_LINKS.cloud.event,
257+
eventData: { source: HELP_LINKS.cloud.sources.databaseConnectionList },
258+
})
259+
250260
const link = document.createElement('a')
251261
link.setAttribute('href', getUtmExternalLink(EXTERNAL_LINKS.tryFree, { campaign: 'list_of_databases' }))
252262
link.setAttribute('target', '_blank')

redisinsight/ui/src/pages/home/components/database-list-header/DatabaseListHeader.spec.tsx

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
11
import React from 'react'
22
import { instance, mock } from 'ts-mockito'
3-
import { render } from 'uiSrc/utils/test-utils'
3+
import { render, screen } from 'uiSrc/utils/test-utils'
4+
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
45
import DatabaseListHeader, { Props } from './DatabaseListHeader'
56

67
const mockedProps = mock<Props>()
78

9+
jest.mock('uiSrc/slices/app/features', () => ({
10+
...jest.requireActual('uiSrc/slices/app/features'),
11+
appFeatureFlagsFeaturesSelector: jest.fn().mockReturnValue({
12+
enhancedCloudUI: {
13+
flag: false
14+
}
15+
}),
16+
}))
17+
18+
jest.mock('uiSrc/slices/content/create-redis-buttons', () => ({
19+
...jest.requireActual('uiSrc/slices/content/create-redis-buttons'),
20+
contentSelector: jest.fn().mockReturnValue({
21+
data: {
22+
cloud: {
23+
title: 'Try Redis Cloud: your ultimate Redis starting point',
24+
description: 'Includes native support for JSON, Search and Query, and more',
25+
links: {
26+
main: {
27+
altText: 'Try Redis Cloud.',
28+
url: 'https://redis.io/try-free/?utm_source=redisinsight&utm_medium=main&utm_campaign=main'
29+
}
30+
},
31+
}
32+
}
33+
}),
34+
}))
35+
836
jest.mock('uiSrc/telemetry', () => ({
937
...jest.requireActual('uiSrc/telemetry'),
1038
sendEventTelemetry: jest.fn(),
@@ -14,4 +42,28 @@ describe('DatabaseListHeader', () => {
1442
it('should render', () => {
1543
expect(render(<DatabaseListHeader {...instance(mockedProps)} />)).toBeTruthy()
1644
})
45+
46+
it('should not show promo cloud button with disabled feature flag', () => {
47+
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
48+
enhancedCloudUI: {
49+
flag: true
50+
}
51+
})
52+
53+
render(<DatabaseListHeader {...instance(mockedProps)} />)
54+
55+
expect(screen.queryByTestId('promo-btn')).not.toBeInTheDocument()
56+
})
57+
58+
it('should show promo cloud button with enabled feature flag', () => {
59+
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
60+
enhancedCloudUI: {
61+
flag: false
62+
}
63+
})
64+
65+
render(<DatabaseListHeader {...instance(mockedProps)} />)
66+
67+
expect(screen.getByTestId('promo-btn')).toBeInTheDocument()
68+
})
1769
})

redisinsight/ui/src/pages/home/components/database-list-header/DatabaseListHeader.tsx

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
1-
import React from 'react'
1+
import React, { useContext, useEffect, useState } from 'react'
22
import {
33
EuiButton,
44
EuiFlexGroup,
55
EuiFlexItem,
66
EuiSpacer,
77
} from '@elastic/eui'
88
import { useSelector } from 'react-redux'
9+
import { isEmpty } from 'lodash'
10+
import cx from 'classnames'
911
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
1012
import { instancesSelector } from 'uiSrc/slices/instances/instances'
11-
import { OAuthSocialSource } from 'uiSrc/slices/interfaces'
13+
import { OAuthSocialAction, OAuthSocialSource } from 'uiSrc/slices/interfaces'
14+
import PromoLink from 'uiSrc/components/promo-link/PromoLink'
15+
16+
import { OAuthSsoHandlerDialog } from 'uiSrc/components'
17+
import { getPathToResource } from 'uiSrc/services/resourcesService'
18+
import { ContentCreateRedis } from 'uiSrc/slices/interfaces/content'
19+
import { HELP_LINKS } from 'uiSrc/pages/home/constants'
20+
import { contentSelector } from 'uiSrc/slices/content/create-redis-buttons'
21+
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
22+
import { getContentByFeature } from 'uiSrc/utils/content'
23+
import { ThemeContext } from 'uiSrc/contexts/themeContext'
24+
import { FeatureFlags } from 'uiSrc/constants'
1225
import SearchDatabasesList from '../search-databases-list'
1326

1427
import styles from './styles.module.scss'
@@ -19,6 +32,24 @@ export interface Props {
1932

2033
const DatabaseListHeader = ({ onAddInstance }: Props) => {
2134
const { data: instances } = useSelector(instancesSelector)
35+
const featureFlags = useSelector(appFeatureFlagsFeaturesSelector)
36+
const { loading, data } = useSelector(contentSelector)
37+
38+
const [promoData, setPromoData] = useState<ContentCreateRedis>()
39+
40+
const { theme } = useContext(ThemeContext)
41+
const { [FeatureFlags.enhancedCloudUI]: enhancedCloudUIFeature } = featureFlags
42+
const isShowPromoBtn = !enhancedCloudUIFeature?.flag
43+
44+
useEffect(() => {
45+
if (loading || !data || isEmpty(data)) {
46+
return
47+
}
48+
49+
if (data?.cloud && !isEmpty(data.cloud)) {
50+
setPromoData(getContentByFeature(data.cloud, featureFlags))
51+
}
52+
}, [loading, data, featureFlags])
2253

2354
const handleOnAddDatabase = () => {
2455
sendEventTelemetry({
@@ -30,25 +61,89 @@ const DatabaseListHeader = ({ onAddInstance }: Props) => {
3061
onAddInstance()
3162
}
3263

64+
const handleClickLink = (event: TelemetryEvent, eventData: any = {}) => {
65+
if (event) {
66+
sendEventTelemetry({
67+
event,
68+
eventData: {
69+
...eventData
70+
}
71+
})
72+
}
73+
}
74+
75+
const handleCreateDatabaseClick = (
76+
event: TelemetryEvent,
77+
eventData: any = {},
78+
) => {
79+
handleClickLink(event, eventData)
80+
}
81+
3382
const AddInstanceBtn = () => (
3483
<EuiButton
3584
fill
36-
size="s"
85+
size={isShowPromoBtn ? 'm' : 's'}
3786
color="secondary"
3887
onClick={handleOnAddDatabase}
3988
className={styles.addInstanceBtn}
4089
data-testid="add-redis-database-short"
4190
>
42-
<span>+ DB</span>
91+
{!isShowPromoBtn ? (<span>+ DB</span>) : (<span>+ Add Redis database</span>)}
92+
4393
</EuiButton>
4494
)
4595

96+
const CreateBtn = ({ content }: { content: ContentCreateRedis }) => {
97+
if (!isShowPromoBtn) return null
98+
99+
const { title, description, styles: stylesCss, links } = content
100+
// @ts-ignore
101+
const linkStyles = stylesCss ? stylesCss[theme] : {}
102+
return (
103+
<OAuthSsoHandlerDialog>
104+
{(ssoCloudHandlerClick, isSSOEnabled) => (
105+
<PromoLink
106+
title={title}
107+
description={description}
108+
url={links?.main?.url}
109+
testId="promo-btn"
110+
icon="arrowRight"
111+
styles={{
112+
...linkStyles,
113+
backgroundImage: linkStyles?.backgroundImage
114+
? `url(${getPathToResource(linkStyles.backgroundImage)})`
115+
: undefined
116+
}}
117+
onClick={(e) => {
118+
!isSSOEnabled && handleCreateDatabaseClick(
119+
HELP_LINKS.cloud.event,
120+
{ source: HELP_LINKS.cloud.sources.databaseList },
121+
)
122+
ssoCloudHandlerClick(e, { source: OAuthSocialSource.ListOfDatabases, action: OAuthSocialAction.Create })
123+
}}
124+
/>
125+
)}
126+
</OAuthSsoHandlerDialog>
127+
)
128+
}
129+
46130
return (
47131
<div className={styles.containerDl}>
48132
<EuiFlexGroup className={styles.contentDL} alignItems="center" responsive={false} gutterSize="s">
49133
<EuiFlexItem grow={false}>
50134
<AddInstanceBtn />
51135
</EuiFlexItem>
136+
{!loading && !isEmpty(data) && (
137+
<EuiFlexItem grow={false} className={cx(styles.promo)}>
138+
<EuiFlexGroup alignItems="center" gutterSize="s">
139+
{promoData && (
140+
<EuiFlexItem grow={false}>
141+
<CreateBtn content={promoData} />
142+
</EuiFlexItem>
143+
)}
144+
</EuiFlexGroup>
145+
</EuiFlexItem>
146+
)}
52147
{instances.length > 0 && (
53148
<EuiFlexItem grow={false} className={styles.searchContainer}>
54149
<SearchDatabasesList />

redisinsight/ui/src/pages/home/components/database-list-header/styles.module.scss

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,28 @@
4545
margin-left: auto !important;
4646
padding-left: 12px;
4747
}
48+
49+
.promo {
50+
display: flex !important;
51+
@media only screen and (max-width: 800px) {
52+
display: none !important;
53+
}
54+
}
55+
56+
.cloudSsoPromoTooltip {
57+
display: flex;
58+
flex-direction: row;
59+
line-height: normal;
60+
font-size: 12px !important;
61+
}
62+
.cloudSsoPromoTooltipIcon {
63+
width: 20px !important;
64+
height: 20px !important;
65+
margin-right: 8px;
66+
}
67+
68+
@include global.insights-open(1350px) {
69+
.promo {
70+
display: none !important;
71+
}
72+
}

0 commit comments

Comments
 (0)