Browse Source

SONAR-18128 SONAR-18358 SONAR-18368 Page titles do not identify purpose of pages

tags/10.0.0.68432
stanislavh 1 year ago
parent
commit
982ba29996

+ 8
- 4
server/sonar-web/src/main/js/app/components/AdminContainer.tsx View File

@@ -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}

+ 1
- 2
server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap View File

@@ -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={[]}

+ 3
- 1
server/sonar-web/src/main/js/app/utils/startReactApp.tsx View File

@@ -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()}


+ 1
- 1
server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts View File

@@ -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();
});

server/sonar-web/src/main/js/apps/coding-rules/components/App.tsx → server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx View File

@@ -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));

server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/App-test.tsx → server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CodingRulesApp-test.tsx View File

@@ -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,
})}

server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/App-test.tsx.snap → server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CodingRulesApp-test.tsx.snap View File


+ 2
- 2
server/sonar-web/src/main/js/apps/coding-rules/routes.tsx View File

@@ -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 />} />;

server/sonar-web/src/main/js/apps/component-measures/components/App.tsx → server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx View File

@@ -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);

server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx → server/sonar-web/src/main/js/apps/component-measures/components/__tests__/ComponentMeasuresApp-test.tsx View File

@@ -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()}

server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap → server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/ComponentMeasuresApp-test.tsx.snap View File

@@ -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"

+ 2
- 2
server/sonar-web/src/main/js/apps/component-measures/routes.tsx View File

@@ -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={

+ 12
- 1
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx View File

@@ -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>


+ 8
- 3
server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx View File

@@ -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" />


+ 9
- 1
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx View File

@@ -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}

+ 3
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -675,6 +675,8 @@ regulatory_page.select_branch=Select Branch
#
#------------------------------------------------------------------------------

page_title.template.default=%s - SonarQube
page_title.template.with_category=%s - {0} - SonarQube
overview.page=Overview
code.page=Code
permissions.page=Permissions
@@ -1656,7 +1658,7 @@ project.info.see_more_info_on_x_locs=See more information on your {0} lines of c
#------------------------------------------------------------------------------

quality_profiles.page_title_changelog_x={0} - Quality profile changelog
quality_profiles.page_title_compare_x={0} - Quality profile comparaison
quality_profiles.page_title_compare_x={0} - Quality profile comparison
quality_profiles.new_profile=New Quality Profile
quality_profiles.compare_with=Compare with
quality_profiles.filter_by=Filter profiles by

Loading…
Cancel
Save