diff options
author | stanislavh <stanislav.honcharov@sonarsource.com> | 2023-02-13 11:44:11 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-02-13 20:02:53 +0000 |
commit | 982ba2999674768245cc20150fc37c6259204b39 (patch) | |
tree | a5b8db35737f983b32549d437599f56a5e415273 /server/sonar-web | |
parent | 3dd53167b7bf957fac4c456a154d341980af923d (diff) | |
download | sonarqube-982ba2999674768245cc20150fc37c6259204b39.tar.gz sonarqube-982ba2999674768245cc20150fc37c6259204b39.zip |
SONAR-18128 SONAR-18358 SONAR-18368 Page titles do not identify purpose of pages
Diffstat (limited to 'server/sonar-web')
15 files changed, 77 insertions, 61 deletions
diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx index bcc5dc09267..82ab8d97641 100644 --- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx @@ -24,7 +24,7 @@ import { getSettingsNavigation } from '../../api/navigation'; import { getPendingPlugins } from '../../api/plugins'; import { getSystemStatus, waitSystemUPStatus } from '../../api/system'; import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization'; -import { translate } from '../../helpers/l10n'; +import { translate, translateWithParameters } from '../../helpers/l10n'; import { AdminPagesContext } from '../../types/admin'; import { AppState } from '../../types/appstate'; import { PendingPluginResult } from '../../types/plugins'; @@ -119,13 +119,17 @@ export class AdminContainer extends React.PureComponent<AdminContainerProps, Sta } const { pendingPlugins, systemStatus } = this.state; - const defaultTitle = translate('layout.settings'); - const adminPagesContext: AdminPagesContext = { adminPages }; return ( <div> - <Helmet defaultTitle={defaultTitle} defer={false} titleTemplate={`%s - ${defaultTitle}`} /> + <Helmet + defer={false} + titleTemplate={translateWithParameters( + 'page_title.template.with_category', + translate('layout.settings') + )} + /> <SettingsNav extensions={adminPages} fetchPendingPlugins={this.fetchPendingPlugins} diff --git a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap index 5b721b10020..6d04b34fa6d 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap @@ -3,11 +3,10 @@ exports[`should render correctly 1`] = ` <div> <Helmet - defaultTitle="layout.settings" defer={false} encodeSpecialCharacters={true} prioritizeSeoTags={false} - titleTemplate="%s - layout.settings" + titleTemplate="page_title.template.with_category.layout.settings" /> <WithLocation extensions={[]} diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx index c34ad1b5e8b..490a7f9043a 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { render } from 'react-dom'; -import { HelmetProvider } from 'react-helmet-async'; +import { Helmet, HelmetProvider } from 'react-helmet-async'; import { IntlProvider } from 'react-intl'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import accountRoutes from '../../apps/account/routes'; @@ -57,6 +57,7 @@ import tutorialsRoutes from '../../apps/tutorials/routes'; import usersRoutes from '../../apps/users/routes'; import webAPIRoutes from '../../apps/web-api/routes'; import webhooksRoutes from '../../apps/webhooks/routes'; +import { translate } from '../../helpers/l10n'; import { getBaseUrl } from '../../helpers/system'; import { AppState } from '../../types/appstate'; import { Feature } from '../../types/features'; @@ -185,6 +186,7 @@ export default function startReactApp( <IntlProvider defaultLocale={lang} locale={lang}> <GlobalMessagesContainer /> <BrowserRouter basename={getBaseUrl()}> + <Helmet titleTemplate={translate('page_title.template.default')} /> <Routes> {renderRedirects()} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts index 00fe9b0ff43..9750dfc6762 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts @@ -75,7 +75,7 @@ it('should show open rule with default description section', async () => { expect( await screen.findByRole('heading', { level: 1, name: 'Awsome java rule' }) ).toBeInTheDocument(); - expect(document.title).toEqual('coding_rule.page.Java.Awsome java rule'); + expect(document.title).toEqual('page_title.template.with_category.coding_rules.page'); expect(screen.getByText('Why')).toBeInTheDocument(); expect(screen.getByText('Because')).toBeInTheDocument(); }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/App.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx index c69234ee29c..79dcc48d547 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx @@ -96,7 +96,7 @@ interface State { rules: Rule[]; } -export class App extends React.PureComponent<Props, State> { +export class CodingRulesApp extends React.PureComponent<Props, State> { mounted = false; constructor(props: Props) { @@ -562,16 +562,20 @@ export class App extends React.PureComponent<Props, State> { return ( <> <Suggestions suggestions="coding_rules" /> - <Helmet - defer={false} - title={ - openRule - ? translateWithParameters('coding_rule.page', openRule.langName, openRule.name) - : translate('coding_rules.page') - } - > - <meta content="noindex" name="robots" /> - </Helmet> + {openRule ? ( + <Helmet + defer={false} + title={translateWithParameters('coding_rule.page', openRule.langName, openRule.name)} + titleTemplate={translateWithParameters( + 'page_title.template.with_category', + translate('coding_rules.page') + )} + /> + ) : ( + <Helmet defer={false} title={translate('coding_rules.page')}> + <meta content="noindex" name="robots" /> + </Helmet> + )} <div className="layout-page" id="coding-rules-page"> <ScreenPositionHelper className="layout-page-side-outer"> {({ top }) => ( @@ -715,4 +719,4 @@ function parseFacets(rawFacets: { property: string; values: { count: number; val return facets; } -export default withRouter(withCurrentUserContext(App)); +export default withRouter(withCurrentUserContext(CodingRulesApp)); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CodingRulesApp-test.tsx index aa18b5cba52..09ccc3496a5 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CodingRulesApp-test.tsx @@ -29,7 +29,7 @@ import { mockRouter, } from '../../../../helpers/testMocks'; import { waitAndUpdate } from '../../../../helpers/testUtils'; -import { App } from '../App'; +import { CodingRulesApp } from '../CodingRulesApp'; jest.mock('../../../../components/common/ScreenPositionHelper'); @@ -105,9 +105,9 @@ describe('renderBulkButton', () => { }); }); -function shallowRender(props: Partial<App['props']> = {}) { - return shallow<App>( - <App +function shallowRender(props: Partial<CodingRulesApp['props']> = {}) { + return shallow<CodingRulesApp>( + <CodingRulesApp currentUser={mockCurrentUser({ isLoggedIn: true, })} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CodingRulesApp-test.tsx.snap index 08da9918375..08da9918375 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CodingRulesApp-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/coding-rules/routes.tsx b/server/sonar-web/src/main/js/apps/coding-rules/routes.tsx index 2e9bb7a9355..5ba7f224b13 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/routes.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/routes.tsx @@ -20,7 +20,7 @@ import React, { useEffect } from 'react'; import { Route, useLocation, useNavigate } from 'react-router-dom'; import { RawQuery } from '../../types/types'; -import App from './components/App'; +import CodingRulesApp from './components/CodingRulesApp'; import { parseQuery, serializeQuery } from './query'; const EXPECTED_SPLIT_PARTS = 2; @@ -56,7 +56,7 @@ function HashEditWrapper() { } }, [location, navigate]); - return <App />; + return <CodingRulesApp />; } const routes = () => <Route path="coding_rules" element={<HashEditWrapper />} />; diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx index 922fa2d0906..b5d7f9da79c 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx @@ -33,11 +33,7 @@ import { enhanceMeasure } from '../../../components/measure/utils'; import '../../../components/search-navigator.css'; import { Alert } from '../../../components/ui/Alert'; import { getBranchLikeQuery, isPullRequest, isSameBranchLike } from '../../../helpers/branch-like'; -import { - getLocalizedMetricDomain, - translate, - translateWithParameters, -} from '../../../helpers/l10n'; +import { translate } from '../../../helpers/l10n'; import { addSideBarClass, addWhitePageClass, @@ -64,7 +60,6 @@ import { hasFullMeasures, hasTree, hasTreemap, - isProjectOverview, parseQuery, Query, serializeQuery, @@ -89,7 +84,7 @@ interface State { metrics: Dict<Metric>; } -export class App extends React.PureComponent<Props, State> { +export class ComponentMeasuresApp extends React.PureComponent<Props, State> { mounted = false; state: State; @@ -174,18 +169,6 @@ export class App extends React.PureComponent<Props, State> { ); } - getHelmetTitle = (query: Query, displayOverview: boolean, metric?: Metric) => { - if (displayOverview && query.metric) { - return isProjectOverview(query.metric) - ? translate('component_measures.overview.project_overview.facet') - : translateWithParameters( - 'component_measures.domain_x_overview', - getLocalizedMetricDomain(query.metric) - ); - } - return metric ? metric.name : translate('layout.measures'); - }; - getSelectedMetric = (query: Query, displayOverview: boolean) => { if (displayOverview) { return undefined; @@ -310,7 +293,7 @@ export class App extends React.PureComponent<Props, State> { return ( <div id="component-measures"> <Suggestions suggestions="component_measures" /> - <Helmet defer={false} title={this.getHelmetTitle(query, displayOverview, metric)} /> + <Helmet defer={false} title={translate('layout.measures')} /> {measures.length > 0 ? ( <div className="layout-page"> <ScreenPositionHelper className="layout-page-side-outer"> @@ -368,7 +351,7 @@ const AlertContent = styled.div` * is that we can't use the usual withComponentContext HOC, because the type * of `component` isn't the same. It probably used to work because of the lazy loading */ -const WrappedApp = withRouter(withBranchStatusActions(App)); +const WrappedApp = withRouter(withBranchStatusActions(ComponentMeasuresApp)); function AppWithComponentContext() { const { branchLike, component } = React.useContext(ComponentContext); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/ComponentMeasuresApp-test.tsx index 373b501acf9..f7751cebc97 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/ComponentMeasuresApp-test.tsx @@ -27,7 +27,7 @@ import { mockComponent } from '../../../../helpers/mocks/component'; import { mockIssue, mockLocation, mockRouter } from '../../../../helpers/testMocks'; import { waitAndUpdate } from '../../../../helpers/testUtils'; import { ComponentQualifier } from '../../../../types/component'; -import { App } from '../App'; +import { ComponentMeasuresApp } from '../ComponentMeasuresApp'; jest.mock('../../../../api/metrics', () => ({ getAllMetrics: jest.fn().mockResolvedValue([ @@ -151,9 +151,9 @@ it.each([ } ); -function shallowRender(props: Partial<App['props']> = {}) { - return shallow<App>( - <App +function shallowRender(props: Partial<ComponentMeasuresApp['props']> = {}) { + return shallow<ComponentMeasuresApp>( + <ComponentMeasuresApp branchLike={mockMainBranch()} component={mockComponent({ key: 'foo', name: 'Foo' })} fetchBranchStatus={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/ComponentMeasuresApp-test.tsx.snap index 523e596b871..671b12b6fdf 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/ComponentMeasuresApp-test.tsx.snap @@ -11,7 +11,7 @@ exports[`should not render drilldown for estimated duplications 1`] = ` defer={false} encodeSpecialCharacters={true} prioritizeSeoTags={false} - title="Coverage" + title="layout.measures" /> <div className="layout-page" @@ -49,7 +49,7 @@ exports[`should render a message when there are no measures 1`] = ` defer={false} encodeSpecialCharacters={true} prioritizeSeoTags={false} - title="Coverage" + title="layout.measures" /> <MeasuresEmpty /> </div> @@ -123,7 +123,7 @@ exports[`should render correctly 1`] = ` defer={false} encodeSpecialCharacters={true} prioritizeSeoTags={false} - title="Coverage" + title="layout.measures" /> <div className="layout-page" diff --git a/server/sonar-web/src/main/js/apps/component-measures/routes.tsx b/server/sonar-web/src/main/js/apps/component-measures/routes.tsx index edee717bbb5..4e1e0411f4d 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/routes.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/routes.tsx @@ -22,11 +22,11 @@ import { Navigate, Route, useParams, useSearchParams } from 'react-router-dom'; import NavigateWithParams from '../../app/utils/NavigateWithParams'; import { omitNil } from '../../helpers/request'; import { searchParamsToQuery } from '../../helpers/urls'; -import App from './components/App'; +import ComponentMeasuresApp from './components/ComponentMeasuresApp'; const routes = () => ( <Route path="component_measures"> - <Route index={true} element={<App />} /> + <Route index={true} element={<ComponentMeasuresApp />} /> <Route path="domain/:domainName" element={ diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx index 4a3ee0c5980..35c82364238 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx @@ -1164,7 +1164,18 @@ export class App extends React.PureComponent<Props, State> { id="issues-page" > <Suggestions suggestions="issues" /> - <Helmet defer={false} title={openIssue ? openIssue.message : translate('issues.page')} /> + {openIssue ? ( + <Helmet + defer={false} + title={openIssue.message} + titleTemplate={translateWithParameters( + 'page_title.template.with_category', + translate('issues.page') + )} + /> + ) : ( + <Helmet defer={false} title={translate('issues.page')} /> + )} <h1 className="a11y-hidden">{translate('issues.page')}</h1> diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx index 87cb5a6097c..a0ce8d48ab9 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx @@ -25,7 +25,7 @@ import ScreenPositionHelper from '../../../components/common/ScreenPositionHelpe import Suggestions from '../../../components/embed-docs-modal/Suggestions'; import '../../../components/search-navigator.css'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; -import { translate } from '../../../helpers/l10n'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; import { addSideBarClass, addWhitePageClass, @@ -113,11 +113,16 @@ class App extends React.PureComponent<Props, State> { render() { const { name } = this.props; const { canCreate, qualityGates } = this.state; - const defaultTitle = translate('quality_gates.page'); return ( <> - <Helmet defaultTitle={defaultTitle} defer={false} titleTemplate={`%s - ${defaultTitle}`} /> + <Helmet + defer={false} + titleTemplate={translateWithParameters( + 'page_title.template.with_category', + translate('quality_gates.page') + )} + /> <div className="layout-page" id="quality-gates-page"> <Suggestions suggestions="quality_gates" /> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx index 5588a4d15b9..77ac4b8d845 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import { Helmet } from 'react-helmet-async'; import { Outlet, useSearchParams } from 'react-router-dom'; import { useLocation } from '../../../components/hoc/withRouter'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; import ProfileHeader from '../details/ProfileHeader'; import { useQualityProfilesContext } from '../qualityProfilesContext'; import ProfileNotFound from './ProfileNotFound'; @@ -58,7 +59,14 @@ export default function ProfileContainer() { return ( <div id="quality-profile"> - <Helmet defer={false} title={profile.name} /> + <Helmet + defer={false} + title={profile.name} + titleTemplate={translateWithParameters( + 'page_title.template.with_category', + translate('quality_profiles.page') + )} + /> <ProfileHeader profile={profile} isComparable={filteredProfiles.length > 1} |