aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorstanislavh <stanislav.honcharov@sonarsource.com>2024-10-31 09:40:03 +0100
committersonartech <sonartech@sonarsource.com>2024-11-01 20:02:47 +0000
commit3acf8e51e635be1e900d7d9cd23007f34ae96d8d (patch)
treef8780ce53bebf895df1ee19b378523ec42a25927 /server/sonar-web
parent82ba1f6654402ade21476396ea717f07f31a67bd (diff)
downloadsonarqube-3acf8e51e635be1e900d7d9cd23007f34ae96d8d.tar.gz
sonarqube-3acf8e51e635be1e900d7d9cd23007f34ae96d8d.zip
SONAR-23300 Add popover to temporal issue filters
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/QGMetricsMismatchHelp.tsx49
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx16
-rw-r--r--server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx43
-rw-r--r--server/sonar-web/src/main/js/components/facets/StandardSeverityFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/design-system/components/FacetBox.tsx78
7 files changed, 135 insertions, 57 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/QGMetricsMismatchHelp.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/QGMetricsMismatchHelp.tsx
new file mode 100644
index 00000000000..f2e99fd3c77
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/QGMetricsMismatchHelp.tsx
@@ -0,0 +1,49 @@
+/*
+ * 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>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx
index 34a1edbf186..310729c94c9 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx
@@ -20,6 +20,7 @@
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 {
@@ -35,6 +36,7 @@ export function SoftwareQualityFacet(props: Props) {
itemNamePrefix="software_quality"
listItems={SOFTWARE_QUALITIES}
selectedItems={qualities}
+ help={Boolean(props.secondLine) && <QGMetricsMismatchHelp />}
{...rest}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx
index a96858bd4bc..a0ed6e04035 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx
@@ -28,6 +28,7 @@ import { Dict } from '../../../types/types';
import { Query, formatFacetStat } from '../utils';
import { FacetItemsList } from './FacetItemsList';
import { MultipleSelectionHint } from './MultipleSelectionHint';
+import QGMetricsMismatchHelp from './QGMetricsMismatchHelp';
interface Props {
fetching: boolean;
@@ -128,6 +129,7 @@ export class TypeFacet extends React.PureComponent<Props> {
onClear={this.handleClear}
onClick={this.handleHeaderClick}
open={open}
+ help={Boolean(secondLine) && <QGMetricsMismatchHelp />}
secondLine={secondLine}
>
<FacetItemsList labelledby={typeFacetHeaderId}>
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx
index 57d9bd85aa2..730b7b8f42d 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx
@@ -19,6 +19,7 @@
*/
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';
@@ -78,7 +79,8 @@ describe('MQR mode', () => {
]);
});
- 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] }),
});
@@ -93,6 +95,11 @@ describe('MQR mode', () => {
.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();
@@ -237,6 +244,7 @@ describe('Standard mode', () => {
});
it('should show show mqr filters if they exist in query', async () => {
+ const user = userEvent.setup();
let component = renderSidebar({
query: mockQuery({ impactSeverities: [SoftwareImpactSeverity.Blocker] }),
});
@@ -251,6 +259,12 @@ describe('Standard mode', () => {
.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();
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 1ec8d6223e3..23784daa404 100644
--- a/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx
+++ b/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx
@@ -22,6 +22,7 @@ import { Popover } from '@sonarsource/echoes-react';
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';
@@ -54,24 +55,30 @@ export default function SeverityFacet(props: Readonly<BasicProps>) {
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>
+ )
}
/>
);
diff --git a/server/sonar-web/src/main/js/components/facets/StandardSeverityFacet.tsx b/server/sonar-web/src/main/js/components/facets/StandardSeverityFacet.tsx
index b667875a350..ce8801954ea 100644
--- a/server/sonar-web/src/main/js/components/facets/StandardSeverityFacet.tsx
+++ b/server/sonar-web/src/main/js/components/facets/StandardSeverityFacet.tsx
@@ -19,6 +19,7 @@
*/
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';
@@ -45,6 +46,7 @@ export default function StandardSeverityFacet(
return (
<Facet
{...props}
+ help={Boolean(props.secondLine) && <QGMetricsMismatchHelp />}
options={SEVERITIES}
property="severities"
renderName={renderName}
diff --git a/server/sonar-web/src/main/js/design-system/components/FacetBox.tsx b/server/sonar-web/src/main/js/design-system/components/FacetBox.tsx
index bac0c8b20db..c93ac18933f 100644
--- a/server/sonar-web/src/main/js/design-system/components/FacetBox.tsx
+++ b/server/sonar-web/src/main/js/design-system/components/FacetBox.tsx
@@ -94,45 +94,47 @@ export function FacetBox(props: FacetBoxProps) {
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} />}