You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IssueTransition.tsx 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import styled from '@emotion/styled';
  21. import {
  22. Dropdown,
  23. DropdownMenuWrapper,
  24. ItemDivider,
  25. PopupPlacement,
  26. PopupZLevel,
  27. SearchSelectDropdownControl,
  28. } from 'design-system';
  29. import * as React from 'react';
  30. import { addIssueComment, setIssueTransition } from '../../../api/issues';
  31. import { SESSION_STORAGE_TRANSITION_GUIDE_KEY } from '../../../apps/issues/components/IssueNewStatusAndTransitionGuide';
  32. import { translate, translateWithParameters } from '../../../helpers/l10n';
  33. import { Issue } from '../../../types/types';
  34. import StatusHelper from '../../shared/StatusHelper';
  35. import { updateIssue } from '../actions';
  36. import { IssueTransitionOverlay } from './IssueTransitionOverlay';
  37. interface Props {
  38. isOpen: boolean;
  39. issue: Pick<Issue, 'key' | 'resolution' | 'issueStatus' | 'transitions' | 'type' | 'actions'>;
  40. onChange: (issue: Issue) => void;
  41. togglePopup: (popup: string, show?: boolean) => void;
  42. }
  43. export default function IssueTransition(props: Readonly<Props>) {
  44. const { isOpen, issue, onChange, togglePopup } = props;
  45. const guideStepIndex = +(sessionStorage.getItem(SESSION_STORAGE_TRANSITION_GUIDE_KEY) ?? 0);
  46. const guideIsRunning = sessionStorage.getItem(SESSION_STORAGE_TRANSITION_GUIDE_KEY) !== null;
  47. const [transitioning, setTransitioning] = React.useState(false);
  48. async function handleSetTransition(transition: string, comment?: string) {
  49. setTransitioning(true);
  50. try {
  51. if (typeof comment === 'string' && comment.length > 0) {
  52. await setIssueTransition({ issue: issue.key, transition });
  53. await updateIssue(onChange, addIssueComment({ issue: issue.key, text: comment }));
  54. } else {
  55. await updateIssue(onChange, setIssueTransition({ issue: issue.key, transition }));
  56. }
  57. togglePopup('transition', false);
  58. } finally {
  59. setTransitioning(false);
  60. }
  61. }
  62. function handleClose() {
  63. togglePopup('transition', false);
  64. }
  65. function onToggleClick() {
  66. togglePopup('transition', !isOpen);
  67. }
  68. if (issue.transitions?.length) {
  69. return (
  70. <StyledDropdown
  71. allowResizing
  72. closeOnClick={false}
  73. id="issue-transition"
  74. onClose={handleClose}
  75. openDropdown={isOpen}
  76. withClickOutHandler={!guideIsRunning}
  77. withFocusOutHandler={!guideIsRunning}
  78. overlay={
  79. <IssueTransitionOverlay
  80. issue={issue}
  81. onClose={handleClose}
  82. onSetTransition={handleSetTransition}
  83. loading={transitioning}
  84. guideStepIndex={guideStepIndex}
  85. />
  86. }
  87. placement={PopupPlacement.Bottom}
  88. zLevel={PopupZLevel.Absolute}
  89. size="medium"
  90. >
  91. {({ a11yAttrs }) => (
  92. <SearchSelectDropdownControl
  93. {...a11yAttrs}
  94. onClick={onToggleClick}
  95. onClear={handleClose}
  96. isDiscreet
  97. className="it__issue-transition sw-px-1"
  98. label={
  99. <StatusHelper className="sw-flex sw-items-center" issueStatus={issue.issueStatus} />
  100. }
  101. ariaLabel={translateWithParameters(
  102. 'issue.transition.status_x_click_to_change',
  103. translate('issue.issue_status', issue.issueStatus),
  104. )}
  105. />
  106. )}
  107. </StyledDropdown>
  108. );
  109. }
  110. return <StatusHelper issueStatus={issue.issueStatus} />;
  111. }
  112. const StyledDropdown = styled(Dropdown)`
  113. overflow: auto;
  114. & ${DropdownMenuWrapper} {
  115. border-radius: 8px;
  116. ${ItemDivider} {
  117. margin-left: 0;
  118. margin-right: 0;
  119. }
  120. }
  121. `;