Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions redisinsight/ui/src/constants/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export enum FeatureFlags {
disabledByEnv = 'disabledByEnv',
rdi = 'redisDataIntegration',
hashFieldExpiration = 'hashFieldExpiration',
enhancedCloudUI = 'enhancedCloudUI',
}
41 changes: 41 additions & 0 deletions redisinsight/ui/src/pages/home/HomePage.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import { render, screen } from 'uiSrc/utils/test-utils'
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
import HomePage from './HomePage'

jest.mock('uiSrc/slices/panels/sidePanels', () => ({
Expand All @@ -9,6 +10,24 @@ jest.mock('uiSrc/slices/panels/sidePanels', () => ({
}),
}))

jest.mock('uiSrc/slices/content/create-redis-buttons', () => ({
...jest.requireActual('uiSrc/slices/content/create-redis-buttons'),
contentSelector: jest.fn().mockReturnValue({
data: {
cloud_list_of_databases: {}
}
}),
}))

jest.mock('uiSrc/slices/app/features', () => ({
...jest.requireActual('uiSrc/slices/app/features'),
appFeatureFlagsFeaturesSelector: jest.fn().mockReturnValue({
enhancedCloudUI: {
flag: false
}
}),
}))

/**
* HomePage tests
*
Expand All @@ -30,4 +49,26 @@ describe('HomePage', () => {

expect(screen.getByTestId('side-panels-insights')).toBeInTheDocument()
})

it('should not render free cloud db with feature flag disabled', async () => {
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
enhancedCloudUI: {
flag: false
}
})
await render(<HomePage />)

expect(screen.queryByTestId('db-row_create-free-cloud-db')).not.toBeInTheDocument()
})

it('should render free cloud db with feature flag enabled', async () => {
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
enhancedCloudUI: {
flag: true
}
})
await render(<HomePage />)

expect(screen.getByTestId('db-row_create-free-cloud-db')).toBeInTheDocument()
})
})
6 changes: 4 additions & 2 deletions redisinsight/ui/src/pages/home/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useDispatch, useSelector } from 'react-redux'
import { clusterSelector, resetDataRedisCluster, resetInstancesRedisCluster, } from 'uiSrc/slices/instances/cluster'
import { Nullable, setTitle } from 'uiSrc/utils'
import { HomePageTemplate } from 'uiSrc/templates'
import { BrowserStorageItem } from 'uiSrc/constants'
import { BrowserStorageItem, FeatureFlags } from 'uiSrc/constants'
import { resetKeys } from 'uiSrc/slices/browser/keys'
import { resetCliHelperSettings, resetCliSettingsAction } from 'uiSrc/slices/cli/cli-settings'
import { resetRedisearchKeysData } from 'uiSrc/slices/browser/redisearch'
Expand Down Expand Up @@ -41,6 +41,7 @@ import DatabasePanelDialog from './components/database-panel-dialog'

import './styles.scss'
import styles from './styles.module.scss'
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'

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

const {
loading,
Expand All @@ -73,7 +75,7 @@ const HomePage = () => {

const { contextInstanceId } = useSelector(appContextSelector)

const predefinedInstances = createDbContent?.cloud_list_of_databases ? [
const predefinedInstances = enhancedCloudUIFeature?.flag && createDbContent?.cloud_list_of_databases ? [
{ id: CREATE_CLOUD_DB_ID, ...createDbContent.cloud_list_of_databases } as Instance
] : []
const isInstanceExists = instances.length > 0 || predefinedInstances.length > 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ describe('DatabasesListWrapper', () => {

expect(store.getActions()).toEqual([
setSSOFlow(OAuthSocialAction.Create),
setSocialDialogState(OAuthSocialSource.ListOfDatabases)
setSocialDialogState(OAuthSocialSource.DatabaseConnectionList)
])
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {
Criteria,
EuiButtonIcon,
EuiIcon,
EuiLink, EuiResizeObserver,
EuiLink,
EuiResizeObserver,
EuiTableFieldDataColumnType,
EuiText,
EuiTextColor,
Expand Down Expand Up @@ -53,7 +54,7 @@ import { setSSOFlow } from 'uiSrc/slices/instances/cloud'
import { setSocialDialogState } from 'uiSrc/slices/oauth/cloud'
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
import { getUtmExternalLink } from 'uiSrc/utils/links'
import { CREATE_CLOUD_DB_ID } from 'uiSrc/pages/home/constants'
import { CREATE_CLOUD_DB_ID, HELP_LINKS } from 'uiSrc/pages/home/constants'
import styles from './styles.module.scss'

export interface Props {
Expand Down Expand Up @@ -243,10 +244,19 @@ const DatabasesListWrapper = (props: Props) => {
const handleClickFreeDb = () => {
if (cloudSsoFeature?.flag) {
dispatch(setSSOFlow(OAuthSocialAction.Create))
dispatch(setSocialDialogState(OAuthSocialSource.ListOfDatabases))
dispatch(setSocialDialogState(OAuthSocialSource.DatabaseConnectionList))
sendEventTelemetry({
event: TelemetryEvent.CLOUD_FREE_DATABASE_CLICKED,
eventData: { source: OAuthSocialSource.DatabaseConnectionList },
})
return
}

sendEventTelemetry({
event: HELP_LINKS.cloud.event,
eventData: { source: HELP_LINKS.cloud.sources.databaseConnectionList },
})

const link = document.createElement('a')
link.setAttribute('href', getUtmExternalLink(EXTERNAL_LINKS.tryFree, { campaign: 'list_of_databases' }))
link.setAttribute('target', '_blank')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
import React from 'react'
import { instance, mock } from 'ts-mockito'
import { render } from 'uiSrc/utils/test-utils'
import { render, screen } from 'uiSrc/utils/test-utils'
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
import DatabaseListHeader, { Props } from './DatabaseListHeader'

const mockedProps = mock<Props>()

jest.mock('uiSrc/slices/app/features', () => ({
...jest.requireActual('uiSrc/slices/app/features'),
appFeatureFlagsFeaturesSelector: jest.fn().mockReturnValue({
enhancedCloudUI: {
flag: false
}
}),
}))

jest.mock('uiSrc/slices/content/create-redis-buttons', () => ({
...jest.requireActual('uiSrc/slices/content/create-redis-buttons'),
contentSelector: jest.fn().mockReturnValue({
data: {
cloud: {
title: 'Try Redis Cloud: your ultimate Redis starting point',
description: 'Includes native support for JSON, Search and Query, and more',
links: {
main: {
altText: 'Try Redis Cloud.',
url: 'https://redis.io/try-free/?utm_source=redisinsight&utm_medium=main&utm_campaign=main'
}
},
}
}
}),
}))

jest.mock('uiSrc/telemetry', () => ({
...jest.requireActual('uiSrc/telemetry'),
sendEventTelemetry: jest.fn(),
Expand All @@ -14,4 +42,28 @@ describe('DatabaseListHeader', () => {
it('should render', () => {
expect(render(<DatabaseListHeader {...instance(mockedProps)} />)).toBeTruthy()
})

it('should not show promo cloud button with disabled feature flag', () => {
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
enhancedCloudUI: {
flag: true
}
})

render(<DatabaseListHeader {...instance(mockedProps)} />)

expect(screen.queryByTestId('promo-btn')).not.toBeInTheDocument()
})

it('should show promo cloud button with enabled feature flag', () => {
(appFeatureFlagsFeaturesSelector as jest.Mock).mockReturnValueOnce({
enhancedCloudUI: {
flag: false
}
})

render(<DatabaseListHeader {...instance(mockedProps)} />)

expect(screen.getByTestId('promo-btn')).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import React from 'react'
import React, { useContext, useEffect, useState } from 'react'
import {
EuiButton,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
} from '@elastic/eui'
import { useSelector } from 'react-redux'
import { isEmpty } from 'lodash'
import cx from 'classnames'
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
import { instancesSelector } from 'uiSrc/slices/instances/instances'
import { OAuthSocialSource } from 'uiSrc/slices/interfaces'
import { OAuthSocialAction, OAuthSocialSource } from 'uiSrc/slices/interfaces'
import PromoLink from 'uiSrc/components/promo-link/PromoLink'

import { OAuthSsoHandlerDialog } from 'uiSrc/components'
import { getPathToResource } from 'uiSrc/services/resourcesService'
import { ContentCreateRedis } from 'uiSrc/slices/interfaces/content'
import { HELP_LINKS } from 'uiSrc/pages/home/constants'
import { contentSelector } from 'uiSrc/slices/content/create-redis-buttons'
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
import { getContentByFeature } from 'uiSrc/utils/content'
import { ThemeContext } from 'uiSrc/contexts/themeContext'
import { FeatureFlags } from 'uiSrc/constants'
import SearchDatabasesList from '../search-databases-list'

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

const DatabaseListHeader = ({ onAddInstance }: Props) => {
const { data: instances } = useSelector(instancesSelector)
const featureFlags = useSelector(appFeatureFlagsFeaturesSelector)
const { loading, data } = useSelector(contentSelector)

const [promoData, setPromoData] = useState<ContentCreateRedis>()

const { theme } = useContext(ThemeContext)
const { [FeatureFlags.enhancedCloudUI]: enhancedCloudUIFeature } = featureFlags
const isShowPromoBtn = !enhancedCloudUIFeature?.flag

useEffect(() => {
if (loading || !data || isEmpty(data)) {
return
}

if (data?.cloud && !isEmpty(data.cloud)) {
setPromoData(getContentByFeature(data.cloud, featureFlags))
}
}, [loading, data, featureFlags])

const handleOnAddDatabase = () => {
sendEventTelemetry({
Expand All @@ -30,25 +61,89 @@ const DatabaseListHeader = ({ onAddInstance }: Props) => {
onAddInstance()
}

const handleClickLink = (event: TelemetryEvent, eventData: any = {}) => {
if (event) {
sendEventTelemetry({
event,
eventData: {
...eventData
}
})
}
}

const handleCreateDatabaseClick = (
event: TelemetryEvent,
eventData: any = {},
) => {
handleClickLink(event, eventData)
}

const AddInstanceBtn = () => (
<EuiButton
fill
size="s"
size={isShowPromoBtn ? 'm' : 's'}
color="secondary"
onClick={handleOnAddDatabase}
className={styles.addInstanceBtn}
data-testid="add-redis-database-short"
>
<span>+ DB</span>
{!isShowPromoBtn ? (<span>+ DB</span>) : (<span>+ Add Redis database</span>)}

</EuiButton>
)

const CreateBtn = ({ content }: { content: ContentCreateRedis }) => {
if (!isShowPromoBtn) return null

const { title, description, styles: stylesCss, links } = content
// @ts-ignore
const linkStyles = stylesCss ? stylesCss[theme] : {}
return (
<OAuthSsoHandlerDialog>
{(ssoCloudHandlerClick, isSSOEnabled) => (
<PromoLink
title={title}
description={description}
url={links?.main?.url}
testId="promo-btn"
icon="arrowRight"
styles={{
...linkStyles,
backgroundImage: linkStyles?.backgroundImage
? `url(${getPathToResource(linkStyles.backgroundImage)})`
: undefined
}}
onClick={(e) => {
!isSSOEnabled && handleCreateDatabaseClick(
HELP_LINKS.cloud.event,
{ source: HELP_LINKS.cloud.sources.databaseList },
)
ssoCloudHandlerClick(e, { source: OAuthSocialSource.ListOfDatabases, action: OAuthSocialAction.Create })
}}
/>
)}
</OAuthSsoHandlerDialog>
)
}

return (
<div className={styles.containerDl}>
<EuiFlexGroup className={styles.contentDL} alignItems="center" responsive={false} gutterSize="s">
<EuiFlexItem grow={false}>
<AddInstanceBtn />
</EuiFlexItem>
{!loading && !isEmpty(data) && (
<EuiFlexItem grow={false} className={cx(styles.promo)}>
<EuiFlexGroup alignItems="center" gutterSize="s">
{promoData && (
<EuiFlexItem grow={false}>
<CreateBtn content={promoData} />
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
)}
{instances.length > 0 && (
<EuiFlexItem grow={false} className={styles.searchContainer}>
<SearchDatabasesList />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,28 @@
margin-left: auto !important;
padding-left: 12px;
}

.promo {
display: flex !important;
@media only screen and (max-width: 800px) {
display: none !important;
}
}

.cloudSsoPromoTooltip {
display: flex;
flex-direction: row;
line-height: normal;
font-size: 12px !important;
}
.cloudSsoPromoTooltipIcon {
width: 20px !important;
height: 20px !important;
margin-right: 8px;
}

@include global.insights-open(1350px) {
.promo {
display: none !important;
}
}
Loading
Loading