aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIsmail Cherri <ismail.cherri@sonarsource.com>2024-12-02 18:17:25 +0100
committersonartech <sonartech@sonarsource.com>2024-12-04 20:03:23 +0000
commit82363aafd35b4c2569d2dd4136630a2700ee2010 (patch)
tree9e2ffba352f8f77577d4214c66a181e5c4990c37
parentac98556dca2639470bc730a72707c28255fbdb49 (diff)
downloadsonarqube-82363aafd35b4c2569d2dd4136630a2700ee2010.tar.gz
sonarqube-82363aafd35b4c2569d2dd4136630a2700ee2010.zip
SONAR-23656 Update footer to reflect instance mode
-rw-r--r--server/sonar-web/src/main/js/app/components/GlobalFooter.tsx68
-rw-r--r--server/sonar-web/src/main/js/app/components/GlobalFooterBranding.tsx62
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx18
-rw-r--r--server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx4
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties7
6 files changed, 116 insertions, 45 deletions
diff --git a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
index 06496f14848..e1c14d66f7f 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
+++ b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
@@ -21,12 +21,20 @@
import styled from '@emotion/styled';
import { LinkHighlight, LinkStandalone } from '@sonarsource/echoes-react';
import { useIntl } from 'react-intl';
-import { FlagMessage, LAYOUT_VIEWPORT_MIN_WIDTH, themeBorder, themeColor } from '~design-system';
+import {
+ FlagMessage,
+ LAYOUT_VIEWPORT_MIN_WIDTH,
+ SeparatorCircleIcon,
+ themeBorder,
+ themeColor,
+} from '~design-system';
import InstanceMessage from '../../components/common/InstanceMessage';
import AppVersionStatus from '../../components/shared/AppVersionStatus';
import { COMMUNITY_FORUM_URL, DocLink } from '../../helpers/doc-links';
import { useDocUrl } from '../../helpers/docs';
import { getEdition } from '../../helpers/editions';
+import { getInstanceVersionNumber } from '../../helpers/strings';
+import { useStandardExperienceModeQuery } from '../../queries/mode';
import { EditionKey } from '../../types/editions';
import GlobalFooterBranding from './GlobalFooterBranding';
import { useAppState } from './app-state/withAppStateContext';
@@ -37,8 +45,10 @@ interface GlobalFooterProps {
export default function GlobalFooter({ hideLoggedInInfo }: Readonly<GlobalFooterProps>) {
const appState = useAppState();
+ const { data: isStandardMode } = useStandardExperienceModeQuery();
const currentEdition = appState?.edition && getEdition(appState.edition);
const intl = useIntl();
+ const version = getInstanceVersionNumber(appState.version);
const docUrl = useDocUrl();
@@ -46,7 +56,7 @@ export default function GlobalFooter({ hideLoggedInInfo }: Readonly<GlobalFooter
return (
<StyledFooter className="sw-p-6" id="footer">
- <div className="sw-typo-default sw-h-full sw-flex sw-flex-col sw-items-stretch">
+ <div className="sw-h-full sw-flex sw-flex-col sw-items-stretch">
{appState?.productionDatabase === false && (
<FlagMessage className="sw-mb-4" id="evaluation_warning" variant="warning">
<p>
@@ -63,21 +73,45 @@ export default function GlobalFooter({ hideLoggedInInfo }: Readonly<GlobalFooter
</FlagMessage>
)}
- <div className="sw-flex sw-justify-between sw-items-center">
+ <div className="sw-text-xs sw-flex sw-justify-between sw-items-center">
<GlobalFooterBranding />
- <ul className="sw-flex sw-items-center sw-gap-3 sw-ml-4">
- {!hideLoggedInInfo && currentEdition && <li>{currentEdition.name}</li>}
+ {!hideLoggedInInfo && (
+ <ul className="sw-code sw-flex sw-items-center sw-gap-1">
+ {currentEdition && (
+ <>
+ <li>{currentEdition.name}</li>
+ <SeparatorCircleIcon aria-hidden as="li" />
+ </>
+ )}
- {!hideLoggedInInfo && appState?.version && (
- <li className="sw-code">
- <AppVersionStatus />
- </li>
- )}
+ {appState?.version && (
+ <>
+ <li>{intl.formatMessage({ id: 'footer.version.short' }, { version })}</li>
+ <SeparatorCircleIcon aria-hidden as="li" />
+ <li>
+ <AppVersionStatus statusOnly />
+ </li>
+ </>
+ )}
+ {isStandardMode !== undefined && (
+ <>
+ <SeparatorCircleIcon aria-hidden as="li" />
+ <li className="sw-uppercase">
+ {intl.formatMessage({
+ id: `footer.mode.${isStandardMode ? 'STANDARD' : 'MQR'}`,
+ })}
+ </li>
+ </>
+ )}
+ </ul>
+ )}
+ <ul className="sw-flex sw-items-center sw-gap-3">
<li>
{isCommunityBuildRunning ? (
<LinkStandalone
+ shouldOpenInNewTab
highlight={LinkHighlight.CurrentColor}
to="https://www.gnu.org/licenses/lgpl-3.0.txt"
>
@@ -85,6 +119,7 @@ export default function GlobalFooter({ hideLoggedInInfo }: Readonly<GlobalFooter
</LinkStandalone>
) : (
<LinkStandalone
+ shouldOpenInNewTab
highlight={LinkHighlight.CurrentColor}
to="https://www.sonarsource.com/legal/sonarqube/terms-and-conditions/"
>
@@ -94,19 +129,28 @@ export default function GlobalFooter({ hideLoggedInInfo }: Readonly<GlobalFooter
</li>
<li>
- <LinkStandalone highlight={LinkHighlight.CurrentColor} to={COMMUNITY_FORUM_URL}>
+ <LinkStandalone
+ shouldOpenInNewTab
+ highlight={LinkHighlight.CurrentColor}
+ to={COMMUNITY_FORUM_URL}
+ >
{intl.formatMessage({ id: 'footer.community' })}
</LinkStandalone>
</li>
<li>
- <LinkStandalone highlight={LinkHighlight.CurrentColor} to={docUrl(DocLink.Root)}>
+ <LinkStandalone
+ shouldOpenInNewTab
+ highlight={LinkHighlight.CurrentColor}
+ to={docUrl(DocLink.Root)}
+ >
{intl.formatMessage({ id: 'footer.documentation' })}
</LinkStandalone>
</li>
<li>
<LinkStandalone
+ shouldOpenInNewTab
highlight={LinkHighlight.CurrentColor}
to={docUrl(DocLink.InstanceAdminPluginVersionMatrix)}
>
diff --git a/server/sonar-web/src/main/js/app/components/GlobalFooterBranding.tsx b/server/sonar-web/src/main/js/app/components/GlobalFooterBranding.tsx
index 27a6e7a2ea4..2f82db1f585 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalFooterBranding.tsx
+++ b/server/sonar-web/src/main/js/app/components/GlobalFooterBranding.tsx
@@ -24,32 +24,42 @@ import { isOfficial } from '../../helpers/system';
export default function GlobalFooterBranding() {
const official = isOfficial();
- return official ? (
- <div>
- SonarQube&trade; technology is powered by{' '}
- <Link highlight={LinkHighlight.CurrentColor} to="https://www.sonarsource.com">
- SonarSource SA
- </Link>
- </div>
- ) : (
- <div>
- This application is based on{' '}
- <Link
- highlight={LinkHighlight.CurrentColor}
- to="https://www.sonarsource.com/products/sonarqube/?referrer=sonarqube"
- title="SonarQube™"
- >
- SonarQube™
- </Link>{' '}
- but is <strong>not</strong> an official version provided by{' '}
- <Link
- highlight={LinkHighlight.CurrentColor}
- to="https://www.sonarsource.com"
- title="SonarSource SA"
- >
- SonarSource SA
- </Link>
- .
+ return (
+ <div className="max-[1400px]:sw-max-w-[12rem] sw-flex sw-items-center">
+ {official ? (
+ <span>
+ SonarQube&trade; technology is powered by{' '}
+ <Link
+ shouldOpenInNewTab
+ highlight={LinkHighlight.CurrentColor}
+ to="https://www.sonarsource.com"
+ >
+ SonarSource SA
+ </Link>
+ </span>
+ ) : (
+ <span>
+ This application is based on{' '}
+ <Link
+ shouldOpenInNewTab
+ highlight={LinkHighlight.CurrentColor}
+ to="https://www.sonarsource.com/products/sonarqube/?referrer=sonarqube"
+ title="SonarQube™"
+ >
+ SonarQube™
+ </Link>{' '}
+ but is <strong>not</strong> an official version provided by{' '}
+ <Link
+ shouldOpenInNewTab
+ highlight={LinkHighlight.CurrentColor}
+ to="https://www.sonarsource.com"
+ title="SonarSource SA"
+ >
+ SonarSource SA
+ </Link>
+ .
+ </span>
+ )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
index 004b857d034..a00ba6c3d13 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
@@ -20,6 +20,7 @@
import { addDays, subDays } from 'date-fns';
import { byRole, byText } from '~sonar-aligned/helpers/testSelector';
+import { ModeServiceMock } from '../../../api/mocks/ModeServiceMock';
import SystemServiceMock from '../../../api/mocks/SystemServiceMock';
import { getEdition } from '../../../helpers/editions';
import { mockAppState } from '../../../helpers/testMocks';
@@ -27,13 +28,16 @@ import { renderComponent } from '../../../helpers/testReactTestingUtils';
import { AppState } from '../../../types/appstate';
import { EditionKey } from '../../../types/editions';
import { FCProps } from '../../../types/misc';
+import { Mode } from '../../../types/mode';
import GlobalFooter from '../GlobalFooter';
const systemMock = new SystemServiceMock();
+const modeHandler = new ModeServiceMock();
const COMMUNITY = getEdition(EditionKey.community).name;
afterEach(() => {
+ modeHandler.reset();
systemMock.reset();
});
@@ -42,7 +46,7 @@ it('should render the logged-in information', async () => {
expect(ui.databaseWarningMessage.query()).not.toBeInTheDocument();
- expect(ui.footerListItems.getAll()).toHaveLength(7);
+ expect(ui.footerListItems.getAll()).toHaveLength(8);
expect(byText(COMMUNITY).get()).toBeInTheDocument();
expect(await ui.versionLabel('4.2').find()).toBeInTheDocument();
@@ -78,6 +82,16 @@ it('should show inactive status if offline and reached EOL', async () => {
expect(await ui.ltaDocumentationLinkInactive.find()).toBeInTheDocument();
});
+it.each([
+ ['Standard', Mode.Standard, 'STANDARD'],
+ ['MQR', Mode.MQR, 'MQR'],
+])('should show correct %s mode', async (_, mode, expected) => {
+ modeHandler.setMode(mode);
+ renderGlobalFooter();
+
+ expect(await byText(`footer.mode.${expected}`).find()).toBeInTheDocument();
+});
+
it('should not render missing logged-in information', () => {
renderGlobalFooter({}, { edition: undefined, version: '' });
@@ -124,7 +138,7 @@ const ui = {
databaseWarningMessage: byText('footer.production_database_warning'),
versionLabel: (version?: string) =>
- version ? byText(/footer\.version\.*(\d.\d)/) : byText(/footer\.version/),
+ version ? byText(/footer\.version\.short\.*(\d.\d)/) : byText(/footer\.version\.short/),
// links
websiteLink: byRole('link', { name: 'SonarQube™' }),
diff --git a/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx b/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx
index bd30797fb90..cdd86c1660c 100644
--- a/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx
@@ -148,7 +148,7 @@ function getPageObjects() {
healthCauseWarning: byText('Friendly warning'),
saveButton: byRole('button', { name: 'save' }),
versionLabel: (version?: string) =>
- version ? byText(/footer\.version\s*(\d.\d)/) : byText(/footer\.version/),
+ version ? byText(/footer\.version\.full\s*(\d.\d)/) : byText(/footer\.version\.full/),
ltaDocumentationLinkActive: byRole('link', {
name: `footer.version.status.active`,
}),
diff --git a/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx b/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx
index bd481c9c061..6af7f6e8f56 100644
--- a/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx
+++ b/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx
@@ -29,7 +29,7 @@ import { isCurrentVersionEOLActive } from '../../helpers/system';
import { useSystemUpgrades } from '../../queries/system';
import { EditionKey } from '../../types/editions';
-export default function AppVersionStatus() {
+export default function AppVersionStatus({ statusOnly }: Readonly<{ statusOnly?: boolean }>) {
const { data } = useSystemUpgrades();
const { edition, version, versionEOL } = useAppState();
@@ -45,7 +45,7 @@ export default function AppVersionStatus() {
const intl = useIntl();
return intl.formatMessage(
- { id: `footer.version` },
+ { id: statusOnly ? `footer.version.status` : `footer.version.full` },
{
version: getInstanceVersionNumber(version),
status: edition && edition !== EditionKey.community && (
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 4f70d1535a6..056494d574f 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -4779,11 +4779,14 @@ footer.production_database_warning=Embedded database should be used for evaluati
footer.security=Security
footer.status=Status
footer.terms=Terms
-footer.version=v{version}{status}
+footer.version.full=v{version}{status}
+footer.version.short=v{version}
+footer.version.status={status}
footer.version.status.active=ACTIVE
footer.version.status.inactive=NO LONGER ACTIVE
footer.web_api=Web API
-
+footer.mode.STANDARD=Standard Experience
+footer.mode.MQR=MQR Mode
#------------------------------------------------------------------------------
#