pdf-preview.stories.jsx 8.9 KB

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