Browse Source

SONAR-19345 - Fixed several issues MUI

tags/10.1.0.73491
Kevin Silva 11 months ago
parent
commit
77eb3f8ecf

+ 3
- 1
server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx View File

@@ -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<
</SearchHighlighterContext.Provider>
}
placement={PopupPlacement.BottomLeft}
zLevel={PopupZLevel.Global}
zLevel={zLevel}
>
<SearchSelectDropdownControl
ariaLabel={controlAriaLabel}

+ 3
- 2
server/sonar-web/design-system/src/components/popups.tsx View File

@@ -24,7 +24,7 @@ import React, { AriaRole } from 'react';
import { createPortal, findDOMNode } from 'react-dom';
import tw from 'twin.macro';
import { THROTTLE_SCROLL_DELAY } from '../helpers/constants';
import { PopupPlacement, popupPositioning, PopupZLevel } from '../helpers/positioning';
import { PopupPlacement, PopupZLevel, popupPositioning } from '../helpers/positioning';
import { themeBorder, themeColor, themeContrast, themeShadow } from '../helpers/theme';
import ClickEventBoundary from './ClickEventBoundary';

@@ -129,7 +129,7 @@ export class Popup extends React.PureComponent<PopupProps, State> {
};

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,

+ 1
- 0
server/sonar-web/design-system/src/helpers/positioning.ts View File

@@ -52,6 +52,7 @@ export enum PopupZLevel {
Content = 'content',
Default = 'popup',
Global = 'global',
Absolute = 'absolute',
}

export type BasePlacement = Extract<

+ 6
- 11
server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx View File

@@ -69,19 +69,14 @@ export default class IssueHeader extends React.PureComponent<Props, State> {
}

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 };
});
};


+ 9
- 0
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx View File

@@ -430,6 +430,14 @@ export class App extends React.PureComponent<Props, State> {
}
};

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<Props, State> {
onIssueChange={this.handleIssueChange}
onIssueCheck={currentUser.isLoggedIn ? this.handleIssueCheck : undefined}
onIssueClick={this.openIssue}
onIssueSelect={this.selectIssue}
onPopupToggle={this.handlePopupToggle}
openPopup={this.state.openPopup}
selectedIssue={selectedIssue}

+ 2
- 0
server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx View File

@@ -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<Props, State> {
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}

+ 2
- 0
server/sonar-web/src/main/js/apps/issues/components/ListItem.tsx View File

@@ -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<Query>) => void;
onPopupToggle: (issue: string, popupName: string, open?: boolean) => void;
openPopup: string | undefined;
@@ -93,6 +94,7 @@ export default class ListItem extends React.PureComponent<Props> {
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}

+ 1
- 1
server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx View File

@@ -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}

+ 1
- 1
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap View File

@@ -49,8 +49,8 @@ exports[`should render issues 1`] = `
}
key="issue"
onChange={[MockFunction]}
onClick={[MockFunction]}
onPopupToggle={[MockFunction]}
onSelect={[MockFunction]}
selected={true}
/>
</div>

+ 2
- 0
server/sonar-web/src/main/js/components/issue/Issue.tsx View File

@@ -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<Props> {
onChange={this.props.onChange}
onCheck={this.props.onCheck}
onClick={this.props.onClick}
onSelect={this.props.onSelect}
selected={this.props.selected}
togglePopup={this.togglePopup}
/>

+ 8
- 4
server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx View File

@@ -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<Omit<Issue['props'], 'onChange' | 'onPopupTo
);
}

return renderApp('/', <Wrapper issue={mockIssue()} selected={false} {...props} />, {
currentUser: mockLoggedInUser({ login: 'leia', name: 'Organa' }),
});
return renderApp(
'/',
<Wrapper onSelect={jest.fn()} issue={mockIssue()} selected={false} {...props} />,
{
currentUser: mockLoggedInUser({ login: 'leia', name: 'Organa' }),
}
);
}

+ 1
- 1
server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx View File

@@ -162,7 +162,7 @@ export default function IssueActionsBar(props: Props) {
<IssueCommentAction
commentAutoTriggered={commentState.commentAutoTriggered}
commentPlaceholder={commentState.commentPlaceholder}
currentPopup={currentPopup}
currentPopup={currentPopup === 'comment'}
issueKey={issue.key}
onChange={onChange}
toggleComment={toggleComment}

+ 25
- 22
server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx View File

@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { LabelValueSelectOption, SearchSelectDropdown } from 'design-system';
import { LabelValueSelectOption, PopupZLevel, SearchSelectDropdown } from 'design-system';
import * as React from 'react';
import { Options, SingleValue } from 'react-select';
import { searchUsers } from '../../../api/users';
@@ -132,26 +132,29 @@ export default function IssueAssignee(props: Props) {
}

return (
<SearchSelectDropdown
size="medium"
className="it__issue-assign"
controlAriaLabel={
assinedUser
? translateWithParameters('issue.assign.assigned_to_x_click_to_change', assinedUser)
: translate('issue.assign.unassigned_click_to_assign')
}
defaultOptions={defaultOptions}
onChange={handleAssign}
loadOptions={handleSearchAssignees}
menuIsOpen={props.isOpen}
minLength={minSearchLength}
onMenuOpen={() => 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')}
/>
<div className="sw-relative">
<SearchSelectDropdown
size="medium"
className="it__issue-assign"
controlAriaLabel={
assinedUser
? translateWithParameters('issue.assign.assigned_to_x_click_to_change', assinedUser)
: translate('issue.assign.unassigned_click_to_assign')
}
defaultOptions={defaultOptions}
onChange={handleAssign}
loadOptions={handleSearchAssignees}
menuIsOpen={props.isOpen}
minLength={minSearchLength}
onMenuOpen={() => 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}
/>
</div>
);
}

+ 2
- 2
server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.tsx View File

@@ -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<Props> {
<Toggler
closeOnClickOutside={false}
onRequestClose={this.handleClose}
open={this.props.currentPopup === 'comment'}
open={!!this.props.currentPopup}
overlay={
showCommentsInPopup ? (
<CommentListPopup

+ 8
- 3
server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx View File

@@ -23,11 +23,11 @@ import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import { getComponentIssuesUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
import { IssueType } from '../../../types/issues';
import { Issue } from '../../../types/types';
import { IssueMessageHighlighting } from '../IssueMessageHighlighting';

export interface IssueMessageProps {
onClick?: () => 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 ? (
<StandoutLink onClick={props.onClick} className="it__issue-message" preventDefault to={{}}>
{issueUrl?.pathname ? (
<StandoutLink className="it__issue-message" to={issueUrl}>
<IssueMessageHighlighting message={message} messageFormattings={messageFormattings} />
</StandoutLink>
) : (

+ 0
- 2
server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx View File

@@ -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}
/>
</div>
<div className="js-issue-tags sw-body-sm sw-grow-0 sw-whitespace-nowrap">

+ 4
- 25
server/sonar-web/src/main/js/components/issue/components/IssueView.tsx View File

@@ -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<Props> {
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<Props> {
}
};

handleBoxClick = (event: React.MouseEvent<HTMLDivElement>) => {
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<Props> {

return (
<IssueItem
onClick={() => this.props.onSelect(issue.key)}
className={issueClass}
role="region"
aria-label={issue.message}
@@ -120,7 +109,6 @@ export default class IssueView extends React.PureComponent<Props> {
<IssueTitleBar
currentPopup={currentPopup}
branchLike={branchLike}
onClick={this.handleDetailClick}
displayWhyIsThisAnIssue={displayWhyIsThisAnIssue}
issue={issue}
onChange={this.props.onChange}
@@ -141,15 +129,6 @@ export default class IssueView extends React.PureComponent<Props> {
}
}

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')};

Loading…
Cancel
Save