"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/ */
* 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,
CheckIcon,
ClipboardButton,
InputField,
- Link,
ListItem,
Note,
OrderedList,
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';
{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">
{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">
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>
),
}}
/>
{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')}
{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">
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';
<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))}
/>
)
* 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';
return (
<div aria-label={title} role="img">
{customLogoUrl ? (
- <img alt={title} src={customLogoUrl} width={customLogoWidth} />
+ <Image alt={title} src={customLogoUrl} width={customLogoWidth} />
) : (
<SonarQubeLogo />
)}
* 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';
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>
* 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';
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}
* 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';
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`}
/>
);
<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}
)}
</div>
- <Spinner loading={loadingBindings}>
+ <Spinner isLoading={loadingBindings}>
{!hasConfig &&
(canAdmin ? (
<ButtonSecondary onClick={() => props.onConfigMode(configMode)}>
<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>
* 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,
} 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';
}
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`}
/>
);
};
* 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 {
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>
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>
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>
<UnorderedList className="sw-ml-8" ticks>
<li>
<span>PR / MR decoration & 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>
* 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';
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
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>
* 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';
<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>
* 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,
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';
onClick={() => handleSelectType(BadgeType.measure)}
selected={BadgeType.measure === selectedType}
image={
- <img
+ <Image
alt={translate('overview.badges', BadgeType.measure, 'alt')}
src={getBadgeUrl(BadgeType.measure, fullBadgeOptions, token)}
/>
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"
* 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';
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>
* 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;
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`)}
* 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';
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">
* 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,
{
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')}
</>
),
{
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')}
</>
),
{
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')}
</>
),
{
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')}
</>
),
onUpdateDefinitions={props.onUpdateDefinitions}
/>
- {definitionKeyForDeletion && (
+ {isDefined(definitionKeyForDeletion) && (
<DeleteModal
id={definitionKeyForDeletion}
onCancel={props.onCancelDelete}
* 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';
};
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) {
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';
<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} />
)}
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}
--- /dev/null
+/*
+ * 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} />;
+}
* 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';
}) {
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}
* 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';
<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 && (
</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>
* 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';
<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>
* 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';
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';
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">
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"
/>,
)}
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"
/>,
)}
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"
/>,
)}
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"
/>,
)}
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"
/>,
)}
{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>
)}
* 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 {
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')}