diff --git a/redisinsight/ui/src/components/config/Config.spec.tsx b/redisinsight/ui/src/components/config/Config.spec.tsx index 1d451cdcb1..a37cd88ba5 100644 --- a/redisinsight/ui/src/components/config/Config.spec.tsx +++ b/redisinsight/ui/src/components/config/Config.spec.tsx @@ -15,7 +15,6 @@ import { appServerInfoSelector, getServerInfo } from 'uiSrc/slices/app/info' import { processCliClient } from 'uiSrc/slices/cli/cli-settings' import { getRedisCommands } from 'uiSrc/slices/app/redis-commands' import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features' -import { getWBGuides } from 'uiSrc/slices/workbench/wb-guides' import { getWBTutorials } from 'uiSrc/slices/workbench/wb-tutorials' import { getContentRecommendations } from 'uiSrc/slices/recommendations/recommendations' import { getGuideLinks } from 'uiSrc/slices/content/guide-links' @@ -68,7 +67,6 @@ describe('Config', () => { getNotifications(), getContentRecommendations(), getGuideLinks(), - getWBGuides(), getWBTutorials(), getWBCustomTutorials(), getFeatureFlags(), @@ -106,7 +104,6 @@ describe('Config', () => { getNotifications(), getContentRecommendations(), getGuideLinks(), - getWBGuides(), getWBTutorials(), getWBCustomTutorials(), getFeatureFlags(), diff --git a/redisinsight/ui/src/components/config/Config.tsx b/redisinsight/ui/src/components/config/Config.tsx index 196fba3b08..e6208ddd40 100644 --- a/redisinsight/ui/src/components/config/Config.tsx +++ b/redisinsight/ui/src/components/config/Config.tsx @@ -23,7 +23,6 @@ import { import { setFavicon, isDifferentConsentsExists } from 'uiSrc/utils' import { fetchUnsupportedCliCommandsAction } from 'uiSrc/slices/cli/cli-settings' import { fetchRedisCommandsInfo } from 'uiSrc/slices/app/redis-commands' -import { fetchGuides } from 'uiSrc/slices/workbench/wb-guides' import { fetchTutorials } from 'uiSrc/slices/workbench/wb-tutorials' import { fetchCustomTutorials } from 'uiSrc/slices/workbench/wb-custom-tutorials' import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features' @@ -52,8 +51,7 @@ const Config = () => { dispatch(fetchContentRecommendations()) dispatch(fetchGuideLinksAction()) - // get guides & tutorials - dispatch(fetchGuides()) + // get tutorials dispatch(fetchTutorials()) dispatch(fetchCustomTutorials()) diff --git a/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.test.tsx b/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.test.tsx index 25a8feec28..4eb6eada2f 100644 --- a/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.test.tsx +++ b/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.test.tsx @@ -76,7 +76,7 @@ jest.mock('uiSrc/telemetry', () => ({ jest.mock('uiSrc/utils', () => ({ ...jest.requireActual('uiSrc/utils'), - getTutorialCapability: jest.fn().mockReturnValue({ tutorialPage: { id: 'id' }, telemetryName: 'searchAndQuery' }), + getTutorialCapability: jest.fn().mockReturnValue({ path: 'path', telemetryName: 'searchAndQuery' }), })) jest.mock('uiSrc/services', () => ({ @@ -243,6 +243,8 @@ describe('DatabaseSidePanels', () => { render() const expectedActions = [ + resetExplorePanelSearch(), + setExplorePanelIsPageOpen(false), changeSelectedTab(InsightsPanelTabs.Explore), toggleInsightsPanel(true), ] diff --git a/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.tsx b/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.tsx index 48cdc20a64..63fc14bfa2 100644 --- a/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.tsx +++ b/redisinsight/ui/src/components/database-side-panels/DatabaseSidePanels.tsx @@ -14,6 +14,7 @@ import { FullScreen, OnboardingTour } from 'uiSrc/components' import { appContextCapability } from 'uiSrc/slices/app/context' import { getTutorialCapability } from 'uiSrc/utils' import { isShowCapabilityTutorialPopover } from 'uiSrc/services' +import { EAManifestFirstKey } from 'uiSrc/constants' import LiveTimeRecommendations from './panels/live-time-recommendations' import EnablementAreaWrapper from './panels/enablement-area' @@ -63,12 +64,12 @@ const DatabaseSidePanels = (props: Props) => { return } - const tutorialCapabilityPath = getTutorialCapability(capabilitySource)?.tutorialPage?.args?.path || '' + const tutorialCapabilityPath = getTutorialCapability(capabilitySource)?.path || '' - // set 'guidPath' with the path to capability tutorial + // set 'path' with the path to capability tutorial if (tutorialCapabilityPath) { const search = new URLSearchParams(window.location.search) - search.set('guidePath', tutorialCapabilityPath) + search.set('path', `${EAManifestFirstKey.TUTORIALS}/${tutorialCapabilityPath}`) history.push({ search: search.toString() }) } else { // reset explore if tutorial is not found diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.spec.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.spec.tsx index 6a6aa8208e..1b7ef37e2e 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.spec.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.spec.tsx @@ -3,7 +3,7 @@ import { cloneDeep } from 'lodash' import { instance, mock } from 'ts-mockito' import reactRouterDom from 'react-router-dom' import { cleanup, mockedStore, render, screen, fireEvent, act, waitFor } from 'uiSrc/utils/test-utils' -import { MOCK_GUIDES_ITEMS, MOCK_TUTORIALS_ITEMS, MOCK_CUSTOM_TUTORIALS_ITEMS } from 'uiSrc/constants' +import { MOCK_TUTORIALS_ITEMS, MOCK_CUSTOM_TUTORIALS_ITEMS } from 'uiSrc/constants' import { EnablementAreaComponent, IEnablementAreaItem } from 'uiSrc/slices/interfaces' import { @@ -36,11 +36,11 @@ jest.mock('uiSrc/slices/workbench/wb-custom-tutorials', () => ({ ) })) -jest.mock('uiSrc/slices/workbench/wb-guides', () => { - const defaultState = jest.requireActual('uiSrc/slices/workbench/wb-guides').initialState +jest.mock('uiSrc/slices/workbench/wb-tutorials', () => { + const defaultState = jest.requireActual('uiSrc/slices/workbench/wb-tutorials').initialState return { - ...jest.requireActual('uiSrc/slices/workbench/wb-guides'), - workbenchGuidesSelector: jest.fn().mockReturnValue({ + ...jest.requireActual('uiSrc/slices/workbench/wb-tutorials'), + workbenchTutorialsSelector: jest.fn().mockReturnValue({ ...defaultState, }), } @@ -59,7 +59,6 @@ describe('EnablementArea', () => { it('should render', () => { expect(render()) .toBeTruthy() @@ -94,7 +93,7 @@ describe('EnablementArea', () => { const { queryByTestId } = render( ) @@ -114,7 +113,7 @@ describe('EnablementArea', () => { const { queryByTestId } = render( ) @@ -122,18 +121,22 @@ describe('EnablementArea', () => { }) it('should find guide and push proper search path', async () => { - const search = '?guidePath=/quick-guides/working-with-json.html' + const search = '?guidePath=quick-guides/working-with-json.html' const pushMock = jest.fn() reactRouterDom.useHistory = jest.fn().mockReturnValueOnce({ push: pushMock }) reactRouterDom.useLocation = jest.fn().mockImplementationOnce(() => ({ search })) await act(() => { - render() + render() }) await waitFor(() => { - expect(pushMock).toBeCalledWith({ search: '?path=quick-guides/0/1' }) + expect(pushMock).toBeCalledWith({ search: '?path=tutorials/0/1' }) }, { timeout: 1000 }) }) diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.tsx index 59f580d73c..7d869a24d9 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/EnablementArea.tsx @@ -7,7 +7,7 @@ import { isEmpty } from 'lodash' import { IEnablementAreaItem } from 'uiSrc/slices/interfaces' import { EnablementAreaProvider, IInternalPage } from 'uiSrc/pages/workbench/contexts/enablementAreaContext' import { ApiEndpoints, EAManifestFirstKey, CodeButtonParams } from 'uiSrc/constants' -import { findMarkdownPathByPath, Nullable } from 'uiSrc/utils' +import { findMarkdownPath, Nullable } from 'uiSrc/utils' import { explorePanelSelector, resetExplorePanelSearch, @@ -27,7 +27,6 @@ import { import styles from './styles.module.scss' export interface Props { - guides: IEnablementAreaItem[] tutorials: IEnablementAreaItem[] customTutorials: IEnablementAreaItem[] loading: boolean @@ -42,7 +41,6 @@ export interface Props { const EnablementArea = (props: Props) => { const { - guides = [], tutorials = [], customTutorials = [], openScript, @@ -75,26 +73,23 @@ const EnablementArea = (props: Props) => { }, [search]) useEffect(() => { - // handle guidePath search param - const guidePathParam = new URLSearchParams(search).get('guidePath') - if (guidePathParam) { - const guidesPath = findMarkdownPathByPath(guides, guidePathParam) + // handle guidePath or tutorialId search params + const tutorialPathParam = new URLSearchParams(search).get('guidePath') ?? '' + const tutorialIdParam = new URLSearchParams(search).get('tutorialId') ?? '' + if (tutorialPathParam || tutorialIdParam) { let manifestPath = '' + const options = tutorialIdParam ? { id: tutorialIdParam } : { mdPath: tutorialPathParam } + const tutorialPath = findMarkdownPath(tutorials, options) - if (guidesPath) { - manifestPath = `${EAManifestFirstKey.GUIDES}/${guidesPath}` - } - - const tutorialsPath = findMarkdownPathByPath(tutorials, guidePathParam) - if (tutorialsPath) { - manifestPath = `${EAManifestFirstKey.TUTORIALS}/${tutorialsPath}` + if (tutorialPath) { + manifestPath = `${EAManifestFirstKey.TUTORIALS}/${tutorialPath}` } if (manifestPath) { handleOpenInternalPage({ path: '', manifestPath }, false) } } - }, [search, tutorials, guides]) + }, [search, tutorials]) useEffect(() => { const searchParams = new URLSearchParams(search) @@ -102,10 +97,11 @@ const EnablementArea = (props: Props) => { const manifestPath = searchParams.get('path') const guidePath = searchParams.get('guidePath') + const tutorialId = searchParams.get('tutorialId') const contextManifestPath = searchContextParams.get('path') const { manifest, prefixFolder } = getManifestByPath(manifestPath) - if (guidePath || (isEmpty(manifest) && !contextManifestPath)) { + if (guidePath || tutorialId || (isEmpty(manifest) && !contextManifestPath)) { return } @@ -124,7 +120,7 @@ const EnablementArea = (props: Props) => { } dispatch(setExplorePanelIsPageOpen(false)) - }, [search, customTutorials, guides, tutorials]) + }, [search, customTutorials, tutorials]) const getManifestByPath = (path: Nullable = '') => { const manifestPath = path?.replace(/^\//, '') || '' @@ -134,9 +130,6 @@ const EnablementArea = (props: Props) => { if (manifestPath.startsWith(EAManifestFirstKey.TUTORIALS)) { return ({ manifest: tutorials, prefixFolder: ApiEndpoints.TUTORIALS_PATH }) } - if (manifestPath.startsWith(EAManifestFirstKey.GUIDES)) { - return ({ manifest: guides, prefixFolder: ApiEndpoints.GUIDES_PATH }) - } return { manifest: null } } @@ -175,7 +168,6 @@ const EnablementArea = (props: Props) => { : ( diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.spec.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.spec.tsx index c1aa508cc1..a04955cbec 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.spec.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.spec.tsx @@ -30,7 +30,7 @@ jest.mock('uiSrc/services', () => ({ jest.mock('uiSrc/utils', () => ({ ...jest.requireActual('uiSrc/utils'), - getTutorialCapability: jest.fn().mockReturnValue({ tutorialPage: { id: 'id' }, telemetryName: 'searchAndQuery' }), + getTutorialCapability: jest.fn().mockReturnValue({ path: 'path', telemetryName: 'searchAndQuery' }), })) jest.mock('uiSrc/slices/instances/instances', () => ({ diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.tsx index 2d07fb66d0..4f2260357b 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/InternalPage/InternalPage.tsx @@ -103,7 +103,7 @@ const InternalPage = (props: Props) => { } useEffect(() => { - if (isShowCapabilityTutorialPopover(free) && !!tutorialCapability?.tutorialPage?.id) { + if (isShowCapabilityTutorialPopover(free) && !!tutorialCapability?.path) { setShowCapabilityPopover(true) setCapabilityPopoverShown() sendEventTelemetry({ diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/LazyInternalPage/LazyInternalPage.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/LazyInternalPage/LazyInternalPage.tsx index bcf68361e8..5f54dca37d 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/LazyInternalPage/LazyInternalPage.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/LazyInternalPage/LazyInternalPage.tsx @@ -8,7 +8,6 @@ import { getApiErrorMessage, isStatusSuccessful, Nullable } from 'uiSrc/utils' import { resourcesService } from 'uiSrc/services' import { IS_ABSOLUTE_PATH } from 'uiSrc/constants/regex' import { IEnablementAreaItem } from 'uiSrc/slices/interfaces' -import { workbenchGuidesSelector } from 'uiSrc/slices/workbench/wb-guides' import { workbenchTutorialsSelector } from 'uiSrc/slices/workbench/wb-tutorials' import { workbenchCustomTutorialsSelector } from 'uiSrc/slices/workbench/wb-custom-tutorials' @@ -53,7 +52,6 @@ const LazyInternalPage = ({ }: Props) => { const history = useHistory() const { itemScrollTop, data: contentContext, url } = useSelector(explorePanelSelector) - const { loading: guidesLoading } = useSelector(workbenchGuidesSelector) const { loading: tutorialsLoading } = useSelector(workbenchTutorialsSelector) const { loading: customTutorialsLoading } = useSelector(workbenchCustomTutorialsSelector) const [isLoading, setLoading] = useState(false) @@ -75,7 +73,7 @@ const LazyInternalPage = ({ startLoadContent() }, [path, sourcePath]) - const isMarkdownLoading = isLoading || guidesLoading || tutorialsLoading || customTutorialsLoading + const isMarkdownLoading = isLoading || tutorialsLoading || customTutorialsLoading const getRelatedPages = () => (manifest ? getPagesInsideGroup(manifest, manifestPath) : []) const loadContent = async () => { setLoading(true) diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.spec.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.spec.tsx index 8dcb744589..83c1678220 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.spec.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.spec.tsx @@ -1,11 +1,10 @@ import React from 'react' import { render, screen } from 'uiSrc/utils/test-utils' -import { MOCK_CUSTOM_TUTORIALS_ITEMS, MOCK_GUIDES_ITEMS, MOCK_TUTORIALS_ITEMS } from 'uiSrc/constants' +import { MOCK_CUSTOM_TUTORIALS_ITEMS, MOCK_TUTORIALS_ITEMS } from 'uiSrc/constants' import Navigation from './Navigation' const guides = { - guides: MOCK_GUIDES_ITEMS, tutorials: MOCK_TUTORIALS_ITEMS, customTutorials: MOCK_CUSTOM_TUTORIALS_ITEMS } @@ -18,7 +17,6 @@ describe('Navigation', () => { it('should render navigation groups', () => { render() - expect(screen.queryByTestId('accordion-quick-guides')).toBeInTheDocument() expect(screen.queryByTestId('accordion-tutorials')).toBeInTheDocument() expect(screen.queryByTestId('accordion-custom-tutorials')).toBeInTheDocument() }) diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.tsx index df7294db6c..0a50149a41 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.tsx @@ -25,7 +25,6 @@ import styles from './styles.module.scss' const padding = parseInt(styles.paddingHorizontal) export interface Props { - guides: IEnablementAreaItem[] tutorials: IEnablementAreaItem[] customTutorials: IEnablementAreaItem[] isInternalPageVisible: boolean @@ -40,7 +39,7 @@ const PATHS = { } const Navigation = (props: Props) => { - const { guides, tutorials, customTutorials, isInternalPageVisible } = props + const { tutorials, customTutorials, isInternalPageVisible } = props const [isCreateOpen, setIsCreateOpen] = useState(false) @@ -171,7 +170,6 @@ const Navigation = (props: Props) => { flush className={cx(styles.innerContainer)} > - {guides && renderTreeView(getManifestItems(guides), PATHS.guides)} {tutorials && renderTreeView(getManifestItems(tutorials), PATHS.tutorials)} {customTutorials && renderTreeView(getManifestItems(customTutorials), PATHS.customTutorials)} diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Pagination/Pagination.spec.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Pagination/Pagination.spec.tsx index d9529606d1..0b5a74ba57 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Pagination/Pagination.spec.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/components/Pagination/Pagination.spec.tsx @@ -1,13 +1,13 @@ import { act } from '@testing-library/react' import React from 'react' import { fireEvent, render, screen } from 'uiSrc/utils/test-utils' -import { ApiEndpoints, MOCK_GUIDES_ITEMS } from 'uiSrc/constants' +import { ApiEndpoints, MOCK_TUTORIALS_ITEMS } from 'uiSrc/constants' import { defaultValue, EnablementAreaProvider } from 'uiSrc/pages/workbench/contexts/enablementAreaContext' import { EnablementAreaComponent } from 'uiSrc/slices/interfaces' import Pagination from './Pagination' -const paginationItems = MOCK_GUIDES_ITEMS[0]?.children +const paginationItems = MOCK_TUTORIALS_ITEMS[0]?.children ?.map((item, index) => ({ ...item, _key: `${index}` })) ?.filter((item) => item.type === EnablementAreaComponent.InternalLink) || [] diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/utils/tests/getFileInfo.spec.ts b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/utils/tests/getFileInfo.spec.ts index 54bcaf63e5..ba54a57496 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/utils/tests/getFileInfo.spec.ts +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementArea/utils/tests/getFileInfo.spec.ts @@ -1,4 +1,4 @@ -import { ApiEndpoints, MOCK_GUIDES_ITEMS } from 'uiSrc/constants' +import { ApiEndpoints, MOCK_TUTORIALS_ITEMS } from 'uiSrc/constants' import { getFileInfo, getGroupPath, @@ -48,14 +48,14 @@ const getFileInfoTests = [ expected: { name: '', parent: '', extension: '', location: '', label: '', _key: null, parents: [] } }, { - input: [{ manifestPath: 'quick-guides/0/0', path: '/static/workbench/quick-guides/document/learn-more.md' }, MOCK_GUIDES_ITEMS], + input: [{ manifestPath: 'quick-guides/0/0', path: '/static/workbench/quick-guides/document/learn-more.md' }, MOCK_TUTORIALS_ITEMS], expected: { name: 'learn-more', - parent: MOCK_GUIDES_ITEMS[0].label, + parent: MOCK_TUTORIALS_ITEMS[0].label, extension: 'md', location: '/static/workbench/quick-guides/document', - label: MOCK_GUIDES_ITEMS?.[0]?.children?.[0].label, + label: MOCK_TUTORIALS_ITEMS?.[0]?.children?.[0].label, _key: '0', - parents: [MOCK_GUIDES_ITEMS[0]] } + parents: [MOCK_TUTORIALS_ITEMS[0]] } } ] @@ -72,15 +72,15 @@ describe('getFileInfo', () => { const getPagesInsideGroupTests = [ { - input: [MOCK_GUIDES_ITEMS, 'quick-guides/0/0'], - expected: (MOCK_GUIDES_ITEMS[0].children || []).map((item, index) => ({ + input: [MOCK_TUTORIALS_ITEMS, 'quick-guides/0/0'], + expected: (MOCK_TUTORIALS_ITEMS[0].children || []).map((item, index) => ({ ...item, _groupPath: 'quick-guides/0', _key: `${index}` })) }, { - input: [MOCK_GUIDES_ITEMS, 'https://domen.com/workbench/enablement-area/'], + input: [MOCK_TUTORIALS_ITEMS, 'https://domen.com/workbench/enablement-area/'], expected: [] }, { @@ -137,23 +137,23 @@ describe('getWBSourcePath', () => { const getMarkdownPathByManifestTests = [ { - input: [MOCK_GUIDES_ITEMS, '/quick-guides/0/0', 'static/my-folder'], - expected: `static/my-folder${MOCK_GUIDES_ITEMS[0]?.children?.[0]?.args?.path}` + input: [MOCK_TUTORIALS_ITEMS, '/quick-guides/0/0', 'static/my-folder'], + expected: `static/my-folder${MOCK_TUTORIALS_ITEMS[0]?.children?.[0]?.args?.path}` }, { - input: [MOCK_GUIDES_ITEMS, '/quick-guides/0/0'], - expected: MOCK_GUIDES_ITEMS[0]?.children?.[0]?.args?.path + input: [MOCK_TUTORIALS_ITEMS, '/quick-guides/0/0'], + expected: MOCK_TUTORIALS_ITEMS[0]?.children?.[0]?.args?.path }, { - input: [MOCK_GUIDES_ITEMS, '/my-guides/0/0', 'path/'], + input: [MOCK_TUTORIALS_ITEMS, '/my-guides/0/0', 'path/'], expected: '' }, { - input: [MOCK_GUIDES_ITEMS, '/quick-guides/0/1'], - expected: `/123123-123123${MOCK_GUIDES_ITEMS[0]?.children?.[1]?.args?.path}` + input: [MOCK_TUTORIALS_ITEMS, '/quick-guides/0/1'], + expected: `/${MOCK_TUTORIALS_ITEMS[0]?.children?.[1]?.args?.path}` }, { - input: [MOCK_GUIDES_ITEMS, '/quick-guides/0/3'], + input: [MOCK_TUTORIALS_ITEMS, '/quick-guides/0/2'], expected: '/quick-guides/working-with-hash.html' }, ] @@ -202,9 +202,9 @@ describe('getGroupPath', () => { }) const getParentByManifestTests = [ - { input: [MOCK_GUIDES_ITEMS, '0/0'], expected: MOCK_GUIDES_ITEMS[0] }, - { input: [MOCK_GUIDES_ITEMS, '100/0'], expected: null }, - { input: [MOCK_GUIDES_ITEMS, null], expected: null }, + { input: [MOCK_TUTORIALS_ITEMS, '0/0'], expected: MOCK_TUTORIALS_ITEMS[0] }, + { input: [MOCK_TUTORIALS_ITEMS, '100/0'], expected: null }, + { input: [MOCK_TUTORIALS_ITEMS, null], expected: null }, ] describe('getParentByManifest', () => { diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.spec.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.spec.tsx index c7128f2f66..5c0074988d 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.spec.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.spec.tsx @@ -15,16 +15,6 @@ beforeEach(() => { store.clearActions() }) -jest.mock('uiSrc/slices/workbench/wb-guides', () => { - const defaultState = jest.requireActual('uiSrc/slices/workbench/wb-guides').initialState - return { - ...jest.requireActual('uiSrc/slices/workbench/wb-guides'), - workbenchGuidesSelector: jest.fn().mockReturnValue({ - ...defaultState, - }), - } -}) - jest.mock('uiSrc/slices/workbench/wb-tutorials', () => { const defaultState = jest.requireActual('uiSrc/slices/workbench/wb-tutorials').initialState return { diff --git a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.tsx b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.tsx index 2693c53ae3..8019ead099 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/enablement-area/EnablementAreaWrapper.tsx @@ -2,7 +2,6 @@ import React from 'react' import { useDispatch, useSelector } from 'react-redux' import { useParams } from 'react-router-dom' import { IInternalPage } from 'uiSrc/pages/workbench/contexts/enablementAreaContext' -import { workbenchGuidesSelector } from 'uiSrc/slices/workbench/wb-guides' import { workbenchTutorialsSelector } from 'uiSrc/slices/workbench/wb-tutorials' import { workbenchCustomTutorialsSelector } from 'uiSrc/slices/workbench/wb-custom-tutorials' import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' @@ -16,7 +15,6 @@ export interface Props { } const EnablementAreaWrapper = () => { - const { loading: loadingGuides, items: guides } = useSelector(workbenchGuidesSelector) const { loading: loadingTutorials, items: tutorials } = useSelector(workbenchTutorialsSelector) const { loading: loadingCustomTutorials, items: customTutorials } = useSelector(workbenchCustomTutorialsSelector) @@ -45,10 +43,9 @@ const EnablementAreaWrapper = () => { return ( { isRead={read} vote={vote} hide={hide} - tutorial={recommendationsContent[name]?.tutorial} + tutorialId={recommendationsContent[name]?.tutorialId} provider={provider} params={params} recommendationsContent={recommendationsContent} diff --git a/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.spec.tsx b/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.spec.tsx index 7571b2bf61..17096d8f09 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.spec.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.spec.tsx @@ -8,6 +8,7 @@ import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' import { updateRecommendation } from 'uiSrc/slices/recommendations/recommendations' import { INSTANCE_ID_MOCK } from 'uiSrc/mocks/handlers/instances/instancesHandlers' import { MOCK_RECOMMENDATIONS } from 'uiSrc/constants/mocks/mock-recommendations' +import { findTutorialPath } from 'uiSrc/utils' import Recommendation, { IProps } from './Recommendation' const recommendationsContent = MOCK_RECOMMENDATIONS @@ -23,6 +24,11 @@ jest.mock('uiSrc/telemetry', () => ({ sendEventTelemetry: jest.fn(), })) +jest.mock('uiSrc/utils', () => ({ + ...jest.requireActual('uiSrc/utils'), + findTutorialPath: jest.fn(), +})) + let store: typeof mockedStore beforeEach(() => { cleanup() @@ -43,7 +49,7 @@ describe('Recommendation', () => { render() @@ -60,14 +66,15 @@ describe('Recommendation', () => { it('should properly push history on workbench page', () => { // will be improved const pushMock = jest.fn() - reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }) + reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }); + (findTutorialPath as jest.Mock).mockImplementation(() => 'path') const { container } = render( ) @@ -75,7 +82,7 @@ describe('Recommendation', () => { fireEvent.click(container.querySelector('[data-test-subj="searchJSON-button"]') as HTMLButtonElement) fireEvent.click(screen.getByTestId('searchJSON-to-tutorial-btn')) - expect(pushMock).toHaveBeenCalledWith({ search: 'guidePath=' }) + expect(pushMock).toHaveBeenCalledWith({ search: 'path=tutorials/path' }) expect(sendEventTelemetry).toBeCalledWith({ event: TelemetryEvent.INSIGHTS_TIPS_TUTORIAL_CLICKED, eventData: { @@ -90,14 +97,15 @@ describe('Recommendation', () => { it('should properly call openNewWindowDatabase and open a new window on workbench page to specific guide', () => { // will be improved const pushMock = jest.fn() - reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }) + reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }); + (findTutorialPath as jest.Mock).mockImplementation(() => 'path') const { container } = render( ) @@ -107,7 +115,7 @@ describe('Recommendation', () => { expect(pushMock) .toHaveBeenCalledWith({ - search: 'guidePath=quick-guides/working-with-hash.html' + search: 'path=tutorials/path' }) expect(sendEventTelemetry).toBeCalledWith({ event: TelemetryEvent.INSIGHTS_TIPS_TUTORIAL_CLICKED, @@ -124,14 +132,15 @@ describe('Recommendation', () => { it('should properly push history on workbench page to specific tutorial', () => { // will be improved const pushMock = jest.fn() - reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }) + reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }); + (findTutorialPath as jest.Mock).mockImplementation(() => 'path') const { container } = render( ) @@ -141,7 +150,7 @@ describe('Recommendation', () => { expect(pushMock) .toHaveBeenCalledWith({ - search: 'guidePath=/redis_stack/working_with_json.md' + search: 'path=tutorials/path' }) expect(sendEventTelemetry).toBeCalledWith({ event: TelemetryEvent.INSIGHTS_TIPS_TUTORIAL_CLICKED, @@ -185,21 +194,21 @@ describe('Recommendation', () => { it('should not render "Tutorial" btn if tutorial is Undefined', () => { const name = 'searchJSON' - const { queryByTestId } = render() + const { queryByTestId } = render() expect(queryByTestId(`${name}-to-tutorial-btn`)).not.toBeInTheDocument() }) - it('should render "Tutorial" if tutorial="path"', () => { + it('should render "Tutorial" if tutorialId="path"', () => { const name = 'searchJSON' - const { queryByTestId } = render() + const { queryByTestId } = render() expect(queryByTestId(`${name}-to-tutorial-btn`)).toHaveTextContent('Tutorial') }) - it('should render "Workbench" btn if tutorial=""', () => { + it('should render "Workbench" btn if tutorialId=""', () => { const name = 'searchJSON' - const { queryByTestId } = render() + const { queryByTestId } = render() expect(queryByTestId(`${name}-to-tutorial-btn`)).toHaveTextContent('Workbench') }) diff --git a/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.tsx b/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.tsx index efc966750b..ac57448375 100644 --- a/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.tsx +++ b/redisinsight/ui/src/components/database-side-panels/panels/live-time-recommendations/components/recommendation/Recommendation.tsx @@ -16,7 +16,7 @@ import { import { isUndefined } from 'lodash' import cx from 'classnames' -import { Nullable, Maybe } from 'uiSrc/utils' +import { Nullable, Maybe, findTutorialPath } from 'uiSrc/utils' import { renderRecommendationContent } from 'uiSrc/utils/recommendation/utils' import { Theme } from 'uiSrc/constants' import { RecommendationVoting, RecommendationCopyComponent } from 'uiSrc/components' @@ -41,7 +41,7 @@ export interface IProps { isRead: boolean vote: Nullable hide: boolean - tutorial?: string + tutorialId?: string provider?: string params: IRecommendationParams recommendationsContent: IRecommendationsStatic @@ -52,7 +52,7 @@ const Recommendation = ({ name, isRead, vote, - tutorial, + tutorialId, hide, provider, params, @@ -78,7 +78,8 @@ const Recommendation = ({ } }) - dispatch(openTutorialByPath(tutorial || '', history)) + const tutorialPath = findTutorialPath({ id: tutorialId ?? '' }) + dispatch(openTutorialByPath(tutorialPath ?? '', history)) } const toggleHide = (event: React.MouseEvent) => { @@ -136,7 +137,7 @@ const Recommendation = ({ const recommendationContent = () => ( - {!isUndefined(tutorial) && ( + {!isUndefined(tutorialId) && ( - { tutorial ? 'Start Tutorial' : 'Workbench' } + { tutorialId ? 'Start Tutorial' : 'Workbench' } )} {renderRecommendationContent( diff --git a/redisinsight/ui/src/components/explore-guides/ExploreGuides.spec.tsx b/redisinsight/ui/src/components/explore-guides/ExploreGuides.spec.tsx index c7d128011b..521b54afef 100644 --- a/redisinsight/ui/src/components/explore-guides/ExploreGuides.spec.tsx +++ b/redisinsight/ui/src/components/explore-guides/ExploreGuides.spec.tsx @@ -3,10 +3,10 @@ import reactRouterDom from 'react-router-dom' import { render, screen, fireEvent } from 'uiSrc/utils/test-utils' import { MOCK_EXPLORE_GUIDES } from 'uiSrc/constants/mocks/mock-explore-guides' -import { Pages } from 'uiSrc/constants' import { INSTANCE_ID_MOCK } from 'uiSrc/mocks/handlers/instances/instancesHandlers' import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' import { KeyViewType } from 'uiSrc/slices/interfaces/keys' +import { findTutorialPath } from 'uiSrc/utils' import ExploreGuides from './ExploreGuides' @@ -29,6 +29,11 @@ jest.mock('uiSrc/telemetry', () => ({ sendEventTelemetry: jest.fn(), })) +jest.mock('uiSrc/utils', () => ({ + ...jest.requireActual('uiSrc/utils'), + findTutorialPath: jest.fn(), +})) + describe('ExploreGuides', () => { it('should render', () => { expect(render()).toBeTruthy() @@ -45,7 +50,8 @@ describe('ExploreGuides', () => { it('should call proper history push after click on guide', () => { const pushMock = jest.fn() - reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }) + reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }); + (findTutorialPath as jest.Mock).mockImplementation(() => 'path') render() @@ -53,20 +59,21 @@ describe('ExploreGuides', () => { expect(pushMock) .toHaveBeenCalledWith({ - search: 'guidePath=/quick-guides/document/introduction.md' + search: 'path=tutorials/path' }) }) it('should call proper history push after click on guide with tutorial', () => { const pushMock = jest.fn() - reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }) + reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }); + (findTutorialPath as jest.Mock).mockImplementation(() => 'path') render() fireEvent.click(screen.getByTestId('guide-button-JSON')) expect(pushMock).toHaveBeenCalledWith({ - search: 'guidePath=/quick-guides/document/working-with-json.md' + search: 'path=tutorials/path' }) }) diff --git a/redisinsight/ui/src/components/explore-guides/ExploreGuides.tsx b/redisinsight/ui/src/components/explore-guides/ExploreGuides.tsx index 69e87aa6d3..be36c890e0 100644 --- a/redisinsight/ui/src/components/explore-guides/ExploreGuides.tsx +++ b/redisinsight/ui/src/components/explore-guides/ExploreGuides.tsx @@ -10,6 +10,7 @@ import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances' import { keysSelector } from 'uiSrc/slices/browser/keys' import { openTutorialByPath } from 'uiSrc/slices/panels/insights' +import { findTutorialPath } from 'uiSrc/utils' import styles from './styles.module.scss' const ExploreGuides = () => { @@ -22,7 +23,7 @@ const ExploreGuides = () => { const history = useHistory() const dispatch = useDispatch() - const handleLinkClick = (tutorial: string, title: string) => { + const handleLinkClick = (tutorialId: string, title: string) => { sendEventTelemetry({ event: TelemetryEvent.BROWSER_TUTORIAL_CLICKED, eventData: { @@ -33,7 +34,8 @@ const ExploreGuides = () => { } }) - dispatch(openTutorialByPath(tutorial, history)) + const tutorialPath = findTutorialPath({ id: tutorialId ?? '' }) + dispatch(openTutorialByPath(tutorialPath ?? '', history)) } return ( @@ -45,13 +47,13 @@ const ExploreGuides = () => { {!!data.length && (
- {data.map(({ title, tutorial, icon }) => ( + {data.map(({ title, tutorialId, icon }) => (
{}} - onClick={() => handleLinkClick(tutorial, title)} + onClick={() => handleLinkClick(tutorialId, title)} className={styles.btn} data-testid={`guide-button-${title}`} > diff --git a/redisinsight/ui/src/constants/index.ts b/redisinsight/ui/src/constants/index.ts index 6ae5c0a6c8..0e40368cc8 100644 --- a/redisinsight/ui/src/constants/index.ts +++ b/redisinsight/ui/src/constants/index.ts @@ -16,7 +16,6 @@ export * from './pages' export * from './workbenchResults' export * from './socketEvents' export * from './mocks/mock-redis-commands' -export * from './mocks/mock-guides' export * from './mocks/mock-tutorials' export * from './mocks/mock-custom-tutorials' export * from './socketErrors' diff --git a/redisinsight/ui/src/constants/mocks/mock-guides.ts b/redisinsight/ui/src/constants/mocks/mock-guides.ts deleted file mode 100644 index 8e38901fae..0000000000 --- a/redisinsight/ui/src/constants/mocks/mock-guides.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { EnablementAreaComponent, IEnablementAreaItem } from 'uiSrc/slices/interfaces' - -export const MOCK_GUIDES_ITEMS: IEnablementAreaItem[] = [ - { - type: EnablementAreaComponent.Group, - id: 'quick-guides', - label: 'Quick Guides', - children: [ - { - type: EnablementAreaComponent.InternalLink, - id: 'document-capabilities', - label: 'Document Capabilities', - args: { - path: '/static/workbench/quick-guides/document/learn-more.md', - }, - }, - { - type: EnablementAreaComponent.InternalLink, - id: 'working-with-json', - label: 'Working with JSON', - args: { - path: '/quick-guides/working-with-json.html', - }, - _path: '/123123-123123' - }, - { - type: EnablementAreaComponent.InternalLink, - id: 'working-with-hash', - label: 'Working with HASH', - args: { - path: 'quick-guides/working-with-hash.html', - }, - }, - { - type: EnablementAreaComponent.InternalLink, - id: 'working-with-hash-2', - label: 'Working with HASH', - args: { - path: '\\quick-guides\\working-with-hash.html', - }, - } - ] - }, - { - type: EnablementAreaComponent.InternalLink, - id: 'internal-page', - label: 'Internal Page', - args: { - path: 'quick-guides/document-capabilities.html' - }, - }, - { - type: EnablementAreaComponent.InternalLink, - id: 'second-internal-page', - label: 'Second Internal Page', - args: { - path: 'quick-guides/document-capabilities.html' - }, - }, - { - type: EnablementAreaComponent.CodeButton, - id: 'manual', - label: 'Manual', - args: { - path: '_scripts/manual.txt' - }, - }, -] - -export const MOCK_GUIDES = { - type: EnablementAreaComponent.Group, - id: 'quick-guides', - label: 'Quick Guides', - children: MOCK_GUIDES_ITEMS -} diff --git a/redisinsight/ui/src/constants/mocks/mock-recommendations.ts b/redisinsight/ui/src/constants/mocks/mock-recommendations.ts index 3ab6677f76..244c7a46bb 100644 --- a/redisinsight/ui/src/constants/mocks/mock-recommendations.ts +++ b/redisinsight/ui/src/constants/mocks/mock-recommendations.ts @@ -77,7 +77,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { telemetryEvent: 'shardHashes', title: 'Shard big hashes to small hashes', redisStack: true, - tutorial: '/quick-guides/document/introduction.md', + tutorialId: 'ds-hashes', content: [ { type: 'paragraph', @@ -135,6 +135,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { combineSmallStringsToHashes: { id: 'combineSmallStringsToHashes', title: 'Combine small strings to hashes', + tutorialId: 'ds-hashes', content: [ { type: 'span', @@ -382,7 +383,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { bigStrings: { id: 'bigStrings', title: 'Avoid large strings', - tutorial: '/quick-guides/document/introduction.md', + tutorialId: 'ds-json-intro', content: [ { type: 'span', @@ -517,7 +518,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { id: 'bigSets', telemetryEvent: 'optimizeExistenceChecks', title: 'Consider using probabilistic data structures such as Bloom Filter or HyperLogLog', - tutorial: '/quick-guides/probabilistic-data-structures/introduction.md', + tutorialId: 'ds-prob-intro', redisStack: true, content: [ { @@ -663,7 +664,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { telemetryEvent: 'optimizeTimeSeries', title: 'Try using the Redis native time series data structure and querying capabilities', redisStack: true, - tutorial: '/quick-guides/time-series/introduction.md', + tutorialId: 'ds-ts-intro', content: [ { type: 'span', @@ -868,7 +869,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { title: 'Optimize your query and search experience', deprecated: true, redisStack: true, - tutorial: '/redis_stack/working_with_json.md', + tutorialId: 'ds-json-intro', content: [ { type: 'link', @@ -918,7 +919,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { id: 'searchIndexes', title: 'Try using the indexing, querying, and full-text search, natively developed in Redis', redisStack: true, - tutorial: '/quick-guides/document/introduction.md', + tutorialId: 'sq-intro', content: [ { type: 'paragraph', @@ -1041,7 +1042,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { id: 'searchJSON', title: 'Try indexing your JSON documents for efficient data retrieval', redisStack: true, - tutorial: '/quick-guides/document/introduction.md', + tutorialId: 'sq-intro', content: [ { type: 'span', @@ -1132,7 +1133,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { id: 'stringToJson', title: 'Try using our JSON native document store', redisStack: true, - tutorial: '/quick-guides/document/introduction.md', + tutorialId: 'ds-json-intro', content: [ { type: 'paragraph', @@ -1249,7 +1250,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { searchVisualization: { id: 'searchVisualization', title: 'Try Workbench, the advanced command-line interface', - tutorial: '', + tutorialId: '', content: [ { type: 'paragraph', @@ -1312,7 +1313,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { searchHash: { id: 'searchHash', title: 'Try indexing your hash documents to query and retrieve data', - tutorial: '/quick-guides/document/introduction.md', + tutorialId: 'sq-intro', redisStack: true, content: [ { @@ -1442,7 +1443,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { luaToFunctions: { id: 'luaToFunctions', title: 'Consider using triggers and functions', - tutorial: '/quick-guides/triggers-and-functions/introduction.md', + tutorialId: 'tf-intro', content: [ { type: 'paragraph', @@ -1503,7 +1504,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { functionsWithStreams: { id: 'functionsWithStreams', title: 'Consider using triggers and functions to react in real-time to stream entries', - tutorial: '/quick-guides/triggers-and-functions/introduction.md', + tutorialId: 'tf-intro', content: [ { type: 'paragraph', @@ -1580,7 +1581,7 @@ export const MOCK_RECOMMENDATIONS: IRecommendationsStatic = { functionsWithKeyspace: { id: 'functionsWithKeyspace', title: 'Consider using triggers and functions to react in real-time to database changes', - tutorial: '/quick-guides/triggers-and-functions/introduction.md', + tutorialId: 'tf-intro', content: [ { type: 'paragraph', diff --git a/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.spec.tsx b/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.spec.tsx index d41a968f45..f8692d20e7 100644 --- a/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.spec.tsx +++ b/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.spec.tsx @@ -7,6 +7,7 @@ import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' import { recommendationsSelector } from 'uiSrc/slices/recommendations/recommendations' import { MOCK_RECOMMENDATIONS } from 'uiSrc/constants/mocks/mock-recommendations' +import { findTutorialPath } from 'uiSrc/utils' import Recommendations from './Recommendations' const recommendationsContent = MOCK_RECOMMENDATIONS @@ -57,6 +58,11 @@ jest.mock('uiSrc/slices/instances/instances', () => ({ }), })) +jest.mock('uiSrc/utils', () => ({ + ...jest.requireActual('uiSrc/utils'), + findTutorialPath: jest.fn(), +})) + beforeEach(() => { (recommendationsSelector as jest.Mock).mockImplementation(() => ({ ...mockRecommendationsSelector, @@ -440,7 +446,7 @@ describe('Recommendations', () => { expect(screen.getByTestId('bigHashes-to-tutorial-btn')).toBeInTheDocument() }) - it('should call proper history push after click go tutorial button', () => { + it('should call proper telemetry after click go tutorial button', () => { const sendEventTelemetryMock = jest.fn(); (sendEventTelemetry as jest.Mock).mockImplementation(() => sendEventTelemetryMock); @@ -467,9 +473,11 @@ describe('Recommendations', () => { (sendEventTelemetry as jest.Mock).mockRestore() }) - it('should call proper telemetry after click go tutorial button', () => { + it('should call proper history push after click go tutorial button', () => { const pushMock = jest.fn() + const path = 'path' reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }); + (findTutorialPath as jest.Mock).mockImplementation(() => path); (dbAnalysisSelector as jest.Mock).mockImplementation(() => ({ ...mockdbAnalysisSelector, data: { @@ -483,7 +491,7 @@ describe('Recommendations', () => { fireEvent.click(screen.getByTestId('bigHashes-to-tutorial-btn')) expect(pushMock).toBeCalledWith({ - search: 'guidePath=/quick-guides/document/introduction.md' + search: 'path=tutorials/path' }) pushMock.mockRestore() }) diff --git a/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.tsx b/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.tsx index 05195be6fe..c7a3ca9e12 100644 --- a/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.tsx +++ b/redisinsight/ui/src/pages/database-analysis/components/recommendations-view/Recommendations.tsx @@ -36,6 +36,7 @@ import { } from 'uiSrc/utils/recommendation/utils' import { openTutorialByPath } from 'uiSrc/slices/panels/insights' +import { findTutorialPath } from 'uiSrc/utils' import styles from './styles.module.scss' const Recommendations = () => { @@ -60,7 +61,7 @@ const Recommendations = () => { } }) - const goToTutorial = (mdPath: string, id: string) => { + const goToTutorial = (tutorialId: string, id: string) => { sendEventTelemetry({ event: TelemetryEvent.DATABASE_TIPS_TUTORIAL_CLICKED, eventData: { @@ -70,7 +71,8 @@ const Recommendations = () => { } }) - dispatch(openTutorialByPath(mdPath || '', history)) + const tutorialPath = findTutorialPath({ id: tutorialId || '' }) + dispatch(openTutorialByPath(tutorialPath || '', history)) } const onRedisStackClick = (event: React.MouseEvent) => event.stopPropagation() @@ -145,7 +147,7 @@ const Recommendations = () => { content = [], badges = [], redisStack = false, - tutorial, + tutorialId, telemetryEvent } = recommendationsContent[name] || {} @@ -180,12 +182,12 @@ const Recommendations = () => {
- {tutorial && ( + {tutorialId && ( goToTutorial(tutorial, id)} + onClick={() => goToTutorial(tutorialId, id)} data-testid={`${id}-to-tutorial-btn`} > Tutorial diff --git a/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.spec.tsx b/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.spec.tsx index 50402f3587..7adb5e9ef5 100644 --- a/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.spec.tsx +++ b/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.spec.tsx @@ -6,6 +6,7 @@ import { cleanup, clearStoreActions, render, fireEvent, screen, mockedStore } fr import { changeSelectedTab, toggleInsightsPanel } from 'uiSrc/slices/panels/insights' import { InsightsPanelTabs } from 'uiSrc/slices/interfaces/insights' +import { findTutorialPath } from 'uiSrc/utils' import NoLibrariesScreen, { IProps } from './NoLibrariesScreen' const mockedProps = mock() @@ -17,8 +18,13 @@ beforeEach(() => { store.clearActions() }) -jest.mock('uiSrc/slices/workbench/wb-guides', () => ({ - ...jest.requireActual('uiSrc/slices/workbench/wb-guides'), +jest.mock('uiSrc/utils', () => ({ + ...jest.requireActual('uiSrc/utils'), + findTutorialPath: jest.fn(), +})) + +jest.mock('uiSrc/slices/workbench/wb-tutorials', () => ({ + ...jest.requireActual('uiSrc/slices/workbench/wb-tutorials'), workbenchGuidesSelector: jest.fn().mockReturnValue({ items: [{ label: 'Quick guides', @@ -50,7 +56,8 @@ describe('NoLibrariesScreen', () => { it('should call proper actions and push to quick guides page ', () => { const pushMock = jest.fn() - reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }) + reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock }); + (findTutorialPath as jest.Mock).mockImplementation(() => 'path') render() @@ -62,7 +69,7 @@ describe('NoLibrariesScreen', () => { ] expect(clearStoreActions(store.getActions())).toEqual(clearStoreActions(expectedActions)) expect(pushMock).toBeCalledWith({ - search: 'guidePath=/quick-guides/triggers-and-functions/introduction.md' + search: 'path=tutorials/path' }) }) diff --git a/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.tsx b/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.tsx index ba8028d548..b9361785e5 100644 --- a/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.tsx +++ b/redisinsight/ui/src/pages/triggered-functions/components/NoLibrariesScreen/NoLibrariesScreen.tsx @@ -20,7 +20,7 @@ import { OAuthSocialSource, RedisDefaultModules } from 'uiSrc/slices/interfaces' import { OAuthConnectFreeDb, OAuthSsoHandlerDialog } from 'uiSrc/components' import { freeInstancesSelector } from 'uiSrc/slices/instances/instances' -import { getDbWithModuleLoaded } from 'uiSrc/utils' +import { findTutorialPath, getDbWithModuleLoaded } from 'uiSrc/utils' import { openTutorialByPath } from 'uiSrc/slices/panels/insights' import styles from './styles.module.scss' @@ -30,7 +30,7 @@ export interface IProps { onAddLibrary?: () => void } -const mdPath = '/quick-guides/triggers-and-functions/introduction.md' +const tutorialId = 'tf-intro' const ListItem = ({ item }: { item: string }) => (
  • @@ -54,7 +54,8 @@ const NoLibrariesScreen = (props: IProps) => { const freeDbWithModule = getDbWithModuleLoaded(freeInstances, RedisDefaultModules.RedisGears) const goToTutorial = () => { - dispatch(openTutorialByPath(mdPath, history)) + const tutorialPath = findTutorialPath({ id: tutorialId ?? '' }) + dispatch(openTutorialByPath(tutorialPath ?? '', history)) } return ( diff --git a/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.spec.tsx b/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.spec.tsx index 6025098f50..9c003775ee 100644 --- a/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.spec.tsx +++ b/redisinsight/ui/src/pages/workbench/components/wb-view/WBView/WBView.spec.tsx @@ -23,16 +23,6 @@ jest.mock('uiSrc/utils/workbench', () => ({ updateWBHistoryStorage: jest.fn(), })) -jest.mock('uiSrc/slices/workbench/wb-guides', () => { - const defaultState = jest.requireActual('uiSrc/slices/workbench/wb-guides').initialState - return { - ...jest.requireActual('uiSrc/slices/workbench/wb-guides'), - workbenchGuidesSelector: jest.fn().mockReturnValue({ - ...defaultState, - }), - } -}) - describe('WBView', () => { it('should render', () => { expect(render()).toBeTruthy() diff --git a/redisinsight/ui/src/pages/workbench/components/wb-view/WBViewWrapper.spec.tsx b/redisinsight/ui/src/pages/workbench/components/wb-view/WBViewWrapper.spec.tsx index ab36b8ee40..c0d9cf9787 100644 --- a/redisinsight/ui/src/pages/workbench/components/wb-view/WBViewWrapper.spec.tsx +++ b/redisinsight/ui/src/pages/workbench/components/wb-view/WBViewWrapper.spec.tsx @@ -21,7 +21,6 @@ import { workbenchResultsSelector } from 'uiSrc/slices/workbench/wb-results' -import { getWBCustomTutorials } from 'uiSrc/slices/workbench/wb-custom-tutorials' import WBViewWrapper from './WBViewWrapper' let store: typeof mockedStore @@ -81,16 +80,6 @@ jest.mock('uiSrc/slices/workbench/wb-results', () => ({ }) })) -jest.mock('uiSrc/slices/workbench/wb-guides', () => { - const defaultState = jest.requireActual('uiSrc/slices/workbench/wb-guides').initialState - return { - ...jest.requireActual('uiSrc/slices/workbench/wb-guides'), - workbenchGuidesSelector: jest.fn().mockReturnValue({ - ...defaultState, - }), - } -}) - jest.mock('uiSrc/slices/workbench/wb-tutorials', () => { const defaultState = jest.requireActual('uiSrc/slices/workbench/wb-tutorials').initialState return { diff --git a/redisinsight/ui/src/slices/interfaces/content.ts b/redisinsight/ui/src/slices/interfaces/content.ts index d9fe983402..9f44aecb91 100644 --- a/redisinsight/ui/src/slices/interfaces/content.ts +++ b/redisinsight/ui/src/slices/interfaces/content.ts @@ -34,7 +34,7 @@ export interface StateContentGuideLinks { export interface ContentGuideLinks { title: string - tutorial: string + tutorialId: string icon: string description?: string } diff --git a/redisinsight/ui/src/slices/interfaces/recommendations.ts b/redisinsight/ui/src/slices/interfaces/recommendations.ts index f45b708f57..96c46d0c5c 100644 --- a/redisinsight/ui/src/slices/interfaces/recommendations.ts +++ b/redisinsight/ui/src/slices/interfaces/recommendations.ts @@ -37,7 +37,7 @@ export interface IRecommendationsStatic { liveTitle?: string telemetryEvent?: string redisStack?: boolean - tutorial?: string + tutorialId?: string content?: IRecommendationContent[] badges?: string[] } diff --git a/redisinsight/ui/src/slices/panels/insights.ts b/redisinsight/ui/src/slices/panels/insights.ts index 0724ae6a75..b1fab188cd 100644 --- a/redisinsight/ui/src/slices/panels/insights.ts +++ b/redisinsight/ui/src/slices/panels/insights.ts @@ -5,7 +5,7 @@ import { useHistory } from 'react-router-dom' import { Maybe } from 'uiSrc/utils' import { InsightsPanelState, InsightsPanelTabs } from 'uiSrc/slices/interfaces/insights' import { sessionStorageService } from 'uiSrc/services' -import { BrowserStorageItem } from 'uiSrc/constants' +import { BrowserStorageItem, EAManifestFirstKey } from 'uiSrc/constants' import { AppDispatch, RootState } from '../store' const getTabSelected = (tab?: string): InsightsPanelTabs => { @@ -85,14 +85,16 @@ export const { setExplorePanelManifest, } = insightsPanelSlice.actions -export function openTutorialByPath(guidePath: string, history: ReturnType) { +export function openTutorialByPath(path: string, history: ReturnType) { return async (dispatch: AppDispatch) => { dispatch(changeSelectedTab(InsightsPanelTabs.Explore)) dispatch(toggleInsightsPanel(true)) - history.push({ - search: `guidePath=${guidePath}` - }) + if (path) { + history.push({ + search: `path=${EAManifestFirstKey.TUTORIALS}/${path}` + }) + } } } diff --git a/redisinsight/ui/src/slices/store.ts b/redisinsight/ui/src/slices/store.ts index e96ae89903..fe326835c2 100644 --- a/redisinsight/ui/src/slices/store.ts +++ b/redisinsight/ui/src/slices/store.ts @@ -30,7 +30,6 @@ import appFeaturesReducer from './app/features' import appUrlHandlingReducer from './app/url-handling' import appOauthReducer from './oauth/cloud' import workbenchResultsReducer from './workbench/wb-results' -import workbenchGuidesReducer from './workbench/wb-guides' import workbenchTutorialsReducer from './workbench/wb-tutorials' import workbenchCustomTutorialsReducer from './workbench/wb-custom-tutorials' import contentCreateRedisButtonReducer from './content/create-redis-buttons' @@ -89,7 +88,6 @@ export const rootReducer = combineReducers({ }), workbench: combineReducers({ results: workbenchResultsReducer, - guides: workbenchGuidesReducer, tutorials: workbenchTutorialsReducer, customTutorials: workbenchCustomTutorialsReducer, }), diff --git a/redisinsight/ui/src/slices/tests/workbench/wb-guides.spec.ts b/redisinsight/ui/src/slices/tests/workbench/wb-guides.spec.ts deleted file mode 100644 index ce7bc7e4bd..0000000000 --- a/redisinsight/ui/src/slices/tests/workbench/wb-guides.spec.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { cloneDeep } from 'lodash' -import { cleanup, initialStateDefault, mockedStore, } from 'uiSrc/utils/test-utils' -import { IEnablementAreaItem } from 'uiSrc/slices/interfaces' -import { MOCK_GUIDES } from 'uiSrc/constants' -import { resourcesService } from 'uiSrc/services' - -import reducer, { - initialState, - workbenchGuidesSelector, - getWBGuides, - getWBGuidesFailure, - getWBGuidesSuccess, - fetchGuides, - defaultItems, -} from '../../workbench/wb-guides' - -let store: typeof mockedStore -beforeEach(() => { - cleanup() - store = cloneDeep(mockedStore) - store.clearActions() -}) - -describe('slices', () => { - describe('reducer, actions and selectors', () => { - it('should return the initial state on first run', () => { - // Arrange - const nextState = initialState - - // Act - const result = reducer(undefined, {}) - - // Assert - expect(result).toEqual(nextState) - }) - }) - - describe('getWBGuides', () => { - it('should properly set loading', () => { - // Arrange - const loading = true - const state = { - ...initialState, - loading - } - - // Act - const nextState = reducer(initialState, getWBGuides()) - - // Assert - const rootState = Object.assign(initialStateDefault, { - workbench: { - guides: nextState, - }, - }) - - expect(workbenchGuidesSelector(rootState)).toEqual(state) - }) - }) - - describe('getWBGuidesSuccess', () => { - it('should properly set state after success', () => { - // Arrange - const guides: IEnablementAreaItem = MOCK_GUIDES - const state = { - ...initialState, - items: [guides], - } - - // Act - const nextState = reducer(initialState, getWBGuidesSuccess(guides)) - - // Assert - const rootState = Object.assign(initialStateDefault, { - workbench: { - guides: nextState, - }, - }) - - expect(workbenchGuidesSelector(rootState)).toEqual(state) - }) - }) - - describe('getWBGuidesFailure', () => { - it('should properly set error', () => { - // Arrange - const error = 'error' - const state = { - ...initialState, - loading: false, - items: defaultItems, - error - } - - // Act - const nextState = reducer(initialState, getWBGuidesFailure(error)) - - // Assert - const rootState = Object.assign(initialStateDefault, { - workbench: { - guides: nextState, - }, - }) - - expect(workbenchGuidesSelector(rootState)).toEqual(state) - }) - }) - - // thunks - - describe('fetchGuides', () => { - it('succeed to fetch guides items', async () => { - // Arrange - const data = MOCK_GUIDES - const responsePayload = { status: 200, data } - - resourcesService.get = jest.fn().mockResolvedValue(responsePayload) - - // Act - await store.dispatch(fetchGuides(jest.fn())) - - // Assert - const expectedActions = [ - getWBGuides(), - getWBGuidesSuccess(data), - ] - - expect(mockedStore.getActions()).toEqual(expectedActions) - }) - - it('failed to fetch guides items', async () => { - // Arrange - const errorMessage = 'Something was wrong!' - const responsePayload = { - response: { - status: 500, - data: { message: errorMessage }, - }, - } - resourcesService.get = jest.fn().mockRejectedValue(responsePayload) - - // Act - await store.dispatch(fetchGuides()) - - // Assert - const expectedActions = [ - getWBGuides(), - getWBGuidesFailure(errorMessage), - ] - - expect(mockedStore.getActions()).toEqual(expectedActions) - }) - }) -}) diff --git a/redisinsight/ui/src/slices/workbench/wb-guides.ts b/redisinsight/ui/src/slices/workbench/wb-guides.ts deleted file mode 100644 index d7f2264719..0000000000 --- a/redisinsight/ui/src/slices/workbench/wb-guides.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit' -import { ApiEndpoints } from 'uiSrc/constants' -import { getApiErrorMessage, isStatusSuccessful, } from 'uiSrc/utils' -import { resourcesService } from 'uiSrc/services' -import { IEnablementAreaItem, StateWorkbenchEnablementArea } from 'uiSrc/slices/interfaces' - -import { AppDispatch, RootState } from '../store' - -export const defaultItems: IEnablementAreaItem[] = [] - -export const initialState: StateWorkbenchEnablementArea = { - loading: false, - error: '', - items: [], -} - -// A slice for recipes -const workbenchGuidesSlice = createSlice({ - name: 'workbenchGuides', - initialState, - reducers: { - getWBGuides: (state) => { - state.loading = true - }, - getWBGuidesSuccess: (state, { payload }: { payload: IEnablementAreaItem }) => { - state.loading = false - state.items = [payload] - }, - getWBGuidesFailure: (state, { payload }) => { - state.loading = false - state.error = payload - state.items = defaultItems - }, - } -}) - -// A selector -export const workbenchGuidesSelector = (state: RootState) => state.workbench.guides - -// Actions generated from the slice -export const { - getWBGuides, - getWBGuidesSuccess, - getWBGuidesFailure, -} = workbenchGuidesSlice.actions - -// The reducer -export default workbenchGuidesSlice.reducer - -// Asynchronous thunk action -export function fetchGuides(onSuccessAction?: () => void, onFailAction?: () => void) { - return async (dispatch: AppDispatch) => { - dispatch(getWBGuides()) - - try { - const { data, status } = await resourcesService.get(ApiEndpoints.GUIDES) - if (isStatusSuccessful(status)) { - dispatch(getWBGuidesSuccess(data)) - onSuccessAction?.() - } - } catch (error) { - const errorMessage = getApiErrorMessage(error) - dispatch(getWBGuidesFailure(errorMessage)) - onFailAction?.() - } - } -} diff --git a/redisinsight/ui/src/utils/capability.ts b/redisinsight/ui/src/utils/capability.ts index b280fb0ee0..38c162962f 100644 --- a/redisinsight/ui/src/utils/capability.ts +++ b/redisinsight/ui/src/utils/capability.ts @@ -1,14 +1,14 @@ -import { IEnablementAreaItem, OAuthSocialSource, RedisDefaultModules } from 'uiSrc/slices/interfaces' +import { OAuthSocialSource, RedisDefaultModules } from 'uiSrc/slices/interfaces' import { store } from 'uiSrc/slices/store' import { Nullable } from 'uiSrc/utils' -import { findMarkdownPathById } from 'uiSrc/utils/workbench' +import { findMarkdownPath } from 'uiSrc/utils/workbench' const getCapability = ( telemetryName: string = '', name: string = '', - tutorialPage: Nullable = null + path: Nullable = null ) => ({ - telemetryName, name, tutorialPage + telemetryName, name, path }) export const getSourceTutorialByCapability = (moduleName = '') => `${moduleName}_tutorial` @@ -25,7 +25,7 @@ export const getTutorialCapability = (source: any = '') => { return getCapability( 'searchAndQuery', 'Search and query capability', - findMarkdownPathById(store.getState()?.workbench?.tutorials?.items, 'vector_similarity_search') + findMarkdownPath(store.getState()?.workbench?.tutorials?.items, { id: 'sq-intro' }) ) // RedisJSON @@ -34,7 +34,7 @@ export const getTutorialCapability = (source: any = '') => { return getCapability( 'JSON', 'JSON capability', - findMarkdownPathById(store.getState()?.workbench?.tutorials?.items, 'working_with_json') + findMarkdownPath(store.getState()?.workbench?.tutorials?.items, { id: 'ds-json-intro' }) ) // TimeSeries @@ -43,7 +43,7 @@ export const getTutorialCapability = (source: any = '') => { return getCapability( 'timeSeries', 'Time series data structure', - findMarkdownPathById(store.getState()?.workbench?.tutorials?.items, 'redis_for_time_series') + findMarkdownPath(store.getState()?.workbench?.tutorials?.items, { id: 'ds-ts-intro' }) ) // Bloom @@ -52,7 +52,7 @@ export const getTutorialCapability = (source: any = '') => { return getCapability( 'probabilistic', 'Probabilistic data structures', - findMarkdownPathById(store.getState()?.workbench?.tutorials?.items, 'probabilistic_data_structures') + findMarkdownPath(store.getState()?.workbench?.tutorials?.items, { id: 'ds-prob-intro' }) ) default: diff --git a/redisinsight/ui/src/utils/routing.ts b/redisinsight/ui/src/utils/routing.ts index 0cdca57dcd..9964c8409a 100644 --- a/redisinsight/ui/src/utils/routing.ts +++ b/redisinsight/ui/src/utils/routing.ts @@ -28,7 +28,7 @@ export const getRedirectionPage = (pageInput: string, databaseId?: string): Null const pageUrl = new URL(page, window.location.origin) const { pathname, searchParams } = pageUrl - if (searchParams.has('guidePath')) { + if (searchParams.has('guidePath') || searchParams.has('tutorialId')) { page += '&insights=open' } diff --git a/redisinsight/ui/src/utils/test-utils.tsx b/redisinsight/ui/src/utils/test-utils.tsx index 68879390b1..278bdac05a 100644 --- a/redisinsight/ui/src/utils/test-utils.tsx +++ b/redisinsight/ui/src/utils/test-utils.tsx @@ -36,7 +36,6 @@ import { initialState as initialStateCliOutput } from 'uiSrc/slices/cli/cli-outp import { initialState as initialStateMonitor } from 'uiSrc/slices/cli/monitor' import { initialState as initialStateUserSettings } from 'uiSrc/slices/user/user-settings' import { initialState as initialStateWBResults } from 'uiSrc/slices/workbench/wb-results' -import { initialState as initialStateWBEGuides } from 'uiSrc/slices/workbench/wb-guides' import { initialState as initialStateWBETutorials } from 'uiSrc/slices/workbench/wb-tutorials' import { initialState as initialStateWBECustomTutorials } from 'uiSrc/slices/workbench/wb-custom-tutorials' import { initialState as initialStateCreateRedisButtons } from 'uiSrc/slices/content/create-redis-buttons' @@ -104,7 +103,6 @@ const initialStateDefault: RootState = { }, workbench: { results: cloneDeep(initialStateWBResults), - guides: cloneDeep(initialStateWBEGuides), tutorials: cloneDeep(initialStateWBETutorials), customTutorials: cloneDeep(initialStateWBECustomTutorials), }, diff --git a/redisinsight/ui/src/utils/tests/capability.spec.ts b/redisinsight/ui/src/utils/tests/capability.spec.ts index a69ca2ee4e..7df5df04bb 100644 --- a/redisinsight/ui/src/utils/tests/capability.spec.ts +++ b/redisinsight/ui/src/utils/tests/capability.spec.ts @@ -15,11 +15,11 @@ describe('getSourceTutorialByCapability', () => { }) }) -const emptyCapability = { name: '', telemetryName: '', tutorialPage: null, } -const searchCapability = { name: 'Search and query capability', telemetryName: 'searchAndQuery', tutorialPage: null, } -const jsonCapability = { name: 'JSON capability', telemetryName: 'JSON', tutorialPage: null, } -const tsCapability = { name: 'Time series data structure', telemetryName: 'timeSeries', tutorialPage: null, } -const bloomCapability = { name: 'Probabilistic data structures', telemetryName: 'probabilistic', tutorialPage: null, } +const emptyCapability = { name: '', telemetryName: '', path: null, } +const searchCapability = { name: 'Search and query capability', telemetryName: 'searchAndQuery', path: null, } +const jsonCapability = { name: 'JSON capability', telemetryName: 'JSON', path: null, } +const tsCapability = { name: 'Time series data structure', telemetryName: 'timeSeries', path: null, } +const bloomCapability = { name: 'Probabilistic data structures', telemetryName: 'probabilistic', path: null, } const getTutorialCapabilityTests: any[] = [ [OAuthSocialSource.RediSearch, searchCapability], diff --git a/redisinsight/ui/src/utils/tests/workbench.spec.ts b/redisinsight/ui/src/utils/tests/workbench.spec.ts index f250f22bf8..733a20ef38 100644 --- a/redisinsight/ui/src/utils/tests/workbench.spec.ts +++ b/redisinsight/ui/src/utils/tests/workbench.spec.ts @@ -1,6 +1,6 @@ import { ExecuteQueryParams, ResultsMode, RunQueryMode } from 'uiSrc/slices/interfaces' -import { getExecuteParams, getParsedParamsInQuery, findMarkdownPathByPath, parseParams, findMarkdownPathById } from 'uiSrc/utils' -import { CodeButtonParams, MOCK_GUIDES_ITEMS, MOCK_TUTORIALS_ITEMS } from 'uiSrc/constants' +import { getExecuteParams, getParsedParamsInQuery, parseParams, findMarkdownPath } from 'uiSrc/utils' +import { CodeButtonParams, MOCK_TUTORIALS_ITEMS } from 'uiSrc/constants' const paramsState: ExecuteQueryParams = { activeRunQueryMode: RunQueryMode.ASCII, @@ -51,21 +51,28 @@ describe('getParsedParamsInQuery', () => { }) }) -const findMarkdownPathByPathTests = [ - { input: '/static/workbench/quick-guides/document/learn-more.md', expected: '0/0' }, - { input: 'quick-guides/working-with-hash.html', expected: '0/2' }, - { input: 'quick-guides/working-with-hash.html', expected: '0/2' }, - { input: 'quick-guides/document-capabilities.html', expected: '1' }, - { input: '/redis_stack/working_with_json.md', manifest: MOCK_TUTORIALS_ITEMS, expected: '4' }, - { input: 'quick-guides', expected: '0/0' }, +const findMarkdownPathTests = [ + // mdPath + { input: { mdPath: '/static/workbench/quick-guides/document/learn-more.md' }, expected: '0/0' }, + { input: { mdPath: 'quick-guides/working-with-hash.html' }, expected: '0/2' }, + { input: { mdPath: 'quick-guides/working-with-json.html' }, expected: '0/1' }, + { input: { mdPath: 'quick-guides/document-capabilities.html' }, expected: '1' }, + { input: { mdPath: '/redis_stack/working_with_json.md' }, expected: '4' }, + + // id + { input: { id: 'document-capabilities' }, expected: '0/0' }, + { input: { id: 'working-with-hash' }, expected: '0/2' }, + { input: { id: 'working-with-json' }, expected: '0/1' }, + { input: { id: 'second-internal-page' }, expected: '2' }, + { input: { id: 'working_with_json' }, expected: '4' }, ] -describe('findMarkdownPathByPath', () => { - test.each(findMarkdownPathByPathTests)( +describe('findMarkdownPath', () => { + test.each(findMarkdownPathTests)( '%j', - ({ input, manifest = MOCK_GUIDES_ITEMS, expected }) => { + ({ input, expected }) => { // @ts-ignore - const result = findMarkdownPathByPath(manifest, input) + const result = findMarkdownPath(MOCK_TUTORIALS_ITEMS, input) expect(result).toEqual(expected) } ) @@ -90,17 +97,3 @@ describe('parseParams', () => { expect(result).toEqual(expected) }) }) - -const findMarkdownPathByIdTests: any[] = [ - ['123', null], - ['second-internal-page', { args: { path: 'quick-guides/document-capabilities.html' }, id: 'second-internal-page', label: 'Second Internal Page', type: 'internal-link' }], - ['document-capabilities', { args: { path: '/static/workbench/quick-guides/document/learn-more.md' }, id: 'document-capabilities', label: 'Document Capabilities', type: 'internal-link' }], -] - -describe('findMarkdownPathById', () => { - it.each(findMarkdownPathByIdTests)('for input: %s (id), should be output: %s', - (id, expected) => { - const result = findMarkdownPathById(MOCK_TUTORIALS_ITEMS, id) - expect(result).toEqual(expected) - }) -}) diff --git a/redisinsight/ui/src/utils/workbench.ts b/redisinsight/ui/src/utils/workbench.ts index aa1d76ed11..612fd4a1a6 100644 --- a/redisinsight/ui/src/utils/workbench.ts +++ b/redisinsight/ui/src/utils/workbench.ts @@ -3,6 +3,7 @@ import { CodeButtonResults, CodeButtonRunQueryMode, CodeButtonParams } from 'uiS import { WBQueryType } from 'uiSrc/pages/workbench/constants' import { EnablementAreaComponent, ExecuteQueryParams, IEnablementAreaItem, IPluginVisualization, ResultsMode, RunQueryMode } from 'uiSrc/slices/interfaces' import { getVisualizationsByCommand } from 'uiSrc/utils/plugins' +import { store } from 'uiSrc/slices/store' import { getMonacoLines, isParamsLine } from './monaco' import { Maybe, Nullable } from './types' @@ -65,55 +66,40 @@ export const getParsedParamsInQuery = (query: string) => { return parsedParams } -export const findMarkdownPathByPath = (manifest: IEnablementAreaItem[], markdownPath: string) => { +export const findMarkdownPath = ( + manifest: IEnablementAreaItem[], + { mdPath = '', id = '' }: { mdPath?: string, id?: string } +): Nullable => { if (!manifest) return null - const findPath = (data: IEnablementAreaItem[], mdPath: string, path: number[] = []): Nullable => { + const stack: { data: IEnablementAreaItem[]; mdPath: string; id: string; path: number[] }[] = [ + { data: manifest, mdPath, id, path: [] } + ] + + while (stack.length > 0) { + const { data, mdPath, id, path } = stack.pop()! + for (let i = 0; i < data.length; i++) { const obj = data[i] const currentPath = [...path, i] + const isCurrentObject = (id && obj.id === id) || (mdPath && obj.args?.path?.includes(mdPath)) - if (obj.type === EnablementAreaComponent.InternalLink && obj.args?.path?.includes(mdPath)) { - return currentPath + if (obj.type === EnablementAreaComponent.InternalLink && isCurrentObject) { + return currentPath.join('/') } if (obj.type === EnablementAreaComponent.Group && obj.children) { - const result = findPath(obj.children, mdPath, currentPath) - - if (result) { - return result - } + stack.push({ data: obj.children, mdPath, id, path: currentPath }) } } - - return null - } - - const result = findPath(manifest, markdownPath) - return result ? result.join('/') : null -} - -export const findMarkdownPathById = ( - manifest: IEnablementAreaItem[] = [], - id: string = '', -): Nullable => { - const stack = [...manifest] - - while (stack.length > 0) { - const currentObject = stack.pop() - - if (currentObject?.id === id) { - return currentObject - } - - if (currentObject?.children) { - stack.push(...currentObject.children) - } } return null } +export const findTutorialPath = (options: { mdPath?: string, id?: string }) => + findMarkdownPath(store.getState().workbench.tutorials?.items, options) + const isGroupMode = (mode?: ResultsMode) => mode === ResultsMode.GroupMode const isRawMode = (mode?: RunQueryMode) => mode === RunQueryMode.Raw const isSilentMode = (mode?: ResultsMode) => mode === ResultsMode.Silent