radioPanelGroup.tsx 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import * as React from 'react';
  2. import styled from '@emotion/styled';
  3. import Radio from 'sentry/components/radio';
  4. import space from 'sentry/styles/space';
  5. type RadioPanelGroupProps<C extends string> = {
  6. /**
  7. * An array of [id, name]
  8. */
  9. choices: [C, React.ReactNode, React.ReactNode?][];
  10. label: string;
  11. onChange: (id: C, e: React.FormEvent<HTMLInputElement>) => void;
  12. value: string | null;
  13. };
  14. type Props<C extends string> = RadioPanelGroupProps<C> &
  15. Omit<React.HTMLAttributes<HTMLDivElement>, keyof RadioPanelGroupProps<C>>;
  16. const RadioPanelGroup = <C extends string>({
  17. value,
  18. choices,
  19. label,
  20. onChange,
  21. ...props
  22. }: Props<C>) => (
  23. <Container {...props} role="radiogroup" aria-labelledby={label}>
  24. {(choices || []).map(([id, name, extraContent], index) => (
  25. <RadioPanel key={index}>
  26. <RadioLineItem role="radio" index={index} aria-checked={value === id}>
  27. <Radio
  28. radioSize="small"
  29. aria-label={id}
  30. checked={value === id}
  31. onChange={(e: React.FormEvent<HTMLInputElement>) => onChange(id, e)}
  32. />
  33. <div>{name}</div>
  34. {extraContent}
  35. </RadioLineItem>
  36. </RadioPanel>
  37. ))}
  38. </Container>
  39. );
  40. export default RadioPanelGroup;
  41. const Container = styled('div')`
  42. display: grid;
  43. gap: ${space(1)};
  44. grid-auto-flow: row;
  45. grid-auto-rows: max-content;
  46. grid-auto-columns: auto;
  47. `;
  48. const RadioLineItem = styled('label')<{
  49. index: number;
  50. }>`
  51. display: grid;
  52. gap: ${space(0.25)} ${space(1)};
  53. grid-template-columns: max-content auto max-content;
  54. align-items: center;
  55. cursor: pointer;
  56. outline: none;
  57. font-weight: normal;
  58. margin: 0;
  59. color: ${p => p.theme.subText};
  60. transition: color 0.3s ease-in;
  61. padding: 0;
  62. position: relative;
  63. &:hover,
  64. &:focus {
  65. color: ${p => p.theme.textColor};
  66. }
  67. svg {
  68. display: none;
  69. opacity: 0;
  70. }
  71. &[aria-checked='true'] {
  72. color: ${p => p.theme.textColor};
  73. }
  74. `;
  75. const RadioPanel = styled('div')`
  76. margin: 0;
  77. `;