diff options
author | stanislavh <stanislav.honcharov@sonarsource.com> | 2024-12-03 14:00:53 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-12-04 20:03:23 +0000 |
commit | 8b42a96a0d85de0a88667c0881ba3df7a9dab3c0 (patch) | |
tree | 1500e29891eeb3a6ebd2a2cdbb6038f429a6e9fd /server/sonar-web | |
parent | 79b3fc224ffa57b1e0be55600713c577d376ba5f (diff) | |
download | sonarqube-8b42a96a0d85de0a88667c0881ba3df7a9dab3c0.tar.gz sonarqube-8b42a96a0d85de0a88667c0881ba3df7a9dab3c0.zip |
SONAR-23666 Improve accessibility by informing the user of link destination
Diffstat (limited to 'server/sonar-web')
12 files changed, 115 insertions, 51 deletions
diff --git a/server/sonar-web/__mocks__/react-intl.tsx b/server/sonar-web/__mocks__/react-intl.tsx index f68d0c36c3f..d3c6366be5d 100644 --- a/server/sonar-web/__mocks__/react-intl.tsx +++ b/server/sonar-web/__mocks__/react-intl.tsx @@ -30,7 +30,7 @@ module.exports = { {id} {Object.entries(values).map(([key, value]) => ( <React.Fragment key={key}> - {typeof value === 'function' ? value() : value} + {typeof value === 'function' ? value(`${id}_${key}`) : value} </React.Fragment> ))} </> @@ -45,7 +45,7 @@ module.exports = { values, }: { id: string; - values?: { [x: string]: React.ReactNode | (() => React.ReactNode) }; + values?: { [x: string]: React.ReactNode | ((text: string) => React.ReactNode) }; }) => { return ( <> @@ -53,7 +53,7 @@ module.exports = { {values !== undefined && Object.entries(values).map(([key, value]) => ( <React.Fragment key={key}> - {typeof value === 'function' ? value() : value} + {typeof value === 'function' ? value(`${id}_${key}`) : value} </React.Fragment> ))} </> diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx index 5e3460e49d9..0a677474771 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx @@ -18,7 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { Title } from '~design-system'; +import { Heading, LinkHighlight } from '@sonarsource/echoes-react'; +import { useIntl } from 'react-intl'; import DocumentationLink from '../../../components/common/DocumentationLink'; import { DocLink } from '../../../helpers/doc-links'; import { translate } from '../../../helpers/l10n'; @@ -30,15 +31,28 @@ interface Props { } export default function Header(props: Readonly<Props>) { + const intl = useIntl(); return ( <header className="sw-mb-12 sw-flex sw-justify-between"> <div className="sw-flex-1"> - <Title className="sw-mb-4">{translate('background_tasks.page')}</Title> + <Heading as="h2" className="sw-mb-4"> + {translate('background_tasks.page')} + </Heading> <p className="sw-max-w-3/4"> - {translate('background_tasks.page.description')} - <DocumentationLink className="sw-ml-2" to={DocLink.BackgroundTasks}> - {translate('learn_more')} - </DocumentationLink> + {intl.formatMessage( + { id: 'background_tasks.page.description' }, + { + link: (text) => ( + <DocumentationLink + shouldOpenInNewTab + highlight={LinkHighlight.CurrentColor} + to={DocLink.BackgroundTasks} + > + {text} + </DocumentationLink> + ), + }, + )} </p> </div> {!props.component && ( diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx index c78f88b41c6..b760ff90556 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx @@ -68,8 +68,8 @@ export default function ListHeader({ canCreate }: Readonly<Props>) { title={intl.formatMessage({ id: 'quality_gates.help.title' })} description={intl.formatMessage({ id: 'quality_gates.help.desc' })} footer={ - <DocumentationLink standalone to={DocLink.QualityGates}> - {intl.formatMessage({ id: 'learn_more' })} + <DocumentationLink shouldOpenInNewTab standalone to={DocLink.QualityGates}> + {intl.formatMessage({ id: 'quality_gates.help.link' })} </DocumentationLink> } > diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx index 5d89c56814c..aa29b7e493d 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx @@ -18,14 +18,20 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { Button, ButtonGroup, ButtonVariety, Heading, Link } from '@sonarsource/echoes-react'; +import { + Button, + ButtonGroup, + ButtonVariety, + Heading, + LinkHighlight, +} from '@sonarsource/echoes-react'; import * as React from 'react'; import { useIntl } from 'react-intl'; import { FlagMessage } from '~design-system'; import { useLocation, useRouter } from '~sonar-aligned/components/hoc/withRouter'; import { Actions } from '../../../api/quality-profiles'; +import DocumentationLink from '../../../components/common/DocumentationLink'; import { DocLink } from '../../../helpers/doc-links'; -import { useDocUrl } from '../../../helpers/docs'; import { translate } from '../../../helpers/l10n'; import { Profile } from '../types'; import { getProfilePath } from '../utils'; @@ -44,7 +50,6 @@ export default function PageHeader(props: Readonly<Props>) { const intl = useIntl(); const location = useLocation(); const router = useRouter(); - const docUrl = useDocUrl(DocLink.InstanceAdminQualityProfiles); const [modal, setModal] = React.useState<'' | 'createProfile' | 'restoreProfile'>(''); @@ -67,11 +72,20 @@ export default function PageHeader(props: Readonly<Props>) { </Heading> <div className="sw-typo-default"> - {intl.formatMessage({ id: 'quality_profiles.intro' })} - - <Link className="sw-ml-2" to={docUrl}> - {intl.formatMessage({ id: 'learn_more' })} - </Link> + {intl.formatMessage( + { id: 'quality_profiles.intro' }, + { + link: (text) => ( + <DocumentationLink + shouldOpenInNewTab + to={DocLink.InstanceAdminQualityProfiles} + highlight={LinkHighlight.CurrentColor} + > + {text} + </DocumentationLink> + ), + }, + )} </div> </div> diff --git a/server/sonar-web/src/main/js/apps/settings/components/AnalysisScope.tsx b/server/sonar-web/src/main/js/apps/settings/components/AnalysisScope.tsx index 49525b34f73..bc7bee86eac 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AnalysisScope.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/AnalysisScope.tsx @@ -19,7 +19,8 @@ */ import styled from '@emotion/styled'; -import { LightLabel } from '~design-system'; +import { Heading, LinkHighlight, Text } from '@sonarsource/echoes-react'; +import { useIntl } from 'react-intl'; import DocumentationLink from '../../../components/common/DocumentationLink'; import { DocLink } from '../../../helpers/doc-links'; import { translate } from '../../../helpers/l10n'; @@ -28,28 +29,57 @@ import CategoryDefinitionsList from './CategoryDefinitionsList'; export function AnalysisScope(props: AdditionalCategoryComponentProps) { const { component, definitions, selectedCategory } = props; + const intl = useIntl(); return ( <> - <StyledGrid className="sw-pt-6 sw-px-6 sw-gap-2"> - <p className="sw-col-span-2"> - {translate('settings.analysis_scope.wildcards.introduction')} - </p> + <StyledGrid className="sw-pt-1 sw-px-1 sw-gap-2"> + <Heading as="h2" className="sw-col-span-2"> + {translate('property.category.exclusions')} + </Heading> - <span>*</span> - <LightLabel>{translate('settings.analysis_scope.wildcards.zero_more_char')}</LightLabel> + <Text className="sw-col-span-2 sw-mt-4 sw-mb-3"> + {intl.formatMessage( + { id: 'settings.analysis_scope.introduction' }, + { + link: (text) => ( + <DocumentationLink + shouldOpenInNewTab + highlight={LinkHighlight.CurrentColor} + to={DocLink.AnalysisScopeWildcardPatterns} + > + {text} + </DocumentationLink> + ), + }, + )} + </Text> - <span>**</span> - <LightLabel>{translate('settings.analysis_scope.wildcards.zero_more_dir')}</LightLabel> + <Text as="p" className="sw-col-span-2"> + {intl.formatMessage( + { id: 'settings.analysis_scope.wildcards.introduction' }, + { + link: (text) => ( + <DocumentationLink + shouldOpenInNewTab + highlight={LinkHighlight.CurrentColor} + to={DocLink.AnalysisScope} + > + {text} + </DocumentationLink> + ), + }, + )} + </Text> - <span>?</span> - <LightLabel>{translate('settings.analysis_scope.wildcards.single_char')}</LightLabel> + <Text>*</Text> + <Text isSubdued>{translate('settings.analysis_scope.wildcards.zero_more_char')}</Text> - <div className="sw-col-span-2"> - <DocumentationLink to={DocLink.AnalysisScope}> - {translate('settings.analysis_scope.learn_more')} - </DocumentationLink> - </div> + <Text>**</Text> + <Text isSubdued>{translate('settings.analysis_scope.wildcards.zero_more_dir')}</Text> + + <Text>?</Text> + <Text isSubdued>{translate('settings.analysis_scope.wildcards.single_char')}</Text> </StyledGrid> <CategoryDefinitionsList diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionDescription.tsx b/server/sonar-web/src/main/js/apps/settings/components/DefinitionDescription.tsx index 6afc913924e..ec6188bc0b2 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/DefinitionDescription.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionDescription.tsx @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { Text, Tooltip } from '@sonarsource/echoes-react'; -import { SafeHTMLInjection, SanitizeLevel, SubHeading } from '~design-system'; +import { Heading, Text, Tooltip } from '@sonarsource/echoes-react'; +import { SafeHTMLInjection, SanitizeLevel } from '~design-system'; import { translateWithParameters } from '../../../helpers/l10n'; import { ExtendedSettingDefinition } from '../../../types/settings'; import { getPropertyDescription, getPropertyName } from '../utils'; @@ -34,9 +34,9 @@ export default function DefinitionDescription({ definition }: Readonly<Props>) { return ( <div className="sw-w-abs-300"> - <SubHeading className="sw-text-ellipsis sw-overflow-hidden" title={propertyName}> + <Heading as="h4" className="sw-text-ellipsis sw-overflow-hidden"> {propertyName} - </SubHeading> + </Heading> {description && ( <SafeHTMLInjection htmlAsString={description} sanitizeLevel={SanitizeLevel.RESTRICTED}> diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx index 6a9f88834e8..bf9d5b732ce 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx @@ -18,9 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { Heading } from '@sonarsource/echoes-react'; +import classNames from 'classnames'; import { groupBy, sortBy } from 'lodash'; import * as React from 'react'; -import { BasicSeparator, Note, SafeHTMLInjection, SanitizeLevel, SubTitle } from '~design-system'; +import { BasicSeparator, Note, SafeHTMLInjection, SanitizeLevel } from '~design-system'; import { withRouter } from '~sonar-aligned/components/hoc/withRouter'; import { Location } from '~sonar-aligned/types/router'; import { SettingDefinitionAndValue } from '../../../types/settings'; @@ -81,17 +83,17 @@ class SubCategoryDefinitionsList extends React.PureComponent<SubCategoryDefiniti : sortedSubCategories.filter((c) => !SUB_CATEGORY_EXCLUSIONS[category]?.includes(c.key)); return ( - <ul> + <ul className={classNames({ 'sw-mx-6': !noPadding })}> {filteredSubCategories.map((subCategory, index) => ( - <li className={noPadding ? '' : 'sw-p-6'} key={subCategory.key}> + <li className={classNames({ 'sw-py-6': !noPadding })} key={subCategory.key}> {displaySubCategoryTitle && ( - <SubTitle - as="h2" + <Heading + as="h3" data-key={subCategory.key} ref={this.scrollToSubCategoryOrDefinition} > {subCategory.name} - </SubTitle> + </Heading> )} {subCategory.description != null && ( diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx index dfc9e4c9a83..96807c96a7f 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx @@ -32,7 +32,9 @@ const handler = new SettingsServiceMock(); const ui = { introduction: byText('settings.analysis_scope.wildcards.introduction'), - docLink: byRole('link', { name: /learn_more/ }), + docLink: byRole('link', { + name: 'settings.analysis_scope.wildcards.introduction_link open_in_new_tab', + }), }; beforeEach(() => { diff --git a/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx b/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx index e125d76f9c9..b516cabe002 100644 --- a/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx +++ b/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx @@ -62,8 +62,8 @@ export default function SeverityFacet(props: Readonly<BasicProps>) { { id: `severity_impact.help.description` }, { p1: (text) => <p>{text}</p>, p: (text) => <p className="sw-mt-4">{text}</p> }, )} - link={DocLink.CleanCodeIntroduction} - linkText={intl.formatMessage({ id: 'learn_more' })} + link={DocLink.MQRSeverity} + linkText={intl.formatMessage({ id: 'severity_impact.help.link' })} /> ) } diff --git a/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx b/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx index 3f45f75d7d6..b418e39d953 100644 --- a/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx +++ b/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx @@ -51,7 +51,7 @@ export function CleanCodeAttributePill(props: Readonly<Props>) { )} footer={ <DocumentationLink to={DocLink.CleanCodeIntroduction}> - {translate('learn_more')} + {translate('clean_code_attribute.learn_more')} </DocumentationLink> } > diff --git a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx index 43fb6dd9856..ec6578de8c3 100644 --- a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx +++ b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx @@ -144,8 +144,8 @@ export default function SoftwareImpactPill(props: Props) { </> } footer={ - <DocumentationLink to={DocLink.CleanCodeIntroduction}> - {translate('learn_more')} + <DocumentationLink shouldOpenInNewTab to={DocLink.MQRSeverity}> + {translate('severity_impact.help.link')} </DocumentationLink> } > diff --git a/server/sonar-web/src/main/js/helpers/doc-links.ts b/server/sonar-web/src/main/js/helpers/doc-links.ts index 393dcbea3e2..ca747817d8e 100644 --- a/server/sonar-web/src/main/js/helpers/doc-links.ts +++ b/server/sonar-web/src/main/js/helpers/doc-links.ts @@ -45,6 +45,7 @@ export enum DocLink { AlmSamlAuth = '/instance-administration/authentication/saml/overview/', AlmSamlScimAuth = '/instance-administration/authentication/saml/scim/overview/', AnalysisScope = '/project-administration/analysis-scope/', + AnalysisScopeWildcardPatterns = '/project-administration/analysis-scope/#wildcard-patterns', AuthOverview = '/instance-administration/authentication/overview/', BackgroundTasks = '/analyzing-source-code/background-tasks/', BranchAnalysis = '/analyzing-source-code/branch-analysis/introduction/', @@ -83,6 +84,7 @@ export enum DocLink { QualityGates = '/instance-administration/analysis-functions/quality-gates/', Root = '/', RuleSeverity = '/instance-administration/analysis-functions/quality-profiles/#rule-severity', + MQRSeverity = '/instance-administration/analysis-functions/instance-mode/mqr-mode/#mqr-severity', RulesOverview = '/user-guide/rules/overview', SecurityHotspots = '/user-guide/security-hotspots/', SecurityReports = '/user-guide/viewing-reports/security-reports/', |