index.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import {useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import ActivityItem, {ActivityAuthorType} from 'sentry/components/activity/item';
  4. import space from 'sentry/styles/space';
  5. import {User} from 'sentry/types';
  6. import {NoteType} from 'sentry/types/alerts';
  7. import {ActivityType} from 'sentry/views/alerts/types';
  8. import NoteBody from './body';
  9. import EditorTools from './editorTools';
  10. import NoteHeader from './header';
  11. import NoteInput from './input';
  12. type Props = {
  13. /**
  14. * String for author name to be displayed in header.
  15. *
  16. * This is not completely derived from `props.user` because we can set a
  17. * default from parent component
  18. */
  19. authorName: string;
  20. dateCreated: Date | string;
  21. /**
  22. * min-height for NoteInput textarea
  23. */
  24. minHeight: number;
  25. /**
  26. * This is the id of the note object from the server. This is to indicate you
  27. * are editing an existing item
  28. */
  29. modelId: string;
  30. onDelete: (props: Props) => void;
  31. onUpdate: (data: NoteType, props: Props) => void;
  32. /**
  33. * If used, will fetch list of teams/members that can be mentioned for projects
  34. */
  35. projectSlugs: string[];
  36. /**
  37. * Pass through to ActivityItem. Shows absolute time instead of a relative
  38. * string
  39. */
  40. showTime: boolean;
  41. /**
  42. * The note text itself
  43. */
  44. text: string;
  45. user: User;
  46. /**
  47. * This is unusual usage that Alert Details uses to get back the activity
  48. * that an input was bound to as the onUpdate and onDelete actions forward
  49. * this component's props.
  50. */
  51. activity?: ActivityType;
  52. /**
  53. * pass through to ActivityItem. Hides the date/timestamp in header
  54. */
  55. hideDate?: boolean;
  56. onCreate?: (data: NoteType) => void;
  57. };
  58. function Note(props: Props) {
  59. const [editing, setEditing] = useState(false);
  60. const {
  61. modelId,
  62. user,
  63. dateCreated,
  64. text,
  65. authorName,
  66. hideDate,
  67. minHeight,
  68. showTime,
  69. projectSlugs,
  70. onDelete,
  71. onCreate,
  72. onUpdate,
  73. } = props;
  74. const activityItemProps = {
  75. hideDate,
  76. showTime,
  77. id: `activity-item-${modelId}`,
  78. author: {
  79. type: 'user' as ActivityAuthorType,
  80. user,
  81. },
  82. date: dateCreated,
  83. };
  84. if (!editing) {
  85. const header = (
  86. <NoteHeader
  87. {...{authorName, user}}
  88. onEdit={() => setEditing(true)}
  89. onDelete={() => onDelete(props)}
  90. />
  91. );
  92. return (
  93. <ActivityItemWithEditing {...activityItemProps} header={header}>
  94. <NoteBody text={text} />
  95. </ActivityItemWithEditing>
  96. );
  97. }
  98. // When editing, `NoteInput` has its own header, pass render func to control
  99. // rendering of bubble body
  100. return (
  101. <ActivityItemNote {...activityItemProps}>
  102. {() => (
  103. <NoteInput
  104. {...{modelId, minHeight, text, projectSlugs}}
  105. onEditFinish={() => setEditing(false)}
  106. onUpdate={note => {
  107. onUpdate(note, props);
  108. setEditing(false);
  109. }}
  110. onCreate={note => onCreate?.(note)}
  111. />
  112. )}
  113. </ActivityItemNote>
  114. );
  115. }
  116. const ActivityItemNote = styled(ActivityItem)`
  117. /* this was nested under ".activity-note.activity-bubble" */
  118. ul {
  119. list-style: disc;
  120. }
  121. h1,
  122. h2,
  123. h3,
  124. h4,
  125. p,
  126. ul:not(.nav),
  127. ol,
  128. pre,
  129. hr,
  130. blockquote {
  131. margin-bottom: ${space(2)};
  132. }
  133. ul:not(.nav),
  134. ol {
  135. padding-left: 20px;
  136. }
  137. p {
  138. a {
  139. word-wrap: break-word;
  140. }
  141. }
  142. blockquote {
  143. font-size: 15px;
  144. border-left: 5px solid ${p => p.theme.innerBorder};
  145. padding-left: ${space(1)};
  146. margin-left: 0;
  147. }
  148. `;
  149. const ActivityItemWithEditing = styled(ActivityItemNote)`
  150. &:hover {
  151. ${EditorTools} {
  152. display: inline-block;
  153. }
  154. }
  155. `;
  156. export default Note;