pdf-preview.stories.jsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import { useCallback, useMemo, useState } from 'react'
  2. import useFetchMock from './hooks/use-fetch-mock'
  3. import { Button } from 'react-bootstrap'
  4. import PdfPreviewPane from '../js/features/pdf-preview/components/pdf-preview-pane'
  5. import PdfPreview from '../js/features/pdf-preview/components/pdf-preview'
  6. import PdfFileList from '../js/features/pdf-preview/components/pdf-file-list'
  7. import { buildFileList } from '../js/features/pdf-preview/util/file-list'
  8. import PdfLogsViewer from '../js/features/pdf-preview/components/pdf-logs-viewer'
  9. import PdfPreviewError from '../js/features/pdf-preview/components/pdf-preview-error'
  10. import PdfPreviewHybridToolbar from '../js/features/pdf-preview/components/pdf-preview-hybrid-toolbar'
  11. import { useDetachCompileContext as useCompileContext } from '../js/shared/context/detach-compile-context'
  12. import {
  13. dispatchDocChanged,
  14. mockBuildFile,
  15. mockClearCache,
  16. mockCompile,
  17. mockCompileError,
  18. mockCompileValidationIssues,
  19. mockEventTracking,
  20. outputFiles,
  21. } from './fixtures/compile'
  22. import { cloneDeep } from 'lodash'
  23. import { ScopeDecorator } from './decorators/scope'
  24. import { PdfPreviewProvider } from '@/features/pdf-preview/components/pdf-preview-provider'
  25. export default {
  26. title: 'Editor / PDF Preview',
  27. component: PdfPreview,
  28. subcomponents: {
  29. PdfPreviewHybridToolbar,
  30. PdfFileList,
  31. PdfPreviewError,
  32. },
  33. decorators: [ScopeDecorator],
  34. }
  35. export const Interactive = () => {
  36. useFetchMock(fetchMock => {
  37. mockCompile(fetchMock)
  38. mockBuildFile(fetchMock)
  39. mockClearCache(fetchMock)
  40. })
  41. const Inner = () => {
  42. const context = useCompileContext()
  43. const { setHasLintingError } = context
  44. const toggleLintingError = useCallback(() => {
  45. setHasLintingError(value => !value)
  46. }, [setHasLintingError])
  47. const values = useMemo(() => {
  48. const entries = Object.entries(context).sort((a, b) => {
  49. return a[0].localeCompare(b[0])
  50. })
  51. const values = { boolean: [], other: [] }
  52. for (const entry of entries) {
  53. const type = typeof entry[1]
  54. if (type === 'boolean') {
  55. values.boolean.push(entry)
  56. } else if (type !== 'function') {
  57. values.other.push(entry)
  58. }
  59. }
  60. return values
  61. }, [context])
  62. return (
  63. <div
  64. style={{
  65. padding: 20,
  66. background: 'white',
  67. float: 'left',
  68. zIndex: 10,
  69. position: 'absolute',
  70. top: 60,
  71. bottom: 60,
  72. right: 20,
  73. left: 400,
  74. overflow: 'hidden',
  75. boxShadow: '0px 2px 5px #ccc',
  76. borderRadius: 3,
  77. }}
  78. >
  79. <div
  80. style={{
  81. display: 'flex',
  82. fontSize: 14,
  83. gap: 20,
  84. height: '100%',
  85. overflow: 'hidden',
  86. }}
  87. >
  88. <div style={{ height: '100%', overflow: 'auto', flexShrink: 0 }}>
  89. <table>
  90. <tbody>
  91. {values.boolean.map(([key, value]) => {
  92. return (
  93. <tr key={key} style={{ border: '1px solid #ddd' }}>
  94. <td style={{ padding: 5 }}>{value ? '🟢' : '🔴'}</td>
  95. <th style={{ padding: 5 }}>{key}</th>
  96. </tr>
  97. )
  98. })}
  99. </tbody>
  100. </table>
  101. <div>
  102. <div
  103. style={{
  104. display: 'flex',
  105. flexDirection: 'column',
  106. gap: 10,
  107. margin: '10px 0',
  108. }}
  109. >
  110. <Button onClick={dispatchDocChanged}>trigger doc change</Button>
  111. <Button onClick={toggleLintingError}>
  112. toggle linting error
  113. </Button>
  114. </div>
  115. </div>
  116. </div>
  117. <div style={{ height: '100%', overflow: 'auto' }}>
  118. <table
  119. style={{
  120. width: '100%',
  121. overflow: 'hidden',
  122. }}
  123. >
  124. <tbody>
  125. {values.other.map(([key, value]) => {
  126. return (
  127. <tr
  128. key={key}
  129. style={{
  130. width: '100%',
  131. overflow: 'hidden',
  132. border: '1px solid #ddd',
  133. }}
  134. >
  135. <th
  136. style={{
  137. verticalAlign: 'top',
  138. padding: 5,
  139. }}
  140. >
  141. {key}
  142. </th>
  143. <td
  144. style={{
  145. overflow: 'auto',
  146. padding: 5,
  147. }}
  148. >
  149. <pre
  150. style={{
  151. margin: '0 10px',
  152. fontSize: 10,
  153. }}
  154. >
  155. {JSON.stringify(value, null, 2)}
  156. </pre>
  157. </td>
  158. </tr>
  159. )
  160. })}
  161. </tbody>
  162. </table>
  163. </div>
  164. </div>
  165. </div>
  166. )
  167. }
  168. return (
  169. <div className="pdf-viewer">
  170. <PdfPreviewPane />
  171. <Inner />
  172. </div>
  173. )
  174. }
  175. const compileStatuses = [
  176. 'autocompile-backoff',
  177. 'clear-cache',
  178. 'clsi-maintenance',
  179. 'compile-in-progress',
  180. 'exited',
  181. 'failure',
  182. 'generic',
  183. 'project-too-large',
  184. 'rate-limited',
  185. 'success',
  186. 'terminated',
  187. 'timedout',
  188. 'too-recently-compiled',
  189. 'unavailable',
  190. 'validation-problems',
  191. 'foo',
  192. ]
  193. export const CompileError = () => {
  194. const [status, setStatus] = useState('success')
  195. useFetchMock(fetchMock => {
  196. mockCompileError(fetchMock, status, 0)
  197. mockBuildFile(fetchMock)
  198. })
  199. const Inner = () => {
  200. const { startCompile } = useCompileContext()
  201. const handleStatusChange = useCallback(
  202. event => {
  203. setStatus(event.target.value)
  204. window.setTimeout(() => {
  205. startCompile()
  206. }, 0)
  207. },
  208. [startCompile]
  209. )
  210. return (
  211. <div
  212. style={{
  213. position: 'absolute',
  214. bottom: 10,
  215. left: 10,
  216. background: 'white',
  217. padding: 10,
  218. }}
  219. >
  220. <label>
  221. {'status: '}
  222. <select value={status} onInput={handleStatusChange}>
  223. {compileStatuses.map(status => (
  224. <option key={status}>{status}</option>
  225. ))}
  226. </select>
  227. </label>
  228. </div>
  229. )
  230. }
  231. return (
  232. <>
  233. <PdfPreviewPane />
  234. <Inner />
  235. </>
  236. )
  237. }
  238. const compileErrors = [
  239. 'autocompile-backoff',
  240. 'clear-cache',
  241. 'clsi-maintenance',
  242. 'compile-in-progress',
  243. 'exited',
  244. 'failure',
  245. 'generic',
  246. 'project-too-large',
  247. 'rate-limited',
  248. 'success',
  249. 'terminated',
  250. 'timedout',
  251. 'too-recently-compiled',
  252. 'unavailable',
  253. 'validation-problems',
  254. 'foo',
  255. ]
  256. export const DisplayError = () => {
  257. useFetchMock(fetchMock => {
  258. mockCompile(fetchMock)
  259. })
  260. return (
  261. <>
  262. {compileErrors.map(error => (
  263. <div
  264. key={error}
  265. style={{ background: '#5d6879', padding: 10, margin: 5 }}
  266. >
  267. <div style={{ fontFamily: 'monospace', color: 'white' }}>{error}</div>
  268. <PdfPreviewError error={error} />
  269. </div>
  270. ))}
  271. </>
  272. )
  273. }
  274. export const HybridToolbar = () => {
  275. useFetchMock(fetchMock => {
  276. mockCompile(fetchMock, 500)
  277. mockBuildFile(fetchMock)
  278. mockEventTracking(fetchMock)
  279. })
  280. return (
  281. <div className="pdf">
  282. <PdfPreviewHybridToolbar />
  283. </div>
  284. )
  285. }
  286. export const FileList = () => {
  287. const fileList = useMemo(() => {
  288. return buildFileList(cloneDeep(outputFiles))
  289. }, [])
  290. return (
  291. <div className="dropdown open">
  292. <div className="dropdown-menu">
  293. <PdfFileList fileList={fileList} />
  294. </div>
  295. </div>
  296. )
  297. }
  298. export const Logs = () => {
  299. useFetchMock(fetchMock => {
  300. mockCompileError(fetchMock, 400, 0)
  301. mockBuildFile(fetchMock)
  302. mockClearCache(fetchMock)
  303. })
  304. return (
  305. <div className="pdf">
  306. <PdfPreviewProvider>
  307. <PdfLogsViewer />
  308. </PdfPreviewProvider>
  309. </div>
  310. )
  311. }
  312. const validationProblems = {
  313. sizeCheck: {
  314. resources: [
  315. { path: 'foo/bar', kbSize: 76221 },
  316. { path: 'bar/baz', kbSize: 2342 },
  317. ],
  318. },
  319. mainFile: true,
  320. conflictedPaths: [
  321. {
  322. path: 'foo/bar',
  323. },
  324. {
  325. path: 'foo/baz',
  326. },
  327. ],
  328. }
  329. export const ValidationIssues = () => {
  330. useFetchMock(fetchMock => {
  331. mockCompileValidationIssues(fetchMock, validationProblems, 0)
  332. mockBuildFile(fetchMock)
  333. })
  334. return <PdfPreviewPane />
  335. }