dropdownActions.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import {css} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import DropdownAutoComplete from 'sentry/components/dropdownAutoComplete';
  4. import DropdownButton from 'sentry/components/dropdownButton';
  5. import {Tooltip} from 'sentry/components/tooltip';
  6. import {IconNot} from 'sentry/icons';
  7. import {space} from 'sentry/styles/space';
  8. import {openAdminConfirmModal} from 'admin/components/adminConfirmationModal';
  9. const ActionName = styled('div')`
  10. display: flex;
  11. align-items: center;
  12. gap: ${space(0.5)};
  13. `;
  14. const ActionLabel = styled('div')<{isDisabled: boolean}>`
  15. width: 350px;
  16. ${p =>
  17. p.isDisabled &&
  18. css`
  19. color: ${p.theme.subText};
  20. svg {
  21. color: ${p.theme.red200};
  22. }
  23. `}
  24. `;
  25. const HelpText = styled('div')`
  26. font-size: ${p => p.theme.fontSizeSmall};
  27. color: ${p => p.theme.subText};
  28. line-height: 1.2;
  29. `;
  30. type Props = {
  31. actions: Array<{
  32. key: string;
  33. name: string;
  34. onAction: (params: any) => void;
  35. confirmModalOpts?: any;
  36. disabled?: boolean;
  37. disabledReason?: string;
  38. help?: string;
  39. skipConfirmModal?: boolean;
  40. visible?: boolean;
  41. }>;
  42. label?: string;
  43. };
  44. function DropdownActions({actions, label}: Props) {
  45. return (
  46. <DropdownAutoComplete
  47. alignMenu="right"
  48. searchPlaceholder="Filter actions"
  49. noResultsMessage="No actions match your filter"
  50. onSelect={({value}) => {
  51. const action = actions.find(a => a.key === value);
  52. if (action === undefined) {
  53. return;
  54. }
  55. if (action.disabled) {
  56. return;
  57. }
  58. if (action.skipConfirmModal) {
  59. action.onAction({});
  60. return;
  61. }
  62. const {confirmModalOpts} = action;
  63. const {header, modalSpecificContent, showAuditFields} = confirmModalOpts ?? {};
  64. // We provide some defaults for the openAdminConfirmModal call. But
  65. // those defaults may be overridden
  66. openAdminConfirmModal({
  67. ...confirmModalOpts,
  68. header: header ?? <h4>{action.name}</h4>,
  69. modalSpecificContent: modalSpecificContent ?? action.help,
  70. showAuditFields: showAuditFields ?? true,
  71. onConfirm: action.onAction,
  72. });
  73. }}
  74. items={actions
  75. .filter(action => action.visible !== false)
  76. .map(action => {
  77. const actionLabel = (
  78. <ActionLabel
  79. data-test-id={`action-${action.key}`}
  80. isDisabled={!!action.disabled}
  81. aria-disabled={!!action.disabled}
  82. >
  83. <ActionName>
  84. {action.name}
  85. {action.disabled && (
  86. <Tooltip skipWrapper title={action.disabledReason}>
  87. <IconNot size="xs" data-test-id="icon-not" />
  88. </Tooltip>
  89. )}
  90. </ActionName>
  91. {action.help && <HelpText>{action.help}</HelpText>}
  92. </ActionLabel>
  93. );
  94. return {
  95. value: action.key,
  96. searchKey: action.name,
  97. label: actionLabel,
  98. };
  99. })}
  100. >
  101. {({isOpen}) => (
  102. <DropdownButton data-test-id="detail-actions" size="sm" isOpen={isOpen}>
  103. {label}
  104. </DropdownButton>
  105. )}
  106. </DropdownAutoComplete>
  107. );
  108. }
  109. export default DropdownActions;