diff options
author | stanislavh <stanislav.honcharov@sonarsource.com> | 2024-10-31 09:40:03 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-11-01 20:02:47 +0000 |
commit | 3acf8e51e635be1e900d7d9cd23007f34ae96d8d (patch) | |
tree | f8780ce53bebf895df1ee19b378523ec42a25927 /server/sonar-web | |
parent | 82ba1f6654402ade21476396ea717f07f31a67bd (diff) | |
download | sonarqube-3acf8e51e635be1e900d7d9cd23007f34ae96d8d.tar.gz sonarqube-3acf8e51e635be1e900d7d9cd23007f34ae96d8d.zip |
SONAR-23300 Add popover to temporal issue filters
Diffstat (limited to 'server/sonar-web')
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} />} |