Просмотр исходного кода

Merge pull request #12668 from overleaf/td-history-scroll-to-first-change

Scroll to first change in history document diff viewer

GitOrigin-RevId: e135877ca440c5eb2732890e8723d9098444727f
Tim Down 3 лет назад
Родитель
Сommit
5f5e07a334

+ 16 - 2
services/web/frontend/js/features/history/components/diff-view/document-diff-viewer.tsx

@@ -1,7 +1,12 @@
 import { useCallback, useEffect, useRef, useState } from 'react'
 import withErrorBoundary from '../../../../infrastructure/error-boundary'
 import { ErrorBoundaryFallback } from '../../../../shared/components/error-boundary-fallback'
-import { EditorState, Extension } from '@codemirror/state'
+import {
+  EditorSelection,
+  EditorState,
+  Extension,
+  StateEffect,
+} from '@codemirror/state'
 import { EditorView, lineNumbers } from '@codemirror/view'
 import { indentationMarkers } from '@replit/codemirror-indentation-markers'
 import { highlights, setHighlightsEffect } from '../../extensions/highlights'
@@ -100,9 +105,18 @@ function DocumentDiffViewer({
   const { before, after } = highlightLocations
 
   useEffect(() => {
+    const effects: StateEffect<unknown>[] = [setHighlightsEffect.of(highlights)]
+    if (highlights.length > 0) {
+      const { from, to } = highlights[0].range
+      effects.push(
+        EditorView.scrollIntoView(EditorSelection.range(from, to), {
+          y: 'center',
+        })
+      )
+    }
     view.dispatch({
       changes: { from: 0, to: view.state.doc.length, insert: doc },
-      effects: setHighlightsEffect.of(highlights),
+      effects,
     })
   }, [doc, highlights, view])
 

+ 36 - 18
services/web/frontend/stories/history/document-diff-viewer.stories.tsx

@@ -3,23 +3,6 @@ import DocumentDiffViewer from '../../js/features/history/components/diff-view/d
 import React from 'react'
 import { Highlight } from '../../js/features/history/services/types/doc'
 
-export default {
-  title: 'History / Document Diff Viewer',
-  component: DocumentDiffViewer,
-  decorators: [
-    ScopeDecorator,
-    (Story: React.ComponentType) => (
-      <div style={{ height: '90vh' }}>
-        <Story />
-      </div>
-    ),
-  ],
-}
-
-export const Highlights = () => {
-  return <DocumentDiffViewer doc={content} highlights={highlights} />
-}
-
 const highlights: Highlight[] = [
   {
     type: 'addition',
@@ -47,7 +30,7 @@ const highlights: Highlight[] = [
   },
 ]
 
-const content = `\\documentclass{article}
+const doc = `\\documentclass{article}
 
 % Language setting
 % Replace \`english' with e.g. \`spanish' to change the document language
@@ -131,3 +114,38 @@ Once you're familiar with the editor, you can find various project settings in t
 \\bibliography{sample}
 
 \\end{document}`
+
+export default {
+  title: 'History / Document Diff Viewer',
+  component: DocumentDiffViewer,
+  args: { doc, highlights },
+  argTypes: {
+    doc: {
+      table: { disable: true },
+    },
+    highlights: {
+      table: { disable: true },
+    },
+  },
+  decorators: [
+    ScopeDecorator,
+    (Story: React.ComponentType) => (
+      <div style={{ height: '90vh' }}>
+        <Story />
+      </div>
+    ),
+  ],
+}
+
+export const Highlights = (
+  args: React.ComponentProps<typeof DocumentDiffViewer>
+) => {
+  return <DocumentDiffViewer {...args} />
+}
+
+export const ScrollToFirstHighlight = (
+  args: React.ComponentProps<typeof DocumentDiffViewer>
+) => {
+  const lastHighlightOnly = args.highlights.slice(-1)
+  return <DocumentDiffViewer {...args} highlights={lastHighlightOnly} />
+}

+ 33 - 0
services/web/test/frontend/features/history/components/document-diff-viewer.spec.tsx

@@ -144,4 +144,37 @@ describe('document diff viewer', function () {
     cy.get('.previous-highlight-button').should('have.length', 1)
     cy.get('.next-highlight-button').should('have.length', 1)
   })
+
+  it('scrolls to first change', function () {
+    const scope = mockScope()
+    const finalHighlightOnly = highlights.slice(-1)
+
+    cy.mount(
+      <Container>
+        <EditorProviders scope={scope}>
+          <DocumentDiffViewer doc={doc} highlights={finalHighlightOnly} />
+        </EditorProviders>
+      </Container>
+    )
+
+    cy.get('.cm-scroller').first().invoke('scrollTop').should('not.equal', 0)
+    cy.get('.ol-addition-marker')
+      .first()
+      .then($marker => {
+        cy.get('.cm-content')
+          .first()
+          .then($content => {
+            const contentRect = $content[0].getBoundingClientRect()
+            const markerRect = $marker[0].getBoundingClientRect()
+            expect(markerRect.top).to.be.within(
+              contentRect.top,
+              contentRect.bottom
+            )
+            expect(markerRect.bottom).to.be.within(
+              contentRect.top,
+              contentRect.bottom
+            )
+          })
+      })
+  })
 })