From: Kevin Silva Date: Wed, 14 Jun 2023 11:04:15 +0000 (+0200) Subject: SONAR-19345 - Fixed several issues MUI X-Git-Tag: 10.1.0.73491~27 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=77eb3f8ecf4baab682d80427db156c6ac96a211a;p=sonarqube.git SONAR-19345 - Fixed several issues MUI --- diff --git a/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx b/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx index 18e724c1ed2..96480ed7d7e 100644 --- a/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx +++ b/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx @@ -58,6 +58,7 @@ export interface SearchSelectDropdownProps< controlLabel?: React.ReactNode | string; controlSize?: InputSizeKeys; isDiscreet?: boolean; + zLevel?: PopupZLevel; } export function SearchSelectDropdown< @@ -77,6 +78,7 @@ export function SearchSelectDropdown< minLength, controlAriaLabel, menuIsOpen, + zLevel = PopupZLevel.Global, ...rest } = props; const [open, setOpen] = React.useState(false); @@ -161,7 +163,7 @@ export function SearchSelectDropdown< } placement={PopupPlacement.BottomLeft} - zLevel={PopupZLevel.Global} + zLevel={zLevel} > { }; positionPopup = () => { - if (this.mounted) { + if (this.mounted && this.props.zLevel !== PopupZLevel.Absolute) { // `findDOMNode(this)` will search for the DOM node for the current component // first it will find a React.Fragment (see `render`), // so it will get the DOM node of the first child, i.e. DOM node of `this.props.children` @@ -216,6 +216,7 @@ const PopupWrapper = styled.div<{ zLevel: PopupZLevel }>` [PopupZLevel.Default]: tw`sw-z-popup`, [PopupZLevel.Global]: tw`sw-z-global-popup`, [PopupZLevel.Content]: tw`sw-z-content-popup`, + [PopupZLevel.Absolute]: tw`sw-z-global-popup`, }[zLevel])}; &.is-bottom, diff --git a/server/sonar-web/design-system/src/helpers/positioning.ts b/server/sonar-web/design-system/src/helpers/positioning.ts index 4cceaccc23d..47afd6b3f49 100644 --- a/server/sonar-web/design-system/src/helpers/positioning.ts +++ b/server/sonar-web/design-system/src/helpers/positioning.ts @@ -52,6 +52,7 @@ export enum PopupZLevel { Content = 'content', Default = 'popup', Global = 'global', + Absolute = 'absolute', } export type BasePlacement = Extract< diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx index 8f61c516b0a..4715c45f807 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx @@ -69,19 +69,14 @@ export default class IssueHeader extends React.PureComponent { } handleIssuePopupToggle = (popupName: string, open?: boolean) => { - const openPopupState = { issuePopupName: popupName }; - - const closePopupState = { issuePopupName: undefined }; - this.setState(({ issuePopupName }) => { - if (open) { - return openPopupState; - } else if (open === false) { - return closePopupState; + const samePopup = popupName && issuePopupName === popupName; + if (open !== false && !samePopup) { + return { issuePopupName: popupName }; + } else if (open !== true && samePopup) { + return { issuePopupName: undefined }; } - - // toggle popup - return issuePopupName === popupName ? closePopupState : openPopupState; + return { issuePopupName }; }); }; diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx index 87184179c23..d6439f3c0b7 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx @@ -430,6 +430,14 @@ export class App extends React.PureComponent { } }; + selectIssue = (issueKey: string) => { + this.setState({ + selected: issueKey, + selectedFlowIndex: undefined, + selectedLocationIndex: undefined, + }); + }; + closeIssue = () => { if (this.state.query) { this.props.router.push({ @@ -1124,6 +1132,7 @@ export class App extends React.PureComponent { onIssueChange={this.handleIssueChange} onIssueCheck={currentUser.isLoggedIn ? this.handleIssueCheck : undefined} onIssueClick={this.openIssue} + onIssueSelect={this.selectIssue} onPopupToggle={this.handlePopupToggle} openPopup={this.state.openPopup} selectedIssue={selectedIssue} diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx index 6cf790b1cf1..e5c4d2a15f1 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx @@ -34,6 +34,7 @@ interface Props { onIssueChange: (issue: Issue) => void; onIssueCheck: ((issueKey: string) => void) | undefined; onIssueClick: (issueKey: string) => void; + onIssueSelect: (issueKey: string) => void; onPopupToggle: (issue: string, popupName: string, open?: boolean) => void; openPopup: { issue: string; name: string } | undefined; selectedIssue: Issue | undefined; @@ -77,6 +78,7 @@ export default class IssuesList extends React.PureComponent { onChange={this.props.onIssueChange} onCheck={this.props.onIssueCheck} onClick={this.props.onIssueClick} + onSelect={this.props.onIssueSelect} onFilterChange={this.props.onFilterChange} onPopupToggle={this.props.onPopupToggle} openPopup={openPopup && openPopup.issue === issue.key ? openPopup.name : undefined} diff --git a/server/sonar-web/src/main/js/apps/issues/components/ListItem.tsx b/server/sonar-web/src/main/js/apps/issues/components/ListItem.tsx index f0b1af18a55..8f44dfb1566 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/ListItem.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/ListItem.tsx @@ -30,6 +30,7 @@ interface Props { onChange: (issue: TypeIssue) => void; onCheck: ((issueKey: string) => void) | undefined; onClick: (issueKey: string) => void; + onSelect: (issueKey: string) => void; onFilterChange: (changes: Partial) => void; onPopupToggle: (issue: string, popupName: string, open?: boolean) => void; openPopup: string | undefined; @@ -93,6 +94,7 @@ export default class ListItem extends React.PureComponent { onChange={this.props.onChange} onCheck={this.props.onCheck} onClick={this.props.onClick} + onSelect={this.props.onSelect} onPopupToggle={this.props.onPopupToggle} openPopup={this.props.openPopup} selected={this.props.selected} diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx index a9d639f376b..222b8ac10dd 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx @@ -70,7 +70,7 @@ export default function LineIssuesList(props: LineIssuesListProps) { issue={issue} key={issue.key} onChange={props.onIssueChange} - onClick={props.onIssueClick} + onSelect={props.onIssueClick} onPopupToggle={props.onIssuePopupToggle} openPopup={issuePopup && issuePopup.issue === issue.key ? issuePopup.name : undefined} selected={props.selectedIssue === issue.key} diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap index 11e954e8499..9a03583ab36 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap @@ -49,8 +49,8 @@ exports[`should render issues 1`] = ` } key="issue" onChange={[MockFunction]} - onClick={[MockFunction]} onPopupToggle={[MockFunction]} + onSelect={[MockFunction]} selected={true} /> diff --git a/server/sonar-web/src/main/js/components/issue/Issue.tsx b/server/sonar-web/src/main/js/components/issue/Issue.tsx index 3afdca167f0..d1f7bd57f85 100644 --- a/server/sonar-web/src/main/js/components/issue/Issue.tsx +++ b/server/sonar-web/src/main/js/components/issue/Issue.tsx @@ -35,6 +35,7 @@ interface Props { onChange: (issue: TypeIssue) => void; onCheck?: (issue: string) => void; onClick?: (issueKey: string) => void; + onSelect: (issueKey: string) => void; onPopupToggle: (issue: string, popupName: string, open?: boolean) => void; openPopup?: string; selected: boolean; @@ -119,6 +120,7 @@ export default class Issue extends React.PureComponent { onChange={this.props.onChange} onCheck={this.props.onCheck} onClick={this.props.onClick} + onSelect={this.props.onSelect} selected={this.props.selected} togglePopup={this.togglePopup} /> diff --git a/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx b/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx index dc3166bab0f..96517b9dc68 100644 --- a/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx +++ b/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx @@ -52,7 +52,7 @@ describe('rendering', () => { const { ui } = getPageObject(); const issue = mockIssue(true, { effort: '2 days', message: 'This is an issue' }); const onClick = jest.fn(); - renderIssue({ issue, onClick }); + renderIssue({ issue, onSelect: onClick }); expect(ui.effort('2 days').get()).toBeInTheDocument(); await ui.clickIssueMessage(); @@ -432,7 +432,11 @@ function renderIssue(props: Partial, { - currentUser: mockLoggedInUser({ login: 'leia', name: 'Organa' }), - }); + return renderApp( + '/', + , + { + currentUser: mockLoggedInUser({ login: 'leia', name: 'Organa' }), + } + ); } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx index 3230d4c9621..76fa0f85231 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx @@ -162,7 +162,7 @@ export default function IssueActionsBar(props: Props) { toggleAssign(true)} - onMenuClose={handleClose} - isDiscreet - controlLabel={controlLabel} - tooShortText={translateWithParameters('search.tooShort', String(minSearchLength))} - placeholder={translate('search.search_for_users')} - aria-label={translate('search.search_for_users')} - /> +
+ toggleAssign(true)} + onMenuClose={handleClose} + isDiscreet + controlLabel={controlLabel} + tooShortText={translateWithParameters('search.tooShort', String(minSearchLength))} + placeholder={translate('search.search_for_users')} + aria-label={translate('search.search_for_users')} + zLevel={PopupZLevel.Absolute} + /> +
); } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.tsx index d3e4627b93e..17ccba55a76 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.tsx @@ -29,7 +29,7 @@ interface Props { canComment: boolean; commentAutoTriggered?: boolean; commentPlaceholder: string; - currentPopup?: string; + currentPopup?: boolean; issueKey: string; onChange: (issue: Issue) => void; toggleComment: (open?: boolean, placeholder?: string, autoTriggered?: boolean) => void; @@ -69,7 +69,7 @@ export default class IssueCommentAction extends React.PureComponent { void; issue: Issue; branchLike?: BranchLike; displayWhyIsThisAnIssue?: boolean; @@ -46,10 +46,15 @@ export default function IssueMessage(props: IssueMessageProps) { why: '1', }); + const issueUrl = getComponentIssuesUrl(issue.project, { + ...getBranchLikeQuery(branchLike), + open: issue.key, + types: issue.type === IssueType.SecurityHotspot ? issue.type : undefined, + }); return ( <> - {props.onClick ? ( - + {issueUrl?.pathname ? ( + ) : ( diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx index 36e52814a81..8959b7fb4c3 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx @@ -28,7 +28,6 @@ import IssueTags from './IssueTags'; export interface IssueTitleBarProps { currentPopup?: string; branchLike?: BranchLike; - onClick?: () => void; displayWhyIsThisAnIssue?: boolean; issue: Issue; onChange: (issue: Issue) => void; @@ -46,7 +45,6 @@ export default function IssueTitleBar(props: IssueTitleBarProps) { issue={issue} branchLike={props.branchLike} displayWhyIsThisAnIssue={displayWhyIsThisAnIssue} - onClick={props.onClick} />
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx index ca7250d3af1..23a6fd27509 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx @@ -38,6 +38,7 @@ interface Props { onAssign: (login: string) => void; onChange: (issue: Issue) => void; onCheck?: (issue: string) => void; + onSelect: (issueKey: string) => void; onClick?: (issueKey: string) => void; selected: boolean; togglePopup: (popup: string, show: boolean | void) => void; @@ -49,14 +50,14 @@ export default class IssueView extends React.PureComponent { componentDidMount() { const { selected } = this.props; if (this.nodeRef && selected) { - this.nodeRef.scrollIntoView({ block: 'center', inline: 'center' }); + this.nodeRef.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }); } } componentDidUpdate(prevProps: Props) { const { selected } = this.props; if (!prevProps.selected && selected && this.nodeRef) { - this.nodeRef.scrollIntoView({ block: 'center', inline: 'center' }); + this.nodeRef.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }); } } @@ -66,19 +67,6 @@ export default class IssueView extends React.PureComponent { } }; - handleBoxClick = (event: React.MouseEvent) => { - if (!isClickable(event.target as HTMLElement) && this.props.onClick) { - event.preventDefault(); - this.handleDetailClick(); - } - }; - - handleDetailClick = () => { - if (this.props.onClick) { - this.props.onClick(this.props.issue.key); - } - }; - editComment = (comment: string, text: string) => { updateIssue(this.props.onChange, editIssueComment({ comment, text })); }; @@ -102,6 +90,7 @@ export default class IssueView extends React.PureComponent { return ( this.props.onSelect(issue.key)} className={issueClass} role="region" aria-label={issue.message} @@ -120,7 +109,6 @@ export default class IssueView extends React.PureComponent { { } } -function isClickable(node: HTMLElement | undefined | null): boolean { - if (!node) { - return false; - } - const clickableTags = ['A', 'BUTTON', 'INPUT', 'TEXTAREA']; - const tagName = (node.tagName || '').toUpperCase(); - return clickableTags.includes(tagName) || isClickable(node.parentElement); -} - const IssueItem = styled.li` box-sizing: border-box; border: ${themeBorder('default', 'transparent')};