import { Helmet } from 'react-helmet-async';
import { useLocation } from 'react-router-dom';
import Link from '../../components/common/Link';
-import { getUrlForDoc } from '../../helpers/docs';
-import withAppStateContext, { WithAppStateContextProps } from './app-state/withAppStateContext';
+import { useDocUrl } from '../../helpers/docs';
const PAUSE_REDIRECT = 1;
-function DocumentationRedirect({ appState }: WithAppStateContextProps) {
+export default function DocumentationRedirect() {
const location = useLocation();
- const url = getUrlForDoc(appState.version, location.pathname.replace(/^\/documentation/, ''));
+ const url = useDocUrl(location.pathname.replace(/^\/documentation/, ''));
return (
<>
</>
);
}
-
-export default withAppStateContext(DocumentationRedirect);
import DocumentationRedirect from '../DocumentationRedirect';
it('should redirect to static doc for specific version', async () => {
- renderDocumentationRedirect('land', '9.7.1234');
+ renderDocumentationRedirect('land', '10.0');
expect(await screen.findByRole('link')).toHaveAttribute(
'href',
- 'https://docs.sonarqube.org/9.7/land'
+ 'https://docs.sonarqube.org/10.0/land'
);
});
it('should redirect to static doc for latest version', async () => {
- renderDocumentationRedirect('land', '9.7-SNAPSHOT');
+ renderDocumentationRedirect('land', '10.0-SNAPSHOT');
expect(await screen.findByRole('link')).toHaveAttribute(
'href',
);
});
-function renderDocumentationRedirect(navigatge: string, version?: string) {
+function renderDocumentationRedirect(navigate: string, version?: string) {
renderAppRoutes(
- `documentation/${navigatge}`,
+ `documentation/${navigate}`,
() => <Route path="/documentation/*" element={<DocumentationRedirect />} />,
{ appState: mockAppState({ version }) }
);
<li
className="page-footer-menu-item"
>
- <withAppStateContext(DocLink)
+ <DocLink
to="/"
>
footer.documentation
- </withAppStateContext(DocLink)>
+ </DocLink>
</li>
<li
className="page-footer-menu-item"
>
- <withAppStateContext(DocLink)
+ <DocLink
to="/instance-administration/plugin-version-matrix/"
>
footer.plugins
- </withAppStateContext(DocLink)>
+ </DocLink>
</li>
<li
className="page-footer-menu-item"
<li
className="page-footer-menu-item"
>
- <withAppStateContext(DocLink)
+ <DocLink
to="/"
>
footer.documentation
- </withAppStateContext(DocLink)>
+ </DocLink>
</li>
<li
className="page-footer-menu-item"
>
- <withAppStateContext(DocLink)
+ <DocLink
to="/instance-administration/plugin-version-matrix/"
>
footer.plugins
- </withAppStateContext(DocLink)>
+ </DocLink>
</li>
</ul>
</div>
<li
className="page-footer-menu-item"
>
- <withAppStateContext(DocLink)
+ <DocLink
to="/"
>
footer.documentation
- </withAppStateContext(DocLink)>
+ </DocLink>
</li>
<li
className="page-footer-menu-item"
>
- <withAppStateContext(DocLink)
+ <DocLink
to="/instance-administration/plugin-version-matrix/"
>
footer.plugins
- </withAppStateContext(DocLink)>
+ </DocLink>
</li>
<li
className="page-footer-menu-item"
qualifiers: [],
settings: {},
version: '',
+ documentationUrl: 'https://docs.sonarqube.org/latest',
};
export const AppStateContext = React.createContext<AppState>(DEFAULT_APP_STATE);
<AppStateContextProvider
appState={
{
+ "documentationUrl": "https://docs.sonarqube.org/10.0",
"edition": "community",
"productionDatabase": true,
"qualifiers": [
<Extension
appState={
{
+ "documentationUrl": "https://docs.sonarqube.org/10.0",
"edition": "community",
"productionDatabase": true,
"qualifiers": [
<Extension
appState={
{
+ "documentationUrl": "https://docs.sonarqube.org/10.0",
"edition": "community",
"productionDatabase": true,
"qualifiers": [
<IndexationContextProvider
appState={
{
+ "documentationUrl": "https://docs.sonarqube.org/10.0",
"edition": "community",
"needIssueSync": true,
"productionDatabase": true,
import { Helmet } from 'react-helmet-async';
import { FormattedMessage } from 'react-intl';
import { getMigrationStatus, getSystemStatus, migrateDatabase } from '../../../api/system';
+import DocLink from '../../../components/common/DocLink';
import InstanceMessage from '../../../components/common/InstanceMessage';
import Link from '../../../components/common/Link';
import { Button } from '../../../components/controls/buttons';
id="maintenance.sonarqube_is_under_maintenance.2"
values={{
link: (
- <Link
- // We cannot use <DocLink> here, as it relies on AppState. However, the maintenance
- // app is a special app that can run in a "downgraded" environment, where the AppState
- // may not yet be fully loaded. Hence, we link to this documentation page directly.
- to="https://docs.sonarqube.org/latest/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
- target="_blank"
- >
+ <DocLink to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/">
{translate('maintenance.sonarqube_is_under_maintenance_link.2')}
- </Link>
+ </DocLink>
),
}}
/>
id="marketplace.page.plugins.description2"
values={
{
- "link": <withAppStateContext(DocLink)
+ "link": <DocLink
to="/instance-administration/marketplace/"
>
marketplace.page.plugins.description2.link
- </withAppStateContext(DocLink)>,
+ </DocLink>,
}
}
/>
id="marketplace.page.plugins.description2"
values={
{
- "link": <withAppStateContext(DocLink)
+ "link": <DocLink
to="/instance-administration/marketplace/"
>
marketplace.page.plugins.description2.link
- </withAppStateContext(DocLink)>,
+ </DocLink>,
}
}
/>
*/
import { Card, FlagMessage, Link } from 'design-system';
import * as React from 'react';
-import withAppStateContext, {
- WithAppStateContextProps,
-} from '../../../app/components/app-state/withAppStateContext';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
-import { getUrlForDoc } from '../../../helpers/docs';
+import { useDocUrl } from '../../../helpers/docs';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getProjectQueryUrl } from '../../../helpers/urls';
import { QualityGateStatus } from '../../../types/quality-gates';
caycStatus: CaycStatus;
}
-function ApplicationNonCaycProjectWarning({
- projects,
- caycStatus,
- appState,
-}: Props & WithAppStateContextProps) {
- const caycUrl = getUrlForDoc(appState.version, '/user-guide/clean-as-you-code/');
- const caycDrawbacksUrl = getUrlForDoc(
- appState.version,
- '/user-guide/clean-as-you-code/#potential-drawbacks'
- );
+export default function ApplicationNonCaycProjectWarning({ projects, caycStatus }: Props) {
+ const caycUrl = useDocUrl('/user-guide/clean-as-you-code/');
+ const caycDrawbacksUrl = useDocUrl('/user-guide/clean-as-you-code/#potential-drawbacks');
return (
<Card className="sw-mt-4 sw-body-sm">
</Card>
);
}
-
-export default withAppStateContext(ApplicationNonCaycProjectWarning);
import { DiscreetLink, FlagMessage, Link } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import withAppStateContext, {
- WithAppStateContextProps,
-} from '../../../app/components/app-state/withAppStateContext';
-import { getUrlForDoc } from '../../../helpers/docs';
+import { useDocUrl } from '../../../helpers/docs';
import { translate } from '../../../helpers/l10n';
import { getQualityGateUrl } from '../../../helpers/urls';
import { Component } from '../../../types/types';
component: Pick<Component, 'key' | 'qualifier' | 'qualityGate'>;
}
-function CleanAsYouCodeWarning({ component, appState }: Props & WithAppStateContextProps) {
- const caycUrl = getUrlForDoc(appState.version, '/user-guide/clean-as-you-code/');
+export default function CleanAsYouCodeWarning({ component }: Props) {
+ const caycUrl = useDocUrl('/user-guide/clean-as-you-code/');
return (
<>
</>
);
}
-
-export default withAppStateContext(CleanAsYouCodeWarning);
import { DiscreetLink, Link } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import withAppStateContext, {
- WithAppStateContextProps,
-} from '../../../app/components/app-state/withAppStateContext';
-import { getUrlForDoc } from '../../../helpers/docs';
+import { useDocUrl } from '../../../helpers/docs';
import { translate } from '../../../helpers/l10n';
import { getQualityGateUrl } from '../../../helpers/urls';
import { Component } from '../../../types/types';
component: Pick<Component, 'key' | 'qualifier' | 'qualityGate'>;
}
-function CleanAsYouCodeWarningOverCompliant({
- component,
- appState,
-}: Props & WithAppStateContextProps) {
- const caycDrawbackUrl = getUrlForDoc(
- appState.version,
- '/user-guide/clean-as-you-code/#potential-drawbacks'
- );
+export default function CleanAsYouCodeWarningOverCompliant({ component }: Props) {
+ const caycDrawbackUrl = useDocUrl('/user-guide/clean-as-you-code/#potential-drawbacks');
return (
<>
</>
);
}
-
-export default withAppStateContext(CleanAsYouCodeWarningOverCompliant);
quality_profiles.intro1
<br />
quality_profiles.intro2
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/instance-administration/quality-profiles/"
>
learn_more
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</header>
`;
quality_profiles.intro1
<br />
quality_profiles.intro2
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/instance-administration/quality-profiles/"
>
learn_more
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</header>
`;
quality_profiles.intro1
<br />
quality_profiles.intro2
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/instance-administration/quality-profiles/"
>
learn_more
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</header>
`;
quality_profiles.intro1
<br />
quality_profiles.intro2
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/instance-administration/quality-profiles/"
>
learn_more
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
<CreateProfileForm
languages={
quality_profiles.intro1
<br />
quality_profiles.intro2
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/instance-administration/quality-profiles/"
>
learn_more
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
<RestoreProfileForm
onClose={[Function]}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { render, screen } from '@testing-library/react';
+import { screen } from '@testing-library/react';
import * as React from 'react';
-import { IntlProvider } from 'react-intl';
import { deactivateUser } from '../../../../api/users';
import { mockUser } from '../../../../helpers/testMocks';
+import { renderComponent } from '../../../../helpers/testReactTestingUtils';
import { UserActive } from '../../../../types/users';
import DeactivateForm from '../DeactivateForm';
});
function renderDeactivateForm(user: UserActive) {
- return render(
- <IntlProvider defaultLocale="en" locale="en">
- <DeactivateForm onClose={jest.fn()} onUpdateUsers={jest.fn()} user={user} />
- </IntlProvider>
+ return renderComponent(
+ <DeactivateForm onClose={jest.fn()} onUpdateUsers={jest.fn()} user={user} />
);
}
id="webhooks.description"
values={
{
- "url": <withAppStateContext(DocLink)
+ "url": <DocLink
to="/project-administration/webhooks/"
>
webhooks.documentation_link
- </withAppStateContext(DocLink)>,
+ </DocLink>,
}
}
/>
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import withAppStateContext, {
- WithAppStateContextProps,
-} from '../../app/components/app-state/withAppStateContext';
-import { getUrlForDoc } from '../../helpers/docs';
+import { useDocUrl } from '../../helpers/docs';
import Link, { LinkProps } from './Link';
-type Props = WithAppStateContextProps &
- Omit<LinkProps, 'to'> & { to: string; innerRef?: React.Ref<HTMLAnchorElement> };
+type Props = Omit<LinkProps, 'to'> & { to: string; innerRef?: React.Ref<HTMLAnchorElement> };
-export function DocLink({ appState, to, innerRef, ...props }: Props) {
- const toStatic = getUrlForDoc(appState.version, to);
+export default function DocLink({ to, innerRef, ...props }: Props) {
+ const toStatic = useDocUrl(to);
return <Link ref={innerRef} to={toStatic} target="_blank" {...props} />;
}
-
-export default withAppStateContext(DocLink);
import { ItemLink, OpenNewTabIcon } from 'design-system';
import * as React from 'react';
-import { AppStateContext } from '../../app/components/app-state/AppStateContext';
-
-import { getUrlForDoc } from '../../helpers/docs';
+import { useDocUrl } from '../../helpers/docs';
interface Props {
to: string;
}
export function DocItemLink({ to, innerRef, children }: Props) {
- const { version } = React.useContext(AppStateContext);
-
- const toStatic = getUrlForDoc(version, to);
+ const toStatic = useDocUrl(to);
return (
<ItemLink innerRef={innerRef} to={toStatic}>
>
system.download_x.5.6.7
</a>
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
>
system.how_to_upgrade
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</div>
`;
>
system.download_x.5.6.7
</a>
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
>
system.how_to_upgrade
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</div>
`;
>
system.download_x.5.6.7
</a>
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
>
system.how_to_upgrade
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</div>
`;
>
system.download_x.5.6.7
</a>
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
>
system.how_to_upgrade
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</div>
`;
>
system.download_x.5.6.7
</a>
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
>
system.how_to_upgrade
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</div>
`;
>
system.download_x.5.6.7
</a>
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
>
system.how_to_upgrade
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</div>
`;
>
system.download_x.5.6.7
</a>
- <withAppStateContext(DocLink)
+ <DocLink
className="spacer-left"
to="/setup-and-upgrade/upgrade-the-server/upgrade-guide/"
>
system.how_to_upgrade
- </withAppStateContext(DocLink)>
+ </DocLink>
</div>
</div>
`;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-const VERSION_PARSER = /^(\d+\.\d+).*$/;
+import React from 'react';
+import { AppStateContext } from '../app/components/app-state/AppStateContext';
-export function getUrlForDoc(version: string, to: string) {
- const versionPrefix = VERSION_PARSER.exec(version);
+export function getUrlForDoc(url: string, version: string, to: string) {
const isSnapshot = version.indexOf('SNAPSHOT') !== -1;
- const docPrefix =
- versionPrefix && versionPrefix.length === 2 && !isSnapshot ? versionPrefix[1] : 'latest';
+ const path = to.replace(/^\//, '');
- return `https://docs.sonarqube.org/${docPrefix}${to}`;
+ return isSnapshot
+ ? `${url.replace(url.slice(url.lastIndexOf('/')), '/latest')}/${path}`
+ : `${url}/${path}`;
+}
+
+export function useDocUrl(to: string) {
+ const { version, documentationUrl } = React.useContext(AppStateContext);
+
+ return getUrlForDoc(documentationUrl, version, to);
}
qualifiers: ['TRK'],
settings: {},
version: '1.0',
+ documentationUrl: 'https://docs.sonarqube.org/10.0',
...overrides,
};
}
);
}
-export function renderComponent(component: React.ReactElement, pathname = '/') {
+export function renderComponent(
+ component: React.ReactElement,
+ pathname = '/',
+ { appState = mockAppState() }: RenderContext = {}
+) {
function Wrapper({ children }: { children: React.ReactElement }) {
return (
<IntlProvider defaultLocale="en" locale="en">
<HelmetProvider>
- <MemoryRouter initialEntries={[pathname]}>
- <Routes>
- <Route path="*" element={children} />
- </Routes>
- </MemoryRouter>
+ <AppStateContextProvider appState={appState}>
+ <MemoryRouter initialEntries={[pathname]}>
+ <Routes>
+ <Route path="*" element={children} />
+ </Routes>
+ </MemoryRouter>
+ </AppStateContextProvider>
</HelmetProvider>
</IntlProvider>
);
standalone?: boolean;
version: string;
webAnalyticsJsPath?: string;
+ documentationUrl: string;
}