optionSelector.tsx 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import {Fragment, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import FeatureBadge from 'sentry/components/featureBadge';
  4. import CompactSelect from 'sentry/components/forms/compactSelect';
  5. import Truncate from 'sentry/components/truncate';
  6. import {SelectValue} from 'sentry/types';
  7. import {defined} from 'sentry/utils';
  8. type BaseProps = React.ComponentProps<typeof CompactSelect> & {
  9. options: SelectValue<string>[];
  10. title: string;
  11. featureType?: 'alpha' | 'beta' | 'new';
  12. };
  13. type SingleProps = BaseProps & {
  14. onChange: (value: string) => void;
  15. selected: string;
  16. multiple?: false;
  17. };
  18. type MultipleProps = BaseProps & {
  19. multiple: true;
  20. onChange: (value: string[]) => void;
  21. selected: string[];
  22. };
  23. function OptionSelector({
  24. options,
  25. onChange,
  26. selected,
  27. title,
  28. featureType,
  29. multiple,
  30. ...rest
  31. }: SingleProps | MultipleProps) {
  32. const mappedOptions = useMemo(() => {
  33. return options.map(opt => ({
  34. ...opt,
  35. label: <Truncate value={String(opt.label)} maxLength={60} expandDirection="left" />,
  36. }));
  37. }, [options]);
  38. function onValueChange(option) {
  39. onChange(multiple ? option.map(o => o.value) : option.value);
  40. }
  41. function isOptionDisabled(option) {
  42. return (
  43. // Option is explicitly marked as disabled
  44. option.disabled ||
  45. // The user has reached the maximum number of selections (3), and the option hasn't
  46. // yet been selected. These options should be disabled to visually indicate that the
  47. // user has reached the max.
  48. (multiple && selected.length === 3 && !selected.includes(option.value))
  49. );
  50. }
  51. return (
  52. <CompactSelect
  53. options={mappedOptions}
  54. value={selected}
  55. onChange={onValueChange}
  56. isOptionDisabled={isOptionDisabled}
  57. multiple={multiple}
  58. triggerProps={{
  59. size: 'small',
  60. borderless: true,
  61. prefix: (
  62. <Fragment>
  63. {title}
  64. {defined(featureType) ? <StyledFeatureBadge type={featureType} /> : null}
  65. </Fragment>
  66. ),
  67. }}
  68. placement="bottom right"
  69. {...rest}
  70. />
  71. );
  72. }
  73. const StyledFeatureBadge = styled(FeatureBadge)`
  74. margin-left: 0px;
  75. `;
  76. export default OptionSelector;