@@ -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} |
@@ -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={[]} |
@@ -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()} | |||
@@ -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(); | |||
}); |
@@ -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)); |
@@ -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, | |||
})} |
@@ -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 />} />; |
@@ -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); |
@@ -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()} |
@@ -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" |
@@ -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={ |
@@ -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> | |||
@@ -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" /> | |||
@@ -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} |
@@ -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 |