index.stories.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import {Fragment, useCallback, useState} from 'react';
  2. import {Location} from 'history';
  3. import {Button} from 'sentry/components/button';
  4. import GridEditable, {GridColumnOrder} from 'sentry/components/gridEditable';
  5. import useQueryBasedColumnResize from 'sentry/components/replays/useQueryBasedColumnResize';
  6. import JSXNode from 'sentry/components/stories/jsxNode';
  7. import JSXProperty from 'sentry/components/stories/jsxProperty';
  8. import Matrix from 'sentry/components/stories/matrix';
  9. import SideBySide from 'sentry/components/stories/sideBySide';
  10. import {backend, frontend} from 'sentry/data/platformCategories';
  11. import storyBook from 'sentry/stories/storyBook';
  12. import {useLocation} from 'sentry/utils/useLocation';
  13. interface ExampleDataItem {
  14. category: 'frontend' | 'backend';
  15. name: string;
  16. }
  17. export default storyBook(GridEditable, story => {
  18. const columns: GridColumnOrder<keyof ExampleDataItem>[] = [
  19. {key: 'category', name: 'Platform Category'},
  20. {key: 'name', name: 'Platform Name'},
  21. ];
  22. const data: ExampleDataItem[] = [
  23. ...frontend.slice(0, 3).map(name => ({name, category: 'frontend' as const})),
  24. ...backend.slice(0, 3).map(name => ({name, category: 'backend' as const})),
  25. ];
  26. const mockLocation: Location = {
  27. key: '',
  28. search: '',
  29. hash: '',
  30. action: 'PUSH',
  31. state: null,
  32. query: {},
  33. pathname: '/mock-pathname/',
  34. };
  35. story('Minimal', () => {
  36. return (
  37. <GridEditable
  38. data={[]}
  39. columnOrder={columns}
  40. columnSortBy={[]}
  41. grid={{}}
  42. location={mockLocation}
  43. />
  44. );
  45. });
  46. const columnsWithWidth: GridColumnOrder<keyof ExampleDataItem | 'other'>[] =
  47. columns.map(col => {
  48. col.width = 200;
  49. return col;
  50. });
  51. columnsWithWidth.push({key: 'other', name: 'Other', width: 200});
  52. const renderHeadCell = (column: GridColumnOrder, columnIndex: number) =>
  53. `#${columnIndex} ${column.name}`;
  54. const renderBodyCell = (
  55. column: GridColumnOrder<keyof ExampleDataItem | 'other'>,
  56. dataRow: ExampleDataItem,
  57. rowIndex: number,
  58. columnIndex: number
  59. ) =>
  60. column.key in dataRow
  61. ? dataRow[column.key]
  62. : JSON.stringify({column, dataRow, rowIndex, columnIndex});
  63. story('Basic', () => {
  64. return (
  65. <Fragment>
  66. <p>
  67. By default the column widths are resizable, but will reset frequently unless you
  68. persist them somehow.
  69. </p>
  70. <GridEditable
  71. data={data}
  72. columnOrder={columnsWithWidth}
  73. columnSortBy={[]}
  74. grid={{
  75. renderHeadCell,
  76. renderBodyCell,
  77. }}
  78. location={mockLocation}
  79. />
  80. </Fragment>
  81. );
  82. });
  83. story('Props', () => (
  84. <SideBySide>
  85. <div>
  86. <p>
  87. <JSXNode name="GridEditable" props={{error: String}} />
  88. </p>
  89. <GridEditable
  90. error="An error happened"
  91. data={data}
  92. columnOrder={columns}
  93. columnSortBy={[]}
  94. grid={{}}
  95. location={mockLocation}
  96. />
  97. </div>
  98. <div>
  99. <p>
  100. <JSXNode name="GridEditable" props={{isLoading: true}} />
  101. </p>
  102. <GridEditable
  103. isLoading
  104. data={data}
  105. columnOrder={columns}
  106. columnSortBy={[]}
  107. grid={{}}
  108. location={mockLocation}
  109. />
  110. </div>
  111. </SideBySide>
  112. ));
  113. function useStatefulColumnWidths() {
  114. const [columnsWithDynamicWidths, setColumns] =
  115. useState<GridColumnOrder<keyof ExampleDataItem | 'other'>[]>(columnsWithWidth);
  116. const handleResizeColumn = useCallback(
  117. (
  118. columnIndex: number,
  119. nextColumn: GridColumnOrder<keyof ExampleDataItem | 'other'>
  120. ) => {
  121. setColumns(prev => {
  122. const next = [...prev];
  123. next[columnIndex] = nextColumn;
  124. return next;
  125. });
  126. },
  127. []
  128. );
  129. return {
  130. columns: columnsWithDynamicWidths,
  131. handleResizeColumn,
  132. };
  133. }
  134. story('Column Resize', () => {
  135. const statefulColumnResize = useStatefulColumnWidths();
  136. const location = useLocation();
  137. const queryBasedColumnResize = useQueryBasedColumnResize({
  138. columns: columnsWithWidth,
  139. paramName: 'width',
  140. location,
  141. });
  142. return (
  143. <Fragment>
  144. <p>
  145. You can keep track of the column widths by implementing the{' '}
  146. <JSXProperty name="onResizeColumn" value={Function} /> callback.
  147. </p>
  148. <SideBySide>
  149. <div>
  150. <p>In this example we are saving the column widths to state.</p>
  151. <GridEditable
  152. data={data}
  153. columnOrder={statefulColumnResize.columns}
  154. columnSortBy={[]}
  155. grid={{
  156. renderHeadCell,
  157. renderBodyCell,
  158. onResizeColumn: statefulColumnResize.handleResizeColumn,
  159. }}
  160. location={mockLocation}
  161. />
  162. </div>
  163. <div>
  164. <p>
  165. In this example we are using <kbd>useQueryBasedColumnResize</kbd>. Notice
  166. how the url updates after you drag columns.
  167. </p>
  168. <GridEditable
  169. data={data}
  170. columnOrder={queryBasedColumnResize.columns}
  171. columnSortBy={[]}
  172. grid={{
  173. renderHeadCell,
  174. renderBodyCell,
  175. onResizeColumn: queryBasedColumnResize.handleResizeColumn,
  176. }}
  177. location={mockLocation}
  178. />
  179. </div>
  180. </SideBySide>
  181. </Fragment>
  182. );
  183. });
  184. story('Fixed Height', () => (
  185. <GridEditable
  186. data={data}
  187. columnOrder={columns}
  188. columnSortBy={[]}
  189. grid={{
  190. renderHeadCell,
  191. renderBodyCell,
  192. }}
  193. location={mockLocation}
  194. height={200}
  195. stickyHeader
  196. />
  197. ));
  198. story('Header Augmentations', () => (
  199. <Matrix
  200. render={GridEditable}
  201. propMatrix={{
  202. data: [data],
  203. columnOrder: [columns],
  204. columnSortBy: [[]],
  205. grid: [{}],
  206. location: [mockLocation],
  207. headerButtons: [undefined, () => <Button>Take Action</Button>],
  208. title: [undefined, 'GridEditable Title'],
  209. }}
  210. selectedProps={['title', 'headerButtons']}
  211. sizingWindowProps={{display: 'block'}}
  212. />
  213. ));
  214. });