From b7ad394aa28f50a5d6e12d50c60f6bdc4ee7892f Mon Sep 17 00:00:00 2001 From: Ihor Korenets Date: Fri, 26 Jul 2024 15:30:14 +0300 Subject: [PATCH] [Mob navigation]: refactored Sidebar into SidebarComponent, added apiRefItems into uuiApp in index.tsx --- app/src/common/docs/SidebarComponent.tsx | 63 +++++++++ .../docs/baseDocBlock/BaseDocsBlock.tsx | 14 +- app/src/data/apiDefinition.ts | 4 +- app/src/data/appContext.ts | 4 +- app/src/documents/DocumentsPage.tsx | 126 ++++-------------- app/src/index.tsx | 35 ++++- 6 files changed, 127 insertions(+), 119 deletions(-) create mode 100644 app/src/common/docs/SidebarComponent.tsx diff --git a/app/src/common/docs/SidebarComponent.tsx b/app/src/common/docs/SidebarComponent.tsx new file mode 100644 index 0000000000..46c0b2c371 --- /dev/null +++ b/app/src/common/docs/SidebarComponent.tsx @@ -0,0 +1,63 @@ +import { DocItem } from '../../documents/structure'; +import { Sidebar } from '../sidebar'; +import React from 'react'; +import { DataRowProps } from '@epam/uui-core'; +import { TreeListItem } from '@epam/uui-components'; +import { useQuery } from '../../helpers'; +import { TMode } from './docsConstants'; +import { TTheme } from '../../data'; +import { svc } from '../../services'; + +type DocsQuery = { + id: string, + mode?: TMode, + isSkin?: boolean, + theme?: TTheme, + category?: string +}; + +const redirectTo = (query: DocsQuery) => + svc.uuiRouter.redirect({ + pathname: '/documents', + query, + }); + +export function SidebarComponent(props: { docItems: DocItem[] }) { + const mode = useQuery('mode') || TMode.doc; + const queryParamId: string = useQuery('id'); + const isSkin = useQuery('isSkin'); + const theme = useQuery('theme'); + + const onChange = (row: DataRowProps) => { + if (row.parentId === 'components') { + redirectTo({ + category: row.parentId, + mode, + id: row.id, + isSkin, + theme, + }); + } else { + redirectTo({ id: row.id, category: row.parentId }); + } + }; + + return ( + + value={ queryParamId } + onValueChange={ onChange } + items={ props.docItems } + getSearchFields={ (i) => [i.name, ...(i.tags || [])] } + getItemLink={ (row) => + !row.isFoldable && { + pathname: '/documents', + query: { + id: row.id, + mode: (row.parentId && mode), + isSkin: (row.parentId && isSkin), + category: row.parentId, + }, + } } + /> + ); +} diff --git a/app/src/common/docs/baseDocBlock/BaseDocsBlock.tsx b/app/src/common/docs/baseDocBlock/BaseDocsBlock.tsx index a76fa2224f..adf4a1f7e9 100644 --- a/app/src/common/docs/baseDocBlock/BaseDocsBlock.tsx +++ b/app/src/common/docs/baseDocBlock/BaseDocsBlock.tsx @@ -10,12 +10,11 @@ import { PropExplorerTab } from './tabs/propExplorerTab'; import { TabsNav } from './components/tabsNav'; import { SkinModeToggler } from './components/skinModeToggler'; import { QueryHelpers } from './utils/queryHelpers'; -import { DocItem } from '../../../documents/structure'; -import { Sidebar } from '../../sidebar'; import { ReactComponent as ActionAlignLeftOutlineIcon } from '@epam/assets/icons/action-align_left-outline.svg'; +import { SidebarComponent } from '../SidebarComponent'; +import cx from 'classnames'; // import css from './BaseDocsBlock.module.scss'; -import cx from 'classnames'; type State = { isOpen: boolean; @@ -152,7 +151,6 @@ export abstract class BaseDocsBlock extends React.Component { render() { const mode = QueryHelpers.getMode(); const supportedModes = Object.values(TMode).filter((m) => this.isModeSupported(m)); - const { queryParamId, onChange, items, getSearchFields, getItemLink } = this.props.sidebarProps; return (
{ /> - - value={ queryParamId } - onValueChange={ onChange } - items={ items } - getSearchFields={ getSearchFields } - getItemLink={ getItemLink } - /> + ) } diff --git a/app/src/data/apiDefinition.ts b/app/src/data/apiDefinition.ts index b8c9157a48..22edb0af64 100644 --- a/app/src/data/apiDefinition.ts +++ b/app/src/data/apiDefinition.ts @@ -1,7 +1,5 @@ import { getDemoApi } from '@epam/uui-docs'; -import type { - CommonContexts, UuiContexts, ITablePreset, IProcessRequest, -} from '@epam/uui-core'; +import type { CommonContexts, UuiContexts, ITablePreset, IProcessRequest } from '@epam/uui-core'; import { TType, TTypeRef } from '@epam/uui-docs'; import { TDocsGenTypeSummary } from '../common/apiReference/types'; import { IUuiTokensCollection } from '../sandbox/tokens/palette/types/sharedTypes'; diff --git a/app/src/data/appContext.ts b/app/src/data/appContext.ts index 43af206e1f..f6219b2d46 100644 --- a/app/src/data/appContext.ts +++ b/app/src/data/appContext.ts @@ -1,12 +1,14 @@ import { ThemeBaseParams, builtInThemes, TTheme } from './themes'; import { CustomThemeManifest, loadCustomThemes } from './customThemes'; +import { DocItem } from '../documents/structure'; export interface AppContext { themes: TTheme[], themesById: Record, + apiRefItems: DocItem[], } -export async function getAppContext() { +export async function getThemeContext() { const customThemesArr = await loadCustomThemes(); const allThemes = [...builtInThemes, ...customThemesArr]; const themesById = allThemes.reduce>((acc, t) => { diff --git a/app/src/documents/DocumentsPage.tsx b/app/src/documents/DocumentsPage.tsx index 0407d58a71..d81f6ece32 100644 --- a/app/src/documents/DocumentsPage.tsx +++ b/app/src/documents/DocumentsPage.tsx @@ -1,14 +1,13 @@ import React, { useEffect, useMemo, useState } from 'react'; -import { DataRowProps } from '@epam/uui-core'; -import { TreeListItem } from '@epam/uui-components'; +import { useUuiContext } from '@epam/uui-core'; import { FlexRow } from '@epam/uui'; -import { AppHeader, Page, Sidebar, TypeRefPage } from '../common'; -import { svc } from '../services'; -import { DocItem, items as itemsStructure } from './structure'; +import { AppHeader, Page } from '../common'; +import { items as itemsStructure } from './structure'; import { useQuery } from '../helpers'; import { codesandboxService } from '../data/service'; import { TMode } from '../common/docs/docsConstants'; -import { TTheme } from '../data'; +import { AppContext, type TApi, TTheme } from '../data'; +import { SidebarComponent } from '../common/docs/SidebarComponent'; type DocsQuery = { id: string; @@ -18,56 +17,35 @@ type DocsQuery = { category?: string; }; -const redirectTo = (query: DocsQuery) => - svc.uuiRouter.redirect({ - pathname: '/documents', - query, - }); - -async function loadApiReferenceStructure(): Promise { - if (!svc.api) { - throw new Error('svc.api not available'); - } - const { content: navigation } = await svc.api.getDocsGenExports(); - const root = { id: 'ApiReference', name: 'Api Reference' }; - return Object.keys(navigation).reduce((acc, moduleName) => { - acc.push({ id: moduleName, name: moduleName, parentId: root.id }); - navigation[moduleName].forEach((exportName) => { - acc.push({ id: `${moduleName}:${exportName}`, name: exportName, parentId: moduleName, component: TypeRefPage }); - }); - return acc; - }, [root]); -} - -function useItems(selectedId: string) { - const [apiRefItems, setApiRefItems] = useState(); - - useEffect(() => { - loadApiReferenceStructure().then((res) => { - setApiRefItems(res); - }); - }, []); - - return useMemo(() => { - if (apiRefItems) { - const items = itemsStructure.concat(apiRefItems); - const PageComponent = items.find((item) => item.id === selectedId)?.component; - return { - items, - PageComponent, - }; - } - }, [apiRefItems, selectedId]); -} - export function DocumentsPage() { + const svc = useUuiContext(); const queryParamId: string = useQuery('id'); const isSkin = useQuery('isSkin'); const theme = useQuery('theme'); const itemsInfo = useItems(queryParamId); - const mode = useQuery('mode') || TMode.doc; const [pageWidth, setPageWidth] = useState(window.innerWidth); + const redirectTo = (query: DocsQuery) => + svc.uuiRouter.redirect({ + pathname: '/documents', + query, + }); + + function useItems(selectedId: string) { + const { apiRefItems } = svc.uuiApp; + + return useMemo(() => { + if (apiRefItems) { + const items = itemsStructure.concat(apiRefItems); + const PageComponent = items.find((item) => item.id === selectedId)?.component; + return { + items, + PageComponent, + }; + } + }, [apiRefItems, selectedId]); + } + useEffect(() => { if (itemsInfo && !itemsInfo.PageComponent) { redirectTo({ id: itemsInfo.items[0].id, mode: TMode.doc, isSkin: isSkin, theme: theme }); @@ -86,61 +64,15 @@ export function DocumentsPage() { }; }, []); - const onChange = (row: DataRowProps) => { - if (row.parentId === 'components') { - redirectTo({ - category: row.parentId, - mode, - id: row.id, - isSkin, - theme, - }); - } else { - redirectTo({ id: row.id, category: row.parentId }); - } - }; - const PageComponent = itemsInfo?.PageComponent; - const sidebarProps = { - queryParamId, - onChange, - items: itemsInfo?.items, - getSearchFields: (i: DocItem) => [i.name, ...(i.tags || [])], - getItemLink: (row: DataRowProps) => - !row.isFoldable && { - pathname: '/documents', - query: { - id: row.id, - mode: (row.parentId && mode), - isSkin: (row.parentId && isSkin), - category: row.parentId, - }, - }, - }; - return ( }> { pageWidth > 768 && ( - - value={ queryParamId } - onValueChange={ onChange } - items={ itemsInfo?.items } - getSearchFields={ (i) => [i.name, ...(i.tags || [])] } - getItemLink={ (row) => - !row.isFoldable && { - pathname: '/documents', - query: { - id: row.id, - mode: (row.parentId && mode), - isSkin: (row.parentId && isSkin), - category: row.parentId, - }, - } } - /> + ) } - { PageComponent && } + { PageComponent && } ); diff --git a/app/src/index.tsx b/app/src/index.tsx index 2584f8a217..a6ec19149b 100644 --- a/app/src/index.tsx +++ b/app/src/index.tsx @@ -3,15 +3,14 @@ import { createRoot } from 'react-dom/client'; import { RouterProvider } from 'react-router'; import { createBrowserRouter } from 'react-router-dom'; import { init as initApm } from '@elastic/apm-rum'; -import { - Router6AdaptedRouter, useUuiServices, - UuiContext, IProcessRequest, GAListener, -} from '@epam/uui-core'; +import { Router6AdaptedRouter, useUuiServices, UuiContext, IProcessRequest, GAListener } from '@epam/uui-core'; import { AmplitudeListener } from './analyticsEvents'; import { svc } from './services'; import App from './App'; -import { getApi, TApi, AppContext, getAppContext } from './data'; +import { getApi, TApi, AppContext, getThemeContext } from './data'; import { getAppRootNode } from './helpers/appRootUtils'; +import { DocItem } from './documents/structure'; +import { TypeRefPage } from './common'; import '@epam/internal/styles.css'; import '@epam/assets/theme/theme_vanilla_thunder.scss'; import '@epam/assets/theme/theme_loveship_dark.scss'; @@ -44,6 +43,24 @@ const apm = initApm({ }); apm.addLabels({ project: 'epm-uui', service_type: 'ui' }); +const getApiRefItems = (navigation: Record):DocItem[] => { + const root = { id: 'ApiReference', name: 'Api Reference' }; + + return Object.keys(navigation).reduce((acc, moduleName) => { + const moduleExports = navigation[moduleName]; + acc.push({ id: moduleName, name: moduleName, parentId: root.id }); + moduleExports.forEach((exportName) => { + acc.push({ + id: `${moduleName}:${exportName}`, + name: exportName, + parentId: moduleName, + component: TypeRefPage, + }); + }); + return acc; + }, [root]); +}; + function UuiEnhancedApp() { const [isLoaded, setIsLoaded] = useState(false); const { services } = useUuiServices({ @@ -54,9 +71,13 @@ function UuiEnhancedApp() { }); useEffect(() => { + Object.assign(svc, services); async function initServices() { - services.uuiApp = await getAppContext(); - Object.assign(svc, services); + const docGenExports = await svc.api.getDocsGenExports(); + const apiRefItems = getApiRefItems(docGenExports.content); + const themeContext = await getThemeContext(); + services.uuiApp = { ...themeContext, apiRefItems }; + isProduction && services.uuiAnalytics.addListener(new GAListener(GA_CODE)); services.uuiAnalytics.addListener(new AmplitudeListener(AMP_CODE)); setIsLoaded(true);