Skip to content

Commit c09d9be

Browse files
authored
feat: use custom app title (#2584)
1 parent dd0108a commit c09d9be

File tree

10 files changed

+102
-40
lines changed

10 files changed

+102
-40
lines changed

src/containers/App/App.tsx

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,54 @@ import React from 'react';
33
import type {Store} from '@reduxjs/toolkit';
44
import type {History} from 'history';
55
import {Helmet} from 'react-helmet-async';
6-
import {connect} from 'react-redux';
76

87
import {componentsRegistry} from '../../components/ComponentsProvider/componentsRegistry';
9-
import type {RootState} from '../../store';
8+
import {useTypedSelector} from '../../utils/hooks';
109
import ReduxTooltip from '../ReduxTooltip/ReduxTooltip';
1110
import type {YDBEmbeddedUISettings} from '../UserSettings/settings';
1211

12+
import {useAppTitle} from './AppTitleContext';
1313
import ContentWrapper, {Content} from './Content';
1414
import {NavigationWrapper} from './NavigationWrapper';
1515
import {Providers} from './Providers';
1616

1717
import './App.scss';
1818

19+
const defaultAppTitle = 'YDB Monitoring';
20+
1921
export interface AppProps {
2022
store: Store;
2123
history: History;
22-
singleClusterMode: boolean;
2324
userSettings?: YDBEmbeddedUISettings;
2425
children?: React.ReactNode;
26+
appTitle?: string;
2527
}
2628

27-
function App({store, history, singleClusterMode, children, userSettings}: AppProps) {
29+
function App({store, history, children, userSettings, appTitle = defaultAppTitle}: AppProps) {
2830
const ChatPanel = componentsRegistry.get('ChatPanel');
2931

3032
return (
31-
<Providers store={store} history={history}>
32-
<Helmet defaultTitle="YDB Monitoring" titleTemplate="%s — YDB Monitoring" />
33+
<Providers store={store} history={history} appTitle={appTitle}>
34+
<AppContent userSettings={userSettings}>{children}</AppContent>
35+
{ChatPanel && <ChatPanel />}
36+
<ReduxTooltip />
37+
</Providers>
38+
);
39+
}
40+
41+
function AppContent({
42+
userSettings,
43+
children,
44+
}: {
45+
userSettings?: YDBEmbeddedUISettings;
46+
children?: React.ReactNode;
47+
}) {
48+
const {appTitle} = useAppTitle();
49+
const singleClusterMode = useTypedSelector((state) => state.singleClusterMode);
50+
51+
return (
52+
<React.Fragment>
53+
<Helmet defaultTitle={appTitle} titleTemplate={`%s — ${appTitle}`} />
3354
<ContentWrapper>
3455
<NavigationWrapper
3556
singleClusterMode={singleClusterMode}
@@ -39,16 +60,8 @@ function App({store, history, singleClusterMode, children, userSettings}: AppPro
3960
<div id="fullscreen-root"></div>
4061
</NavigationWrapper>
4162
</ContentWrapper>
42-
{ChatPanel && <ChatPanel />}
43-
<ReduxTooltip />
44-
</Providers>
63+
</React.Fragment>
4564
);
4665
}
4766

48-
function mapStateToProps(state: RootState) {
49-
return {
50-
singleClusterMode: state.singleClusterMode,
51-
};
52-
}
53-
54-
export default connect(mapStateToProps)(App);
67+
export default App;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React, {createContext, useContext} from 'react';
2+
3+
interface AppTitleContextType {
4+
appTitle: string;
5+
}
6+
7+
const AppTitleContext = createContext<AppTitleContextType | undefined>(undefined);
8+
9+
interface AppTitleProviderProps {
10+
appTitle: string;
11+
children: React.ReactNode;
12+
}
13+
14+
export function AppTitleProvider({appTitle, children}: AppTitleProviderProps) {
15+
return <AppTitleContext.Provider value={{appTitle}}>{children}</AppTitleContext.Provider>;
16+
}
17+
18+
export function useAppTitle(): AppTitleContextType {
19+
const context = useContext(AppTitleContext);
20+
if (context === undefined) {
21+
throw new Error('useAppTitle must be used within an AppTitleProvider');
22+
}
23+
return context;
24+
}

src/containers/App/Providers.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,38 @@ import {THEME_KEY} from '../../utils/constants';
1717
import {toaster} from '../../utils/createToast';
1818
import {useSetting} from '../../utils/hooks';
1919

20+
import {AppTitleProvider} from './AppTitleContext';
21+
2022
interface ProvidersProps {
2123
store: Store;
2224
history: History;
2325
componentsRegistry?: ComponentsRegistry;
2426
children: React.ReactNode;
27+
appTitle: string;
2528
}
2629

2730
export function Providers({
2831
store,
2932
history,
3033
componentsRegistry = defaultComponentsRegistry,
3134
children,
35+
appTitle,
3236
}: ProvidersProps) {
3337
return (
3438
<HelmetProvider>
3539
<Provider store={store}>
3640
<Router history={history}>
3741
<QueryParamProvider adapter={ReactRouter5Adapter}>
38-
<Theme>
39-
<ToasterProvider toaster={toaster}>
40-
<ComponentsProvider registry={componentsRegistry}>
41-
<NiceModal.Provider>{children}</NiceModal.Provider>
42-
<ToasterComponent />
43-
</ComponentsProvider>
44-
</ToasterProvider>
45-
</Theme>
42+
<AppTitleProvider appTitle={appTitle}>
43+
<Theme>
44+
<ToasterProvider toaster={toaster}>
45+
<ComponentsProvider registry={componentsRegistry}>
46+
<NiceModal.Provider>{children}</NiceModal.Provider>
47+
<ToasterComponent />
48+
</ComponentsProvider>
49+
</ToasterProvider>
50+
</Theme>
51+
</AppTitleProvider>
4652
</QueryParamProvider>
4753
</Router>
4854
</Provider>

src/containers/AppWithClusters/AppWithClusters.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,18 @@ export interface AppWithClustersProps {
1515
history: History;
1616
userSettings?: YDBEmbeddedUISettings;
1717
children?: React.ReactNode;
18+
appTitle?: string;
1819
}
1920

20-
export function AppWithClusters({store, history, userSettings, children}: AppWithClustersProps) {
21+
export function AppWithClusters({
22+
store,
23+
history,
24+
userSettings,
25+
appTitle,
26+
children,
27+
}: AppWithClustersProps) {
2128
return (
22-
<App store={store} history={history} userSettings={userSettings}>
29+
<App store={store} history={history} userSettings={userSettings} appTitle={appTitle}>
2330
<AppSlots.ClusterSlot>
2431
{({component}) => {
2532
return (

src/containers/Cluster/Cluster.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type {
3030
import {EFlag} from '../../types/api/enums';
3131
import {cn} from '../../utils/cn';
3232
import {useAutoRefreshInterval, useTypedDispatch, useTypedSelector} from '../../utils/hooks';
33+
import {useAppTitle} from '../App/AppTitleContext';
3334
import {Nodes} from '../Nodes/Nodes';
3435
import {PaginatedStorage} from '../Storage/PaginatedStorage';
3536
import {TabletsTable} from '../Tablets/TabletsTable';
@@ -127,11 +128,13 @@ export function Cluster({
127128
[activeTabId, actualClusterTabs],
128129
);
129130

131+
const {appTitle} = useAppTitle();
132+
130133
return (
131134
<div className={b()} ref={container}>
132135
<Helmet
133-
defaultTitle={`${clusterTitle}YDB Monitoring`}
134-
titleTemplate={`%s — ${clusterTitle}YDB Monitoring`}
136+
defaultTitle={`${clusterTitle}${appTitle}`}
137+
titleTemplate={`%s — ${clusterTitle}${appTitle}`}
135138
>
136139
{activeTab ? <title>{activeTab.title}</title> : null}
137140
</Helmet>

src/containers/Node/Node.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {nodeApi} from '../../store/reducers/node/node';
2222
import type {PreparedNode} from '../../store/reducers/node/types';
2323
import {cn} from '../../utils/cn';
2424
import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks';
25+
import {useAppTitle} from '../App/AppTitleContext';
2526
import {PaginatedStorage} from '../Storage/PaginatedStorage';
2627
import {Tablets} from '../Tablets/Tablets';
2728

@@ -121,12 +122,10 @@ interface NodePageHelmetProps {
121122
}
122123

123124
function NodePageHelmet({node, activeTabTitle}: NodePageHelmetProps) {
125+
const {appTitle} = useAppTitle();
124126
const host = node?.Host ? node.Host : i18n('node');
125127
return (
126-
<Helmet
127-
titleTemplate={`%s — ${host} — YDB Monitoring`}
128-
defaultTitle={`${host} — YDB Monitoring`}
129-
>
128+
<Helmet titleTemplate={`%s — ${host}${appTitle}`} defaultTitle={`${host}${appTitle}`}>
130129
<title>{activeTabTitle}</title>
131130
</Helmet>
132131
);

src/containers/PDiskPage/PDiskPage.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {cn} from '../../utils/cn';
2525
import {getPDiskId, getSeverityColor} from '../../utils/disks/helpers';
2626
import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks';
2727
import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
28+
import {useAppTitle} from '../App/AppTitleContext';
2829
import {PaginatedStorage} from '../Storage/PaginatedStorage';
2930

3031
import {DecommissionButton} from './DecommissionButton/DecommissionButton';
@@ -134,6 +135,8 @@ export function PDiskPage() {
134135
}
135136
};
136137

138+
const {appTitle} = useAppTitle();
139+
137140
const renderHelmet = () => {
138141
const pDiskPagePart = pDiskId
139142
? `${pDiskPageKeyset('pdisk')} ${pDiskId}`
@@ -143,8 +146,8 @@ export function PDiskPage() {
143146

144147
return (
145148
<Helmet
146-
titleTemplate={`%s - ${pDiskPagePart}${nodePagePart}YDB Monitoring`}
147-
defaultTitle={`${pDiskPagePart}${nodePagePart}YDB Monitoring`}
149+
titleTemplate={`%s - ${pDiskPagePart}${nodePagePart}${appTitle}`}
150+
defaultTitle={`${pDiskPagePart}${nodePagePart}${appTitle}`}
148151
/>
149152
);
150153
};

src/containers/StorageGroupPage/StorageGroupPage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {EFlag} from '../../types/api/enums';
1919
import {valueIsDefined} from '../../utils';
2020
import {cn} from '../../utils/cn';
2121
import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks';
22+
import {useAppTitle} from '../App/AppTitleContext';
2223
import {PaginatedStorage} from '../Storage/PaginatedStorage';
2324

2425
import {storageGroupPageKeyset} from './i18n';
@@ -53,6 +54,7 @@ export function StorageGroupPage() {
5354
const storageGroupData = groupQuery.data?.groups?.[0];
5455

5556
const loading = groupQuery.isFetching && storageGroupData === undefined;
57+
const {appTitle} = useAppTitle();
5658

5759
const renderHelmet = () => {
5860
const pageTitle = groupId
@@ -61,8 +63,8 @@ export function StorageGroupPage() {
6163

6264
return (
6365
<Helmet
64-
titleTemplate={`%s - ${pageTitle}YDB Monitoring`}
65-
defaultTitle={`${pageTitle}YDB Monitoring`}
66+
titleTemplate={`%s - ${pageTitle}${appTitle}`}
67+
defaultTitle={`${pageTitle}${appTitle}`}
6668
/>
6769
);
6870
};

src/containers/Tenant/Tenant.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {cn} from '../../utils/cn';
1313
import {DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY} from '../../utils/constants';
1414
import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';
1515
import {isAccessError} from '../../utils/response';
16+
import {useAppTitle} from '../App/AppTitleContext';
1617

1718
import ObjectGeneral from './ObjectGeneral/ObjectGeneral';
1819
import {ObjectSummary} from './ObjectSummary/ObjectSummary';
@@ -116,11 +117,12 @@ export function Tenant(props: TenantProps) {
116117
}
117118

118119
const title = path || i18n('page.title');
120+
const {appTitle} = useAppTitle();
119121
return (
120122
<div className={b()}>
121123
<Helmet
122-
defaultTitle={`${title}YDB Monitoring`}
123-
titleTemplate={`%s — ${title}YDB Monitoring`}
124+
defaultTitle={`${title}${appTitle}`}
125+
titleTemplate={`%s — ${title}${appTitle}`}
124126
/>
125127
<LoaderWrapper loading={initialLoading}>
126128
<PageError error={showBlockingError ? error : undefined}>

src/containers/VDiskPage/VDiskPage.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {cn} from '../../utils/cn';
2626
import {getSeverityColor, getVDiskSlotBasedId} from '../../utils/disks/helpers';
2727
import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks';
2828
import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
29+
import {useAppTitle} from '../App/AppTitleContext';
2930
import {PaginatedStorage} from '../Storage/PaginatedStorage';
3031

3132
import {VDiskTablets} from './VDiskTablets';
@@ -144,6 +145,8 @@ export function VDiskPage() {
144145
);
145146
};
146147

148+
const {appTitle} = useAppTitle();
149+
147150
const renderHelmet = () => {
148151
const vDiskPagePart = vDiskSlotId
149152
? `${vDiskPageKeyset('vdisk')} ${vDiskSlotId}`
@@ -157,8 +160,8 @@ export function VDiskPage() {
157160

158161
return (
159162
<Helmet
160-
titleTemplate={`%s - ${vDiskPagePart} - ${pDiskPagePart}${nodePagePart}YDB Monitoring`}
161-
defaultTitle={`${vDiskPagePart} - ${pDiskPagePart}${nodePagePart}YDB Monitoring`}
163+
titleTemplate={`%s - ${vDiskPagePart} - ${pDiskPagePart}${nodePagePart}${appTitle}`}
164+
defaultTitle={`${vDiskPagePart} - ${pDiskPagePart}${nodePagePart}${appTitle}`}
162165
/>
163166
);
164167
};

0 commit comments

Comments
 (0)