Browse Source

SONAR-21867 Create Image component with baseUrl

tags/10.5.0.89998
David Cho-Lerat 1 month ago
parent
commit
0360080820
24 changed files with 221 additions and 166 deletions
  1. 11
    0
      server/sonar-web/.eslintrc
  2. 15
    11
      server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx
  3. 3
    3
      server/sonar-web/src/main/js/app/components/nav/component/branch-like/PRLink.tsx
  4. 3
    1
      server/sonar-web/src/main/js/app/components/nav/global/MainSonarQubeBar.tsx
  5. 3
    2
      server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
  6. 4
    3
      server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx
  7. 14
    13
      server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
  8. 4
    3
      server/sonar-web/src/main/js/apps/groups/components/ListItem.tsx
  9. 14
    23
      server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
  10. 10
    5
      server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx
  11. 6
    4
      server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.tsx
  12. 4
    2
      server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx
  13. 3
    7
      server/sonar-web/src/main/js/apps/projects/components/ProjectCreationMenuItem.tsx
  14. 4
    5
      server/sonar-web/src/main/js/apps/security-hotspots/components/EmptyHotspotsPage.tsx
  15. 6
    10
      server/sonar-web/src/main/js/apps/sessions/components/Login.tsx
  16. 10
    27
      server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx
  17. 5
    5
      server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
  18. 7
    4
      server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx
  19. 42
    0
      server/sonar-web/src/main/js/components/common/Image.tsx
  20. 4
    3
      server/sonar-web/src/main/js/components/embed-docs-modal/EmbedDocsPopup.tsx
  21. 8
    4
      server/sonar-web/src/main/js/components/permissions/GroupHolder.tsx
  22. 6
    4
      server/sonar-web/src/main/js/components/permissions/UserHolder.tsx
  23. 27
    21
      server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
  24. 8
    6
      server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx

+ 11
- 0
server/sonar-web/.eslintrc View File

@@ -9,6 +9,17 @@
"rules": {
"camelcase": "off",
"promise/no-return-wrap": "warn",
"react/forbid-elements": [
"warn",
{
"forbid": [
{
"element": "img",
"message": "use <Image> from components/common instead"
}
]
}
],
"react/jsx-curly-brace-presence": "warn",
"testing-library/render-result-naming-convention": "off",
/* Local rules, defined in ./eslint-local-rules/ */

+ 15
- 11
server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx View File

@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { LinkStandalone } from '@sonarsource/echoes-react';
import {
ButtonPrimary,
Card,
@@ -24,7 +26,6 @@ import {
CheckIcon,
ClipboardButton,
InputField,
Link,
ListItem,
Note,
OrderedList,
@@ -33,6 +34,7 @@ import {
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { useSearchParams } from 'react-router-dom';
import { Image } from '../../components/common/Image';
import { whenLoggedIn } from '../../components/hoc/whenLoggedIn';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { generateSonarLintUserToken, portIsValid, sendUserToken } from '../../helpers/sonarlint';
@@ -88,10 +90,9 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
{status === Status.request && (
<>
<Title>{translate('sonarlint-connection.request.title')}</Title>
<img
alt=""
<Image
alt="sonarlint-connection-request"
className="sw-my-4"
role="presentation"
src="/images/SonarLint-connection-request.png"
/>
<p className="sw-my-4">
@@ -107,7 +108,7 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {

{status === Status.tokenError && (
<>
<img alt="" className="sw-my-4 sw-pt-2" role="presentation" src="/images/cross.svg" />
<Image alt="sonarlint-token-error" className="sw-my-4 sw-pt-2" src="/images/cross.svg" />
<Title>{translate('sonarlint-connection.token-error.title')}</Title>
<p className="sw-my-4">{translate('sonarlint-connection.token-error.description')}</p>
<p className="sw-mb-4">
@@ -116,9 +117,9 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
defaultMessage={translate('sonarlint-connection.token-error.description2')}
values={{
link: (
<Link to="/account/security">
<LinkStandalone to="/account/security">
{translate('sonarlint-connection.token-error.description2.link')}
</Link>
</LinkStandalone>
),
}}
/>
@@ -128,7 +129,11 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {

{status === Status.tokenCreated && newToken && (
<>
<img alt="" className="sw-my-4 sw-pt-2" role="presentation" src="/images/check.svg" />
<Image
alt="sonarlint-connection-error"
className="sw-my-4 sw-pt-2"
src="/images/check.svg"
/>
<Title>{translate('sonarlint-connection.connection-error.title')}</Title>
<p className="sw-my-6">
{translate('sonarlint-connection.connection-error.description')}
@@ -160,10 +165,9 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
{status === Status.tokenSent && newToken && (
<>
<Title>{translate('sonarlint-connection.success.title')}</Title>
<img
alt=""
<Image
alt="sonarlint-connection-success"
className="sw-mb-4"
role="presentation"
src="/images/SonarLint-connection-ok.png"
/>
<p className="sw-my-4">

+ 3
- 3
server/sonar-web/src/main/js/app/components/nav/component/branch-like/PRLink.tsx View File

@@ -20,9 +20,9 @@

import { LinkStandalone } from '@sonarsource/echoes-react';
import React from 'react';
import { Image } from '../../../../../components/common/Image';
import { isPullRequest } from '../../../../../helpers/branch-like';
import { translate, translateWithParameters } from '../../../../../helpers/l10n';
import { getBaseUrl } from '../../../../../helpers/system';
import { isDefined } from '../../../../../helpers/types';
import { AlmKeys } from '../../../../../types/alm-settings';
import { BranchLike } from '../../../../../types/branch-like';
@@ -70,10 +70,10 @@ export default function PRLink({
<LinkStandalone
iconLeft={
almKey !== '' && (
<img
<Image
alt={almKey}
height={16}
src={`${getBaseUrl()}/images/alm/${almKey}.svg`}
src={`/images/alm/${almKey}.svg`}
title={translateWithParameters('branches.see_the_pr_on_x', translate(almKey))}
/>
)

+ 3
- 1
server/sonar-web/src/main/js/app/components/nav/global/MainSonarQubeBar.tsx View File

@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { MainAppBar, SonarQubeLogo } from 'design-system';
import * as React from 'react';
import { Image } from '../../../../components/common/Image';
import { translate } from '../../../../helpers/l10n';
import { GlobalSettingKeys } from '../../../../types/settings';
import { AppStateContext } from '../../app-state/AppStateContext';
@@ -35,7 +37,7 @@ function LogoWithAriaText() {
return (
<div aria-label={title} role="img">
{customLogoUrl ? (
<img alt={title} src={customLogoUrl} width={customLogoWidth} />
<Image alt={title} src={customLogoUrl} width={customLogoWidth} />
) : (
<SonarQubeLogo />
)}

+ 3
- 2
server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx View File

@@ -17,12 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import styled from '@emotion/styled';
import { ButtonPrimary, ButtonSecondary, themeBorder, themeColor } from 'design-system';
import * as React from 'react';
import { dismissNotice } from '../../../api/users';
import { Image } from '../../../components/common/Image';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { NoticeType, isLoggedIn } from '../../../types/users';
import { CurrentUserContextInterface } from '../current-user/CurrentUserContext';
import withCurrentUserContext from '../current-user/withCurrentUserContext';
@@ -47,7 +48,7 @@ export function PromotionNotification(props: CurrentUserContextInterface) {
return (
<PromotionNotificationWrapper className="it__promotion_notification sw-z-global-popup sw-rounded-1 sw-flex sw-items-center sw-px-4">
<div className="sw-mr-2">
<img alt="SonarQube + SonarLint" height={80} src={`${getBaseUrl()}/images/sq-sl.svg`} />
<Image alt="SonarQube + SonarLint" height={80} src="/images/sq-sl.svg" />
</div>
<PromotionNotificationContent className="sw-flex-1 sw-px-2 sw-py-4">
<span className="sw-body-sm-highlight">{translate('promotion.sonarlint.title')}</span>

+ 4
- 3
server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx View File

@@ -17,11 +17,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { getTextColor } from 'design-system';
import * as React from 'react';
import { getIdentityProviders } from '../../../api/users';
import { colors } from '../../../app/theme';
import { getBaseUrl } from '../../../helpers/system';
import { Image } from '../../../components/common/Image';
import { IdentityProvider } from '../../../types/types';
import { LoggedInUser } from '../../../types/users';

@@ -104,11 +105,11 @@ export default class UserExternalIdentity extends React.PureComponent<
color: getTextColor(identityProvider.backgroundColor, colors.secondFontColor),
}}
>
<img
<Image
alt={identityProvider.name}
className="sw-mr-1"
height="14"
src={getBaseUrl() + identityProvider.iconPath}
src={identityProvider.iconPath}
width="14"
/>
{user.externalIdentity}

+ 14
- 13
server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx View File

@@ -17,22 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

/* eslint-disable react/no-unused-prop-types */

import { LinkStandalone, Spinner } from '@sonarsource/echoes-react';
import {
ButtonSecondary,
GreyCard,
HelperHintIcon,
LightPrimary,
Spinner,
StandoutLink,
TextMuted,
Title,
} from 'design-system';
import * as React from 'react';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { Image } from '../../../components/common/Image';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { getCreateProjectModeLocation } from '../../../helpers/urls';
import { AlmKeys } from '../../../types/alm-settings';
import { AppState } from '../../../types/appstate';
@@ -79,12 +80,10 @@ function renderAlmOption(
const svgFileNameGrey = `${svgFileName}_grey`;

const icon = (
<img
<Image
alt="" // Should be ignored by screen readers
className="sw-h-4 sw-w-4"
src={`${getBaseUrl()}/images/alm/${
!disabled && hasConfig ? svgFileName : svgFileNameGrey
}.svg`}
src={`/images/alm/${!disabled && hasConfig ? svgFileName : svgFileNameGrey}.svg`}
/>
);

@@ -92,9 +91,11 @@ function renderAlmOption(
<GreyCard key={alm} className="sw-col-span-4 sw-p-4 sw-flex sw-justify-between sw-items-center">
<div className="sw-items-center sw-flex sw-py-2">
{!disabled && hasConfig ? (
<StandoutLink icon={icon} to={getCreateProjectModeLocation(mode)}>
{translate('onboarding.create_project.import_select_method', alm)}
</StandoutLink>
<LinkStandalone iconLeft={icon} to={getCreateProjectModeLocation(mode)}>
<span className="sw-ml-2">
{translate('onboarding.create_project.import_select_method', alm)}
</span>
</LinkStandalone>
) : (
<>
{icon}
@@ -106,7 +107,7 @@ function renderAlmOption(
)}
</div>

<Spinner loading={loadingBindings}>
<Spinner isLoading={loadingBindings}>
{!hasConfig &&
(canAdmin ? (
<ButtonSecondary onClick={() => props.onConfigMode(configMode)}>
@@ -167,9 +168,9 @@ export function CreateProjectModeSelection(props: CreateProjectModeSelectionProp
<div className="sw-grid sw-gap-x-12 sw-gap-y-6 sw-grid-cols-12">
<GreyCard className="sw-col-span-4 sw-p-4 sw-py-6 sw-flex sw-justify-between sw-items-center">
<div>
<StandoutLink to={getCreateProjectModeLocation(CreateProjectModes.Manual)}>
<LinkStandalone to={getCreateProjectModeLocation(CreateProjectModes.Manual)}>
{translate('onboarding.create_project.import_select_method.manual')}
</StandoutLink>
</LinkStandalone>
</div>
</GreyCard>
</div>

+ 4
- 3
server/sonar-web/src/main/js/apps/groups/components/ListItem.tsx View File

@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import {
ActionsDropdown,
Badge,
@@ -33,8 +34,8 @@ import {
} from 'design-system';
import * as React from 'react';
import { useState } from 'react';
import { Image } from '../../../components/common/Image';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { useGroupMembersCountQuery } from '../../../queries/group-memberships';
import { Group, Provider } from '../../../types/types';
import DeleteGroupForm from './DeleteGroupForm';
@@ -69,11 +70,11 @@ export default function ListItem(props: Readonly<ListItemProps>) {
}

return (
<img
<Image
alt={identityProvider}
className="sw-ml-2 sw-mr-2"
height={16}
src={`${getBaseUrl()}/images/alm/${identityProvider}.svg`}
src={`/images/alm/${identityProvider}.svg`}
/>
);
};

+ 14
- 23
server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx View File

@@ -17,9 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { SubHeading, UnorderedList } from 'design-system';
import * as React from 'react';
import { getBaseUrl } from '../../../helpers/system';
import { Image } from '../../../components/common/Image';
import { Edition, EditionKey } from '../../../types/editions';

interface Props {
@@ -32,11 +33,11 @@ export default function EditionBox({ edition }: Readonly<Props>) {
return (
<div>
<SubHeading as="h2" id="data-center-edition">
<img
<Image
alt="SonarQube logo"
className="sw-mr-2"
width={16}
src={`${getBaseUrl()}/images/embed-doc/sq-icon.svg`}
src="/images/embed-doc/sq-icon.svg"
/>
<span>Data Center Edition</span>
</SubHeading>
@@ -56,11 +57,11 @@ export default function EditionBox({ edition }: Readonly<Props>) {
return (
<div>
<SubHeading as="h2" id="enterprise-edition">
<img
<Image
alt="SonarQube logo"
className="sw-mr-2"
width={16}
src={`${getBaseUrl()}/images/embed-doc/sq-icon.svg`}
src="/images/embed-doc/sq-icon.svg"
/>
<span>Enterprise Edition</span>
</SubHeading>
@@ -83,11 +84,11 @@ export default function EditionBox({ edition }: Readonly<Props>) {
return (
<div>
<SubHeading as="h2" id="developer-edition">
<img
<Image
alt="SonarQube logo"
className="sw-mr-2"
width={16}
src={`${getBaseUrl()}/images/embed-doc/sq-icon.svg`}
src="/images/embed-doc/sq-icon.svg"
/>
<span>Developer Edition</span>
</SubHeading>
@@ -98,28 +99,18 @@ export default function EditionBox({ edition }: Readonly<Props>) {
<UnorderedList className="sw-ml-8" ticks>
<li>
<span>PR / MR decoration &amp; Quality Gate</span>
<img
alt="GitHub"
className="sw-ml-2"
src={`${getBaseUrl()}/images/alm/github.svg`}
width={16}
/>
<img
alt="GitLab"
className="sw-ml-2"
src={`${getBaseUrl()}/images/alm/gitlab.svg`}
width={16}
/>
<img
<Image alt="GitHub" className="sw-ml-2" src="/images/alm/github.svg" width={16} />
<Image alt="GitLab" className="sw-ml-2" src="/images/alm/gitlab.svg" width={16} />
<Image
alt="Azure DevOps"
className="sw-ml-2"
src={`${getBaseUrl()}/images/alm/azure.svg`}
src="/images/alm/azure.svg"
width={16}
/>
<img
<Image
alt="Bitbucket"
className="sw-ml-2"
src={`${getBaseUrl()}/images/alm/bitbucket.svg`}
src="/images/alm/bitbucket.svg"
width={16}
/>
</li>

+ 10
- 5
server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx View File

@@ -17,13 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { Link, Note, getTabPanelId } from 'design-system';

import { Link } from '@sonarsource/echoes-react';
import { Note, getTabPanelId } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import DocumentationLink from '../../../components/common/DocumentationLink';
import { Image } from '../../../components/common/Image';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { CodeScope, queryToSearch } from '../../../helpers/urls';
import { Branch } from '../../../types/branch-like';
import { ComponentQualifier } from '../../../types/component';
@@ -42,7 +44,10 @@ export default function MeasuresPanelNoNewCode(props: MeasuresPanelNoNewCodeProp
const isApp = component.qualifier === ComponentQualifier.Application;

const hasBadReferenceBranch =
!isApp && !!period && !period.date && period.mode === NewCodeDefinitionType.ReferenceBranch;
!isApp &&
!!period &&
period.date === '' &&
period.mode === NewCodeDefinitionType.ReferenceBranch;
/*
* If the period is "reference branch"-based, and if there's no date, it means
* that we're not lacking a second analysis, but that we'll never have new code because the
@@ -65,11 +70,11 @@ export default function MeasuresPanelNoNewCode(props: MeasuresPanelNoNewCodeProp
id={getTabPanelId(CodeScope.New)}
style={{ height: 500 }}
>
<img
<Image
alt="" /* Make screen readers ignore this image; it's purely eye candy. */
className="sw-mr-2"
height={52}
src={`${getBaseUrl()}/images/source-code.svg`}
src="/images/source-code.svg"
/>
<Note as="div" className="sw-ml-4 sw-max-w-abs-500">
<p className="sw-mb-2 sw-mt-4">{translate(badExplanationKey)}</p>

+ 6
- 4
server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.tsx View File

@@ -17,11 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { ButtonPrimary, FlagMessage, Title } from 'design-system';
import * as React from 'react';
import GitHubSynchronisationWarning from '../../../../app/components/GitHubSynchronisationWarning';
import { Image } from '../../../../components/common/Image';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system';
import { isDefined } from '../../../../helpers/types';
import { useGithubProvisioningEnabledQuery } from '../../../../queries/identity-provider/github';
import { isApplication, isPortfolioLike, isProject } from '../../../../types/component';
import { Component } from '../../../../types/types';
@@ -69,19 +71,19 @@ export default function PageHeader(props: Props) {
<Title>
{translate('permissions.page')}
{provisionedByGitHub && (
<img
<Image
alt="github"
className="sw-mx-2 sw-align-baseline"
aria-label={translate('project_permission.github_managed')}
height={16}
src={`${getBaseUrl()}/images/alm/github.svg`}
src="/images/alm/github.svg"
/>
)}
</Title>

<div>
<p>{description}</p>
{visibilityDescription && <p>{visibilityDescription}</p>}
{isDefined(visibilityDescription) && <p>{visibilityDescription}</p>}
{provisionedByGitHub && (
<>
<p>{translate('roles.page.description.github')}</p>

+ 4
- 2
server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx View File

@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { Spinner } from '@sonarsource/echoes-react';
import {
BasicSeparator,
@@ -32,6 +33,7 @@ import {
import { isEmpty } from 'lodash';
import * as React from 'react';
import { useState } from 'react';
import { Image } from '../../../components/common/Image';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { localizeMetric } from '../../../helpers/measures';
@@ -103,7 +105,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
onClick={() => handleSelectType(BadgeType.measure)}
selected={BadgeType.measure === selectedType}
image={
<img
<Image
alt={translate('overview.badges', BadgeType.measure, 'alt')}
src={getBadgeUrl(BadgeType.measure, fullBadgeOptions, token)}
/>
@@ -115,7 +117,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
onClick={() => handleSelectType(BadgeType.qualityGate)}
selected={BadgeType.qualityGate === selectedType}
image={
<img
<Image
alt={translate('overview.badges', BadgeType.qualityGate, 'alt')}
src={getBadgeUrl(BadgeType.qualityGate, fullBadgeOptions, token)}
width="128px"

+ 3
- 7
server/sonar-web/src/main/js/apps/projects/components/ProjectCreationMenuItem.tsx View File

@@ -17,10 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { ItemLink } from 'design-system';
import * as React from 'react';
import { Image } from '../../../components/common/Image';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { queryToSearch } from '../../../helpers/urls';
import { AlmKeys } from '../../../types/alm-settings';

@@ -40,12 +41,7 @@ export default function ProjectCreationMenuItem(props: ProjectCreationMenuItemPr
to={{ pathname: '/projects/create', search: queryToSearch({ mode: alm }) }}
>
{alm !== 'manual' && (
<img
alt={alm}
className="sw-mr-2"
width={16}
src={`${getBaseUrl()}/images/alm/${almIcon}.svg`}
/>
<Image alt={alm} className="sw-mr-2" width={16} src={`/images/alm/${almIcon}.svg`} />
)}
{translate('my_account.add_project', alm)}
</ItemLink>

+ 4
- 5
server/sonar-web/src/main/js/apps/security-hotspots/components/EmptyHotspotsPage.tsx View File

@@ -17,11 +17,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { Note } from 'design-system';
import * as React from 'react';
import DocumentationLink from '../../../components/common/DocumentationLink';
import { Image } from '../../../components/common/Image';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';

export interface EmptyHotspotsPageProps {
filtered: boolean;
@@ -45,13 +46,11 @@ export default function EmptyHotspotsPage(props: EmptyHotspotsPageProps) {

return (
<div className="sw-items-center sw-justify-center sw-flex-col sw-flex sw-pt-16">
<img
<Image
alt={translate('hotspots.page')}
className="sw-mt-8"
height={100}
src={`${getBaseUrl()}/images/${
filtered && !filterByFile ? 'filter-large' : 'hotspot-large'
}.svg`}
src={`/images/${filtered && !filterByFile ? 'filter-large' : 'hotspot-large'}.svg`}
/>
<h1 className="sw-mt-10 sw-body-sm-highlight">
{translate(`hotspots.${translationRoot}.title`)}

+ 6
- 10
server/sonar-web/src/main/js/apps/sessions/components/Login.tsx View File

@@ -17,22 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import styled from '@emotion/styled';
import { Spinner } from '@sonarsource/echoes-react';
import {
Card,
FlagMessage,
PageContentFontWrapper,
Spinner,
Title,
themeBorder,
themeColor,
} from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { Image } from '../../../components/common/Image';
import { Location } from '../../../components/hoc/withRouter';
import { translate } from '../../../helpers/l10n';
import { sanitizeUserInput } from '../../../helpers/sanitize';
import { getBaseUrl } from '../../../helpers/system';
import { getReturnUrl } from '../../../helpers/urls';
import { IdentityProvider } from '../../../types/types';
import LoginForm from './LoginForm';
@@ -54,17 +55,12 @@ export default function Login(props: Readonly<LoginProps>) {
return (
<div className="sw-flex sw-flex-col sw-items-center" id="login_form">
<Helmet defer={false} title={translate('login.page')} />
<img alt="" className="sw-mt-32" src={`${getBaseUrl()}/images/sonar-logo-horizontal.png`} />
<Image alt="" className="sw-mt-32" src="/images/sonar-logo-horizontal.png" />
<Card className="sw-my-14 sw-p-0 sw-w-abs-350">
<PageContentFontWrapper className="sw-body-md sw-flex sw-flex-col sw-items-center sw-py-8 sw-px-4">
<img
alt=""
className="sw-mb-6"
src={`${getBaseUrl()}/images/embed-doc/sq-icon.svg`}
width={28}
/>
<Image alt="" className="sw-mb-6" src="/images/embed-doc/sq-icon.svg" width={28} />
<Title className="sw-mb-6">{translate('login.login_to_sonarqube')}</Title>
<Spinner loading={loading}>
<Spinner isLoading={loading}>
<>
{displayError && (
<FlagMessage className="sw-mb-6" variant="error">

+ 10
- 27
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx View File

@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { FlagMessage, Link, SubTitle, ToggleButton } from 'design-system';

import { Link } from '@sonarsource/echoes-react';
import { FlagMessage, SubTitle, ToggleButton } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Image } from '../../../../components/common/Image';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system';
import { isDefined } from '../../../../helpers/types';
import { useGetValuesQuery } from '../../../../queries/settings';
import {
AlmKeys,
@@ -56,12 +59,7 @@ const tabs = [
{
label: (
<>
<img
alt="github"
className="sw-mr-2"
height={16}
src={`${getBaseUrl()}/images/alm/github.svg`}
/>
<Image alt="github" className="sw-mr-2" height={16} src="/images/alm/github.svg" />
{translate('settings.almintegration.tab.github')}
</>
),
@@ -70,12 +68,7 @@ const tabs = [
{
label: (
<>
<img
alt="bitbucket"
className="sw-mr-2"
height={16}
src={`${getBaseUrl()}/images/alm/bitbucket.svg`}
/>
<Image alt="bitbucket" className="sw-mr-2" height={16} src="/images/alm/bitbucket.svg" />
{translate('settings.almintegration.tab.bitbucket')}
</>
),
@@ -84,12 +77,7 @@ const tabs = [
{
label: (
<>
<img
alt="azure"
className="sw-mr-2"
height={16}
src={`${getBaseUrl()}/images/alm/azure.svg`}
/>
<Image alt="azure" className="sw-mr-2" height={16} src="/images/alm/azure.svg" />
{translate('settings.almintegration.tab.azure')}
</>
),
@@ -98,12 +86,7 @@ const tabs = [
{
label: (
<>
<img
alt="gitlab"
className="sw-mr-2"
height={16}
src={`${getBaseUrl()}/images/alm/gitlab.svg`}
/>
<Image alt="gitlab" className="sw-mr-2" height={16} src="/images/alm/gitlab.svg" />
{translate('settings.almintegration.tab.gitlab')}
</>
),
@@ -182,7 +165,7 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp
onUpdateDefinitions={props.onUpdateDefinitions}
/>

{definitionKeyForDeletion && (
{isDefined(definitionKeyForDeletion) && (
<DeleteModal
id={definitionKeyForDeletion}
onCancel={props.onCancelDelete}

+ 5
- 5
server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx View File

@@ -17,16 +17,18 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { Link } from '@sonarsource/echoes-react';
import classNames from 'classnames';
import { FlagMessage, Link, SubTitle, ToggleButton, getTabId, getTabPanelId } from 'design-system';
import { FlagMessage, SubTitle, ToggleButton, getTabId, getTabPanelId } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { useSearchParams } from 'react-router-dom';
import withAvailableFeatures, {
WithAvailableFeaturesProps,
} from '../../../../app/components/available-features/withAvailableFeatures';
import { Image } from '../../../../components/common/Image';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system';
import { searchParamsToQuery } from '../../../../helpers/urls';
import { AlmKeys } from '../../../../types/alm-settings';
import { Feature } from '../../../../types/features';
@@ -54,9 +56,7 @@ export const DOCUMENTATION_LINK_SUFFIXES = {
};

function renderDevOpsIcon(key: string) {
return (
<img alt={key} className="sw-mr-2" height={16} src={`${getBaseUrl()}/images/alm/${key}.svg`} />
);
return <Image alt={key} className="sw-mr-2" height={16} src={`/images/alm/${key}.svg`} />;
}

export function Authentication(props: Props & WithAvailableFeaturesProps) {

+ 7
- 4
server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx View File

@@ -21,8 +21,9 @@
import { Badge, Note, getTextColor } from 'design-system';
import * as React from 'react';
import { colors } from '../../../app/theme';
import { Image } from '../../../components/common/Image';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { isDefined } from '../../../helpers/types';
import { IdentityProvider, Provider } from '../../../types/types';
import { RestUserDetailed } from '../../../types/users';

@@ -39,7 +40,9 @@ export default function UserListItemIdentity({ identityProvider, user, managePro
<strong className="it__user-name sw-body-sm-highlight">{user.name}</strong>
<Note className="it__user-login">{user.login}</Note>
</div>
{user.email && <div className="it__user-email sw-mt-1">{user.email}</div>}
{isDefined(user.email) && user.email !== '' && (
<div className="it__user-email sw-mt-1">{user.email}</div>
)}
{!user.local && user.externalProvider !== 'sonarqube' && (
<ExternalProvider identityProvider={identityProvider} user={user} />
)}
@@ -68,11 +71,11 @@ export function ExternalProvider({ identityProvider, user }: Omit<Props, 'manage
color: getTextColor(identityProvider.backgroundColor, colors.secondFontColor),
}}
>
<img
<Image
alt={identityProvider.name}
className="sw-mr-1"
height="14"
src={getBaseUrl() + identityProvider.iconPath}
src={identityProvider.iconPath}
width="14"
/>
{user.externalLogin}

+ 42
- 0
server/sonar-web/src/main/js/components/common/Image.tsx View File

@@ -0,0 +1,42 @@
/*
* SonarQube
* Copyright (C) 2009-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import * as React from 'react';
import { getBaseUrl } from '../../helpers/system';

export function Image(props: Readonly<JSX.IntrinsicElements['img']>) {
const { alt, src: source, ...rest } = props;

const baseUrl = getBaseUrl();

let src = source;

if (
src !== undefined &&
!src.startsWith(baseUrl) &&
!src.startsWith('http') &&
!src.startsWith('data:')
) {
src = `${baseUrl}/${src}`.replace(/(?<!:)\/+/g, '/');
}

// eslint-disable-next-line react/forbid-elements
return <img alt={alt} src={src} {...rest} />;
}

+ 4
- 3
server/sonar-web/src/main/js/components/embed-docs-modal/EmbedDocsPopup.tsx View File

@@ -17,11 +17,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { ItemDivider, ItemHeader, ItemLink, OpenNewTabIcon } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
import { getBaseUrl } from '../../helpers/system';
import { SuggestionLink } from '../../types/types';
import { Image } from '../common/Image';
import { DocItemLink } from './DocItemLink';
import { SuggestionsContext } from './SuggestionsContext';

@@ -36,12 +37,12 @@ function IconLink({
}) {
return (
<ItemLink to={link}>
<img
<Image
alt={text}
aria-hidden
className="sw-mr-2"
height="18"
src={`${getBaseUrl()}/images/${icon}`}
src={`/images/${icon}`}
width="18"
/>
{text}

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

@@ -17,13 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { Badge, ContentCell, TableRowInteractive, UserGroupIcon } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
import { isPermissionDefinitionGroup } from '../../helpers/permissions';
import { getBaseUrl } from '../../helpers/system';
import { isDefined } from '../../helpers/types';
import { Permissions } from '../../types/permissions';
import { PermissionDefinitions, PermissionGroup } from '../../types/types';
import { Image } from '../common/Image';
import PermissionCell from './PermissionCell';
import usePermissionChange from './usePermissionChange';

@@ -63,12 +65,12 @@ export default function GroupHolder(props: Props) {
<strong>{group.name}</strong>
</div>
{disabled && (
<img
<Image
alt="github"
className="sw-ml-2"
aria-label={translate('project_permission.github_managed')}
height={16}
src={`${getBaseUrl()}/images/alm/github.svg`}
src="/images/alm/github.svg"
/>
)}
{group.name === ANYONE && (
@@ -77,7 +79,9 @@ export default function GroupHolder(props: Props) {
</Badge>
)}
</div>
{description && <div className="sw-mt-2 sw-whitespace-normal">{description}</div>}
{isDefined(description) && (
<div className="sw-mt-2 sw-whitespace-normal">{description}</div>
)}
</div>
</div>
</ContentCell>

+ 6
- 4
server/sonar-web/src/main/js/components/permissions/UserHolder.tsx View File

@@ -17,12 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { Avatar, ContentCell, Note, TableRowInteractive } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
import { isPermissionDefinitionGroup } from '../../helpers/permissions';
import { getBaseUrl } from '../../helpers/system';
import { isDefined } from '../../helpers/types';
import { PermissionDefinitions, PermissionUser } from '../../types/types';
import { Image } from '../common/Image';
import PermissionCell from './PermissionCell';
import usePermissionChange from './usePermissionChange';

@@ -90,16 +92,16 @@ export default function UserHolder(props: Props) {
<Note className="sw-ml-2">{user.login}</Note>
</div>
{disabled && (
<img
<Image
alt="github"
className="sw-ml-2"
height={16}
aria-label={translate('project_permission.github_managed')}
src={`${getBaseUrl()}/images/alm/github.svg`}
src="/images/alm/github.svg"
/>
)}
</div>
{user.email && (
{isDefined(user.email) && (
<div className="sw-mt-2 sw-max-w-100 sw-text-ellipsis sw-whitespace-nowrap sw-overflow-hidden">
{user.email}
</div>

+ 27
- 21
server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx View File

@@ -18,15 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { LinkHighlight, LinkStandalone, Spinner } from '@sonarsource/echoes-react';
import {
Breadcrumbs,
FlagMessage,
GreyCard,
HoverLink,
LightLabel,
LightPrimary,
Spinner,
StandoutLink,
SubTitle,
Title,
} from 'design-system';
@@ -34,13 +32,13 @@ import * as React from 'react';
import { AnalysisStatus } from '../../apps/overview/components/AnalysisStatus';
import { isMainBranch } from '../../helpers/branch-like';
import { translate } from '../../helpers/l10n';
import { getBaseUrl } from '../../helpers/system';
import { getProjectTutorialLocation } from '../../helpers/urls';
import { useBranchesQuery } from '../../queries/branch';
import { AlmKeys, AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings';
import { MainBranch } from '../../types/branch-like';
import { Component } from '../../types/types';
import { LoggedInUser } from '../../types/users';
import { Image } from '../common/Image';
import AzurePipelinesTutorial from './azure-pipelines/AzurePipelinesTutorial';
import BitbucketPipelinesTutorial from './bitbucket-pipelines/BitbucketPipelinesTutorial';
import GitHubActionTutorial from './github-action/GitHubActionTutorial';
@@ -66,9 +64,11 @@ export interface TutorialSelectionRendererProps {
function renderAlm(mode: TutorialModes, project: string, icon?: React.ReactNode) {
return (
<GreyCard className="sw-col-span-4 sw-p-4">
<StandoutLink icon={icon} to={getProjectTutorialLocation(project, mode)}>
{translate('onboarding.tutorial.choose_method', mode)}
</StandoutLink>
<LinkStandalone iconLeft={icon} to={getProjectTutorialLocation(project, mode)}>
<span className={icon ? 'sw-ml-2' : ''}>
{translate('onboarding.tutorial.choose_method', mode)}
</span>
</LinkStandalone>

{mode === TutorialModes.Local && (
<LightLabel as="p" className="sw-mt-3">
@@ -157,10 +157,10 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
renderAlm(
TutorialModes.Jenkins,
component.key,
<img
<Image
alt="" // Should be ignored by screen readers
className="sw-h-4 sw-w-4"
src={`${getBaseUrl()}/images/tutorials/jenkins.svg`}
src="/images/tutorials/jenkins.svg"
/>,
)}

@@ -168,10 +168,10 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
renderAlm(
TutorialModes.GitHubActions,
component.key,
<img
<Image
alt="" // Should be ignored by screen readers
className="sw-h-4 sw-w-4"
src={`${getBaseUrl()}/images/tutorials/github-actions.svg`}
src="/images/tutorials/github-actions.svg"
/>,
)}

@@ -179,10 +179,10 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
renderAlm(
TutorialModes.BitbucketPipelines,
component.key,
<img
<Image
alt="" // Should be ignored by screen readers
className="sw-h-4 sw-w-4"
src={`${getBaseUrl()}/images/alm/bitbucket.svg`}
src="/images/alm/bitbucket.svg"
/>,
)}

@@ -190,10 +190,10 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
renderAlm(
TutorialModes.GitLabCI,
component.key,
<img
<Image
alt="" // Should be ignored by screen readers
className="sw-h-4 sw-w-4"
src={`${getBaseUrl()}/images/alm/gitlab.svg`}
src="/images/alm/gitlab.svg"
/>,
)}

@@ -201,10 +201,10 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
renderAlm(
TutorialModes.AzurePipelines,
component.key,
<img
<Image
alt="" // Should be ignored by screen readers
className="sw-h-4 sw-w-4"
src={`${getBaseUrl()}/images/tutorials/azure-pipelines.svg`}
src="/images/tutorials/azure-pipelines.svg"
/>,
)}

@@ -217,13 +217,19 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender

{selectedTutorial && (
<Breadcrumbs className="sw-mb-3">
<HoverLink to={getProjectTutorialLocation(component.key)}>
<LinkStandalone
highlight={LinkHighlight.CurrentColor}
to={getProjectTutorialLocation(component.key)}
>
{translate('onboarding.tutorial.breadcrumbs.home')}
</HoverLink>
</LinkStandalone>

<HoverLink to={getProjectTutorialLocation(component.key, selectedTutorial)}>
<LinkStandalone
highlight={LinkHighlight.CurrentColor}
to={getProjectTutorialLocation(component.key, selectedTutorial)}
>
{translate('onboarding.tutorial.breadcrumbs', selectedTutorial)}
</HoverLink>
</LinkStandalone>
</Breadcrumbs>
)}


+ 8
- 6
server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx View File

@@ -17,11 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { LinkStandalone } from '@sonarsource/echoes-react';
import classNames from 'classnames';
import { Card, LightLabel, StandoutLink } from 'design-system';
import { Card, LightLabel } from 'design-system';
import React from 'react';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { Image } from '../../common/Image';
import { OSs, TutorialModes } from '../types';

export interface GithubCFamilyExampleRepositoriesProps {
@@ -58,15 +60,15 @@ export default function GithubCFamilyExampleRepositories(
return (
<Card className={classNames('sw-p-4 sw-bg-inherit', className)}>
<div>
<img
<Image
alt="" // Should be ignored by screen readers
className="sw-mr-2"
height={20}
src={`${getBaseUrl()}/images/alm/github.svg`}
src="/images/alm/github.svg"
/>
<StandoutLink target="_blank" to={link}>
<LinkStandalone target="_blank" to={link}>
sonarsource-cfamily-examples
</StandoutLink>
</LinkStandalone>
</div>
<LightLabel as="p" className="sw-mt-4">
{translate('onboarding.tutorial.cfamily.examples_repositories_description')}

Loading…
Cancel
Save