rulesPanel.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import TextareaAutosize from 'react-autosize-textarea';
  2. import styled from '@emotion/styled';
  3. import moment from 'moment';
  4. import {Panel, PanelBody, PanelHeader} from 'sentry/components/panels';
  5. import {IconGithub, IconGitlab, IconSentry} from 'sentry/icons';
  6. import {inputStyles} from 'sentry/styles/input';
  7. import space from 'sentry/styles/space';
  8. type Props = {
  9. 'data-test-id': string;
  10. dateUpdated: string | null;
  11. raw: string;
  12. type: 'codeowners' | 'issueowners';
  13. controls?: React.ReactNode[];
  14. placeholder?: string;
  15. provider?: string;
  16. repoName?: string;
  17. };
  18. function RulesPanel({
  19. raw,
  20. dateUpdated,
  21. provider,
  22. repoName,
  23. type,
  24. placeholder,
  25. controls,
  26. ['data-test-id']: dataTestId,
  27. }: Props) {
  28. function renderIcon() {
  29. switch (provider ?? '') {
  30. case 'github':
  31. return <IconGithub size="md" />;
  32. case 'gitlab':
  33. return <IconGitlab size="md" />;
  34. default:
  35. return <IconSentry size="md" />;
  36. }
  37. }
  38. function renderTitle() {
  39. switch (type) {
  40. case 'codeowners':
  41. return 'CODEOWNERS';
  42. case 'issueowners':
  43. return 'Ownership Rules';
  44. default:
  45. return null;
  46. }
  47. }
  48. return (
  49. <Panel data-test-id={dataTestId}>
  50. <PanelHeader>
  51. {[
  52. <Container key="title">
  53. {renderIcon()}
  54. <Title>{renderTitle()}</Title>
  55. {repoName && <Repository>{`- ${repoName}`}</Repository>}
  56. </Container>,
  57. <Container key="control">
  58. <SyncDate>
  59. {dateUpdated && `Last synced ${moment(dateUpdated).fromNow()}`}
  60. </SyncDate>
  61. <Controls>
  62. {(controls || []).map((c, n) => (
  63. <span key={n}> {c}</span>
  64. ))}
  65. </Controls>
  66. </Container>,
  67. ]}
  68. </PanelHeader>
  69. <PanelBody>
  70. <InnerPanelBody>
  71. <StyledTextArea
  72. value={raw}
  73. spellCheck="false"
  74. autoComplete="off"
  75. autoCorrect="off"
  76. autoCapitalize="off"
  77. placeholder={placeholder}
  78. />
  79. </InnerPanelBody>
  80. </PanelBody>
  81. </Panel>
  82. );
  83. }
  84. export default RulesPanel;
  85. const Container = styled('div')`
  86. display: flex;
  87. align-items: center;
  88. text-transform: none;
  89. `;
  90. const Title = styled('div')`
  91. padding: 0 ${space(0.5)} 0 ${space(1)};
  92. font-size: initial;
  93. `;
  94. const Repository = styled('div')``;
  95. const InnerPanelBody = styled(PanelBody)`
  96. height: auto;
  97. `;
  98. const StyledTextArea = styled(TextareaAutosize)`
  99. ${p => inputStyles(p)};
  100. height: 350px !important;
  101. overflow: auto;
  102. outline: 0;
  103. width: 100%;
  104. resize: none;
  105. margin: 0;
  106. font-family: ${p => p.theme.text.familyMono};
  107. word-break: break-all;
  108. white-space: pre-wrap;
  109. line-height: ${space(3)};
  110. border: none;
  111. box-shadow: none;
  112. padding: ${space(2)};
  113. color: transparent;
  114. text-shadow: 0 0 0 #9386a0;
  115. &:hover,
  116. &:focus,
  117. &:active {
  118. border: none;
  119. box-shadow: none;
  120. }
  121. `;
  122. const SyncDate = styled('div')`
  123. padding: 0 ${space(1)};
  124. font-weight: normal;
  125. `;
  126. const Controls = styled('div')`
  127. display: grid;
  128. align-items: center;
  129. gap: ${space(1)};
  130. grid-auto-flow: column;
  131. justify-content: flex-end;
  132. `;