--- /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 { Button, ButtonVariety, IconQuestionMark, Popover } from '@sonarsource/echoes-react';
+import { useIntl } from 'react-intl';
+import DocumentationLink from '../../../components/common/DocumentationLink';
+import { DocLink } from '../../../helpers/doc-links';
+import { useStandardExperienceMode } from '../../../queries/settings';
+
+export default function QGMetricsMismatchHelp() {
+ const intl = useIntl();
+ const { data: isStandardMode } = useStandardExperienceMode();
+ return (
+ <Popover
+ title={intl.formatMessage({ id: 'issues.qg_mismatch.title' })}
+ description={intl.formatMessage({ id: 'issues.qg_mismatch.description' }, { isStandardMode })}
+ footer={
+ <DocumentationLink standalone to={DocLink.QualityGates}>
+ {intl.formatMessage({ id: 'issues.qg_mismatch.link' })}
+ </DocumentationLink>
+ }
+ >
+ <Button
+ className="sw-p-0 sw-h-fit sw-min-h-fit"
+ aria-label={intl.formatMessage({ id: 'help' })}
+ variety={ButtonVariety.DefaultGhost}
+ >
+ <IconQuestionMark />
+ </Button>
+ </Popover>
+ );
+}
import { SOFTWARE_QUALITIES } from '../../../helpers/constants';
import { SoftwareQuality } from '../../../types/clean-code-taxonomy';
+import QGMetricsMismatchHelp from './QGMetricsMismatchHelp';
import { CommonProps, SimpleListStyleFacet } from './SimpleListStyleFacet';
interface Props extends CommonProps {
itemNamePrefix="software_quality"
listItems={SOFTWARE_QUALITIES}
selectedItems={qualities}
+ help={Boolean(props.secondLine) && <QGMetricsMismatchHelp />}
{...rest}
/>
);
import { Query, formatFacetStat } from '../utils';
import { FacetItemsList } from './FacetItemsList';
import { MultipleSelectionHint } from './MultipleSelectionHint';
+import QGMetricsMismatchHelp from './QGMetricsMismatchHelp';
interface Props {
fetching: boolean;
onClear={this.handleClear}
onClick={this.handleHeaderClick}
open={open}
+ help={Boolean(secondLine) && <QGMetricsMismatchHelp />}
secondLine={secondLine}
>
<FacetItemsList labelledby={typeFacetHeaderId}>
*/
import { screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { ComponentQualifier } from '~sonar-aligned/types/component';
import SettingsServiceMock from '../../../../api/mocks/SettingsServiceMock';
import { mockComponent } from '../../../../helpers/mocks/component';
]);
});
- it('should show show mqr filters if they exist in query', async () => {
+ it('should show standard filters if they exist in query', async () => {
+ const user = userEvent.setup();
let component = renderSidebar({
query: mockQuery({ types: [IssueType.CodeSmell] }),
});
.byText('issues.facet.second_line.mode.standard')
.get(),
).toBeInTheDocument();
+ // help icon
+ expect(byRole('button', { name: 'help' }).get()).toBeInTheDocument();
+ await user.click(byRole('button', { name: 'help' }).get());
+ expect(screen.getByText('issues.qg_mismatch.title')).toBeInTheDocument();
+
expect(
screen.queryByRole('button', { name: 'issues.facet.severities' }),
).not.toBeInTheDocument();
});
it('should show show mqr filters if they exist in query', async () => {
+ const user = userEvent.setup();
let component = renderSidebar({
query: mockQuery({ impactSeverities: [SoftwareImpactSeverity.Blocker] }),
});
.byText('issues.facet.second_line.mode.mqr')
.get(),
).toBeInTheDocument();
+
+ // help icon
+ expect(byRole('button', { name: 'help' }).get()).toBeInTheDocument();
+ await user.click(byRole('button', { name: 'help' }).get());
+ expect(screen.getByText('issues.qg_mismatch.title')).toBeInTheDocument();
+
expect(
screen.queryByRole('button', { name: 'issues.facet.impactSoftwareQualities' }),
).not.toBeInTheDocument();
import * as React from 'react';
import { useIntl } from 'react-intl';
import { BareButton, HelperHintIcon } from '~design-system';
+import QGMetricsMismatchHelp from '../../apps/issues/sidebar/QGMetricsMismatchHelp';
import { IMPACT_SEVERITIES } from '../../helpers/constants';
import { DocLink } from '../../helpers/doc-links';
import { translate } from '../../helpers/l10n';
renderName={renderName}
renderTextName={renderTextName}
help={
- <Popover
- title={intl.formatMessage({ id: 'severity_impact.levels' })}
- description={
- <>
- <p>{intl.formatMessage({ id: 'severity_impact.help.line1' })}</p>
- <p className="sw-mt-2">{intl.formatMessage({ id: 'severity_impact.help.line2' })}</p>
- </>
- }
- footer={
- <DocumentationLink to={DocLink.CleanCodeIntroduction}>
- {intl.formatMessage({ id: 'learn_more' })}
- </DocumentationLink>
- }
- >
- <BareButton aria-label={intl.formatMessage({ id: 'more_information' })}>
- <HelperHintIcon />
- </BareButton>
- </Popover>
+ props.secondLine ? (
+ <QGMetricsMismatchHelp />
+ ) : (
+ <Popover
+ title={intl.formatMessage({ id: 'severity_impact.levels' })}
+ description={
+ <>
+ <p>{intl.formatMessage({ id: 'severity_impact.help.line1' })}</p>
+ <p className="sw-mt-2">
+ {intl.formatMessage({ id: 'severity_impact.help.line2' })}
+ </p>
+ </>
+ }
+ footer={
+ <DocumentationLink to={DocLink.CleanCodeIntroduction}>
+ {intl.formatMessage({ id: 'learn_more' })}
+ </DocumentationLink>
+ }
+ >
+ <BareButton aria-label={intl.formatMessage({ id: 'more_information' })}>
+ <HelperHintIcon />
+ </BareButton>
+ </Popover>
+ )
}
/>
);
*/
import * as React from 'react';
+import QGMetricsMismatchHelp from '../../apps/issues/sidebar/QGMetricsMismatchHelp';
import { SEVERITIES } from '../../helpers/constants';
import { translate } from '../../helpers/l10n';
import SoftwareImpactSeverityIcon from '../icon-mappers/SoftwareImpactSeverityIcon';
return (
<Facet
{...props}
+ help={Boolean(props.secondLine) && <QGMetricsMismatchHelp />}
options={SEVERITIES}
property="severities"
renderName={renderName}
inner={inner}
>
<Header>
- <TitleWithHelp>
- <ChevronAndTitle
- aria-controls={`${id}-panel`}
- aria-disabled={!expandable}
- aria-expanded={open}
- aria-label={ariaLabel ?? name}
- expandable={expandable}
- id={`${id}-header`}
- onClick={() => {
- if (!disabled) {
- onClick?.(!open);
- }
- }}
- >
- {expandable && <OpenCloseIndicator aria-hidden open={open} />}
-
- {disabled ? (
- <Tooltip content={disabledHelper}>
- <HeaderTitle
- aria-disabled
- aria-label={`${name}, ${disabledHelper ?? ''}`}
- disabled={disabled}
- >
- {name}
- </HeaderTitle>
- </Tooltip>
- ) : (
- <div>
- <HeaderTitle>{name}</HeaderTitle>
- {secondLine !== undefined && (
- <Text as="div" isSubdued>
- {secondLine}
- </Text>
- )}
- </div>
- )}
- </ChevronAndTitle>
+ <div className="sw-flex sw-items-center">
+ <TitleWithHelp>
+ <ChevronAndTitle
+ aria-controls={`${id}-panel`}
+ aria-disabled={!expandable}
+ aria-expanded={open}
+ aria-label={ariaLabel ?? name}
+ expandable={expandable}
+ id={`${id}-header`}
+ onClick={() => {
+ if (!disabled) {
+ onClick?.(!open);
+ }
+ }}
+ >
+ {expandable && <OpenCloseIndicator aria-hidden open={open} />}
+
+ {disabled ? (
+ <Tooltip content={disabledHelper}>
+ <HeaderTitle
+ aria-disabled
+ aria-label={`${name}, ${disabledHelper ?? ''}`}
+ disabled={disabled}
+ >
+ {name}
+ </HeaderTitle>
+ </Tooltip>
+ ) : (
+ <div>
+ <HeaderTitle>{name}</HeaderTitle>
+ {secondLine !== undefined && (
+ <Text as="div" isSubdued>
+ {secondLine}
+ </Text>
+ )}
+ </div>
+ )}
+ </ChevronAndTitle>
+ </TitleWithHelp>
{help && <span className="sw-ml-1">{help}</span>}
- </TitleWithHelp>
+ </div>
{<Spinner loading={loading} />}
issues.x_more_locations=+ {0} more locations
issues.not_all_issue_show=Not all issues are included
issues.not_all_issue_show_why=You do not have access to all projects in this portfolio
-
+issues.qg_mismatch.title=Quality Gate metrics mismatch
+issues.qg_mismatch.description=Your quality gate contains conditions from {isStandardMode, select, true {Multi-Quality Rule Mode} other {Standard Expirience}}. While your instance remains in the {isStandardMode, select, true {Standard Experience} other {Multi-Quality Rule Mode}} some additional filters will be visible for these {isStandardMode, select, true {MQR} other {Standard Expirience}} metrics.
+issues.qg_mismatch.link=Lear more about both modes in documentation
issues.open_in_ide.success=Success. Switch to your IDE to see the issue.
issues.open_in_ide.failure=Unable to open the issue in the IDE. Please check the {link}.