selectOption.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import {Fragment} from 'react';
  2. import {components as selectComponents} from 'react-select';
  3. import {ClassNames} from '@emotion/react';
  4. import styled from '@emotion/styled';
  5. import MenuListItem from 'sentry/components/menuListItem';
  6. import Tooltip from 'sentry/components/tooltip';
  7. import {IconCheckmark} from 'sentry/icons';
  8. import {defined} from 'sentry/utils';
  9. type Props = React.ComponentProps<typeof selectComponents.Option>;
  10. // We still have some tests that find select options by the display name "Option".
  11. MenuListItem.displayName = 'Option';
  12. function SelectOption(props: Props) {
  13. const {
  14. label,
  15. data,
  16. selectProps,
  17. isMulti,
  18. isSelected,
  19. isFocused,
  20. isDisabled,
  21. innerProps,
  22. innerRef,
  23. } = props;
  24. const {showDividers} = selectProps;
  25. const {
  26. value,
  27. tooltip,
  28. tooltipOptions = {delay: 500},
  29. selectionMode,
  30. priority,
  31. ...itemProps
  32. } = data;
  33. const isMultiple = defined(selectionMode) ? selectionMode === 'multiple' : isMulti;
  34. // Unless the priority prop is explicitly defined, use 'primary' for
  35. // selected items in single-selection menus and 'default' for the rest.
  36. const itemPriority = priority ?? (isSelected && !isMultiple ? 'primary' : 'default');
  37. return (
  38. <ClassNames>
  39. {({cx}) => (
  40. <Tooltip skipWrapper title={tooltip} {...tooltipOptions}>
  41. <MenuListItem
  42. {...itemProps}
  43. {...innerProps}
  44. ref={innerRef}
  45. className={cx({
  46. option: true,
  47. 'option--is-disabled': isDisabled,
  48. 'option--is-focused': isFocused,
  49. 'option--is-selected': isSelected,
  50. })}
  51. as="div"
  52. value={value}
  53. label={label}
  54. isDisabled={isDisabled}
  55. isFocused={isFocused}
  56. showDivider={showDividers}
  57. priority={itemPriority}
  58. innerWrapProps={{'data-test-id': value}}
  59. labelProps={{as: typeof label === 'string' ? 'p' : 'div'}}
  60. leadingItems={
  61. <Fragment>
  62. <CheckWrap isMultiple={isMultiple} isSelected={isSelected}>
  63. {isSelected && (
  64. <IconCheckmark
  65. size={isMultiple ? 'xs' : 'sm'}
  66. color={isMultiple ? 'white' : undefined}
  67. />
  68. )}
  69. </CheckWrap>
  70. {data.leadingItems}
  71. </Fragment>
  72. }
  73. />
  74. </Tooltip>
  75. )}
  76. </ClassNames>
  77. );
  78. }
  79. export default SelectOption;
  80. const CheckWrap = styled('div')<{isMultiple: boolean; isSelected: boolean}>`
  81. display: flex;
  82. justify-content: center;
  83. align-items: center;
  84. ${p =>
  85. p.isMultiple
  86. ? `
  87. width: 1em;
  88. height: 1em;
  89. padding: 1px;
  90. border: solid 1px ${p.theme.border};
  91. background: ${p.theme.backgroundElevated};
  92. border-radius: 2px;
  93. box-shadow: inset ${p.theme.dropShadowLight};
  94. ${
  95. p.isSelected &&
  96. `
  97. background: ${p.theme.purple300};
  98. border-color: ${p.theme.purple300};
  99. `
  100. }
  101. `
  102. : `
  103. width: 1em;
  104. height: 1.4em;
  105. padding-bottom: 1px;
  106. `}
  107. `;