Implement deleted file restore for history react (#12753)

* Add strikethrough to deleted file tree item in history file tree

* Add "restore file" button on toolbar if the selected file have a `removed` operation.

* Implement "restore file" functionality on removed file:

- Refactor the `Selection` object in history context value since we need the `deletedAtV` data which currently is not passed through the state.
- Refactor and clean up file tree type system to support passing through the whole `FileDiff` object for getting the `deletedAtV` data which only appear on `removed` operation
- Implement `postJSON` with file restoration API and pass it on restore file onClick handler at toolbar

* Implement loading behaviour while restoring file is inflight:

- Add `loadingRestoreFile` to `LoadingState`
- Change restore file button to `Restoring...` when in loading state.

* Refactor:

- Rename `DiffOperation` to `FileOperation`
- Extract `isFileRemoved` and `isFileRenamed` to its own file
- Extract `Toolbar` components into small files

GitOrigin-RevId: 2e32ebd2165f73fc6533ff282a9c084162efd682
This commit is contained in:
M Fahru 2023-04-27 11:45:13 -07:00 committed by Copybot
parent 0de648eecd
commit 0648b8aa6c
29 changed files with 394 additions and 182 deletions

View file

@ -715,6 +715,8 @@
"resend": "",
"resend_confirmation_email": "",
"resending_confirmation_email": "",
"restore_file": "",
"restoring": "",
"reverse_x_sort_order": "",
"revert_pending_plan_change": "",
"review": "",

View file

@ -44,7 +44,6 @@ function Compare({
},
comparing: true,
files: [],
pathname: null,
})
}

View file

@ -24,7 +24,6 @@ function HistoryVersionDetails({
updateRange: { fromV, toV, fromVTimestamp, toVTimestamp },
comparing: false,
files: [],
pathname: null,
})
}
}

View file

@ -1,7 +1,7 @@
import Toolbar from './toolbar'
import { useEffect, useState } from 'react'
import Toolbar from './toolbar/toolbar'
import Main from './main'
import { Diff, DocDiffResponse } from '../../services/types/doc'
import { useEffect, useState } from 'react'
import { Nullable } from '../../../../../../types/utils'
import { useHistoryContext } from '../../context/history-context'
import { diffDoc } from '../../services/api'
@ -14,35 +14,37 @@ function DiffView() {
const { isLoading, runAsync } = useAsync<DocDiffResponse>()
const { updateRange, pathname } = selection
const { updateRange, selectedFile } = selection
useEffect(() => {
if (!updateRange || !pathname) {
if (!updateRange || !selectedFile?.pathname) {
return
}
const { fromV, toV } = updateRange
// TODO: Error handling
runAsync(diffDoc(projectId, fromV, toV, pathname)).then(data => {
let diff: Diff | undefined
runAsync(diffDoc(projectId, fromV, toV, selectedFile.pathname)).then(
data => {
let diff: Diff | undefined
if (!data?.diff) {
setDiff(null)
}
if ('binary' in data.diff) {
diff = { binary: true }
} else {
diff = {
binary: false,
docDiff: highlightsFromDiffResponse(data.diff),
if (!data?.diff) {
setDiff(null)
}
}
setDiff(diff)
})
}, [projectId, runAsync, updateRange, pathname])
if ('binary' in data.diff) {
diff = { binary: true }
} else {
diff = {
binary: false,
docDiff: highlightsFromDiffResponse(data.diff),
}
}
setDiff(diff)
}
)
}, [projectId, runAsync, updateRange, selectedFile])
return (
<div className="doc-panel">

View file

@ -1,73 +0,0 @@
import { Trans, useTranslation } from 'react-i18next'
import { formatTime } from '../../../utils/format-date'
import type { Nullable } from '../../../../../../types/utils'
import type { Diff } from '../../services/types/doc'
import type { HistoryContextValue } from '../../context/types/history-context-value'
type ToolbarProps = {
diff: Nullable<Diff>
selection: HistoryContextValue['selection']
}
function Toolbar({ diff, selection }: ToolbarProps) {
const { t } = useTranslation()
if (!selection) return null
return (
<div className="history-react-toolbar">
<div>
{selection.comparing ? (
<Trans
i18nKey="comparing_x_to_y"
// eslint-disable-next-line react/jsx-key
components={[<time className="history-react-toolbar-time" />]}
values={{
startTime: formatTime(
selection.updateRange?.fromVTimestamp,
'Do MMMM · h:mm a'
),
endTime: formatTime(
selection.updateRange?.toVTimestamp,
'Do MMMM · h:mm a'
),
}}
/>
) : (
<Trans
i18nKey="viewing_x"
// eslint-disable-next-line react/jsx-key
components={[<time className="history-react-toolbar-time" />]}
values={{
endTime: formatTime(
selection.updateRange?.toVTimestamp,
'Do MMMM · h:mm a'
),
}}
/>
)}
</div>
{selection.pathname ? (
<div className="history-react-toolbar-changes">
{t('x_changes_in', {
count: diff?.docDiff?.highlights.length ?? 0,
})}
&nbsp;
<strong>{getFileName(selection)}</strong>
</div>
) : null}
</div>
)
}
function getFileName(selection: HistoryContextValue['selection']) {
const filePathParts = selection?.pathname?.split('/')
let fileName
if (filePathParts) {
fileName = filePathParts[filePathParts.length - 1]
}
return fileName
}
export default Toolbar

View file

@ -0,0 +1,43 @@
import { Trans } from 'react-i18next'
import { formatTime } from '../../../../utils/format-date'
import type { HistoryContextValue } from '../../../context/types/history-context-value'
type ToolbarDatetimeProps = {
selection: HistoryContextValue['selection']
}
export default function ToolbarDatetime({ selection }: ToolbarDatetimeProps) {
return (
<div className="history-react-toolbar-datetime">
{selection.comparing ? (
<Trans
i18nKey="comparing_x_to_y"
// eslint-disable-next-line react/jsx-key
components={[<time className="history-react-toolbar-time" />]}
values={{
startTime: formatTime(
selection.updateRange?.fromVTimestamp,
'Do MMMM · h:mm a'
),
endTime: formatTime(
selection.updateRange?.toVTimestamp,
'Do MMMM · h:mm a'
),
}}
/>
) : (
<Trans
i18nKey="viewing_x"
// eslint-disable-next-line react/jsx-key
components={[<time className="history-react-toolbar-time" />]}
values={{
endTime: formatTime(
selection.updateRange?.toVTimestamp,
'Do MMMM · h:mm a'
),
}}
/>
)}
</div>
)
}

View file

@ -0,0 +1,36 @@
import { useTranslation } from 'react-i18next'
import type { HistoryContextValue } from '../../../context/types/history-context-value'
import type { Diff } from '../../../services/types/doc'
import type { Nullable } from '../../../../../../../types/utils'
type ToolbarFileInfoProps = {
diff: Nullable<Diff>
selection: HistoryContextValue['selection']
}
export default function ToolbarFileInfo({
diff,
selection,
}: ToolbarFileInfoProps) {
const { t } = useTranslation()
return (
<div className="history-react-toolbar-file-info">
{t('x_changes_in', {
count: diff?.docDiff?.highlights?.length ?? 0,
})}
&nbsp;
<strong>{getFileName(selection)}</strong>
</div>
)
}
function getFileName(selection: HistoryContextValue['selection']) {
const filePathParts = selection?.selectedFile?.pathname?.split('/')
let fileName
if (filePathParts) {
fileName = filePathParts[filePathParts.length - 1]
}
return fileName
}

View file

@ -0,0 +1,32 @@
import { Button } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { useHistoryContext } from '../../../context/history-context'
import { useRestoreDeletedFile } from '../../../context/hooks/use-restore-deleted-file'
import type { HistoryContextValue } from '../../../context/types/history-context-value'
type ToolbarRestoreFileButtonProps = {
selection: HistoryContextValue['selection']
}
export default function ToolbarRestoreFileButton({
selection,
}: ToolbarRestoreFileButtonProps) {
const { t } = useTranslation()
const { loadingState } = useHistoryContext()
const onRestoreFile = useRestoreDeletedFile()
return (
<Button
className="btn-secondary history-react-toolbar-restore-file-button"
bsSize="xs"
bsStyle={null}
onClick={() => onRestoreFile(selection)}
disabled={loadingState === 'restoringFile'}
>
{loadingState === 'restoringFile'
? `${t('restoring')}`
: t('restore_file')}
</Button>
)
}

View file

@ -0,0 +1,29 @@
import type { Nullable } from '../../../../../../../types/utils'
import type { Diff } from '../../../services/types/doc'
import type { HistoryContextValue } from '../../../context/types/history-context-value'
import ToolbarDatetime from './toolbar-datetime'
import ToolbarFileInfo from './toolbar-file-info'
import ToolbarRestoreFileButton from './toolbar-restore-file-button'
import { isFileRemoved } from '../../../utils/file-diff'
type ToolbarProps = {
diff: Nullable<Diff>
selection: HistoryContextValue['selection']
}
export default function Toolbar({ diff, selection }: ToolbarProps) {
const showRestoreFileButton =
selection.selectedFile && isFileRemoved(selection.selectedFile)
return (
<div className="history-react-toolbar">
<ToolbarDatetime selection={selection} />
{selection.selectedFile?.pathname ? (
<ToolbarFileInfo diff={diff} selection={selection} />
) : null}
{showRestoreFileButton ? (
<ToolbarRestoreFileButton selection={selection} />
) : null}
</div>
)
}

View file

@ -1,22 +1,20 @@
import classNames from 'classnames'
import HistoryFileTreeItem from './history-file-tree-item'
import iconTypeFromName from '../../../file-tree/util/icon-type-from-name'
import Icon from '../../../../shared/components/icon'
import { useFileTreeItemSelection } from '../../context/hooks/use-file-tree-item-selection'
import { DiffOperation } from '../../services/types/diff-operation'
import classNames from 'classnames'
import type { FileDiff } from '../../services/types/file'
type HistoryFileTreeDocProps = {
file: FileDiff
name: string
pathname: string
operation?: DiffOperation
}
export default function HistoryFileTreeDoc({
file,
name,
pathname,
operation,
}: HistoryFileTreeDocProps) {
const { isSelected, onClick } = useFileTreeItemSelection(pathname)
const { isSelected, onClick } = useFileTreeItemSelection(file)
return (
<li
@ -30,7 +28,7 @@ export default function HistoryFileTreeDoc({
>
<HistoryFileTreeItem
name={name}
operation={operation}
operation={'operation' in file ? file.operation : undefined}
icons={
<Icon
type={iconTypeFromName(name)}

View file

@ -33,12 +33,7 @@ export default function HistoryFileTreeFolderList({
})}
{docs.sort(compareFunction).map(doc => {
return (
<HistoryFileTreeDoc
key={doc.pathname}
name={doc.name}
pathname={doc.pathname}
operation={doc.operation}
/>
<HistoryFileTreeDoc key={doc.pathname} name={doc.name} file={doc} />
)
})}
{children}

View file

@ -1,10 +1,11 @@
import classNames from 'classnames'
import type { ReactNode } from 'react'
import type { DiffOperation } from '../../services/types/diff-operation'
import type { FileOperation } from '../../services/types/file-operation'
import Badge from '../../../../shared/components/badge'
type FileTreeItemProps = {
name: string
operation?: DiffOperation
operation?: FileOperation
icons: ReactNode
}
@ -17,7 +18,13 @@ export default function HistoryFileTreeItem({
<div className="history-file-tree-item" role="presentation">
{icons}
<button className="history-file-tree-item-button">
<span className="history-file-tree-item-button-text">{name}</span>
<span
className={classNames('history-file-tree-item-button-text', {
strikethrough: operation === 'removed',
})}
>
{name}
</span>
{operation ? (
<Badge className="history-file-tree-item-button-badge" size="sm">
{operation}

View file

@ -12,7 +12,8 @@ import { useUserContext } from '../../../shared/context/user-context'
import { useProjectContext } from '../../../shared/context/project-context'
import { HistoryContextValue } from './types/history-context-value'
import { diffFiles, fetchLabels, fetchUpdates } from '../services/api'
import { renamePathnameKey, isFileRenamed } from '../utils/file-tree'
import { renamePathnameKey } from '../utils/file-tree'
import { isFileRenamed } from '../utils/file-diff'
import { loadLabels } from '../utils/label'
import { autoSelectFile } from '../utils/auto-select-file'
import ColorManager from '../../../ide/colors/ColorManager'
@ -61,7 +62,6 @@ const selectionInitialState: Selection = {
updateRange: null,
comparing: false,
files: [],
pathname: null,
}
function useHistory() {
@ -209,7 +209,7 @@ function useHistory() {
const { fromV, toV } = updateRange
diffFiles(projectId, fromV, toV).then(({ diff: files }) => {
const pathname = autoSelectFile(
const selectedFile = autoSelectFile(
files,
updateRange.toV,
comparing,
@ -222,7 +222,12 @@ function useHistory() {
return file
})
setSelection({ updateRange, comparing, files: newFiles, pathname })
setSelection({
updateRange,
comparing,
files: newFiles,
selectedFile,
})
})
}, [updateRange, projectId, updates, comparing])
@ -238,7 +243,6 @@ function useHistory() {
},
comparing: false,
files: [],
pathname: null,
})
}
}, [updateRange, updates])
@ -247,6 +251,7 @@ function useHistory() {
() => ({
error,
loadingState,
setLoadingState,
updatesInfo,
setUpdatesInfo,
labels,
@ -261,6 +266,7 @@ function useHistory() {
[
error,
loadingState,
setLoadingState,
updatesInfo,
setUpdatesInfo,
labels,

View file

@ -1,19 +1,20 @@
import { useCallback } from 'react'
import { useHistoryContext } from '../history-context'
import type { FileDiff } from '../../services/types/file'
export function useFileTreeItemSelection(pathname: string) {
export function useFileTreeItemSelection(file: FileDiff) {
const { selection, setSelection } = useHistoryContext()
const handleClick = useCallback(() => {
if (pathname !== selection.pathname) {
if (file.pathname !== selection.selectedFile?.pathname) {
setSelection({
...selection,
pathname,
selectedFile: file,
})
}
}, [pathname, selection, setSelection])
}, [file, selection, setSelection])
const isSelected = selection.pathname === pathname
const isSelected = selection.selectedFile?.pathname === file.pathname
return { isSelected, onClick: handleClick }
}

View file

@ -0,0 +1,44 @@
import { sendMB } from '../../../../infrastructure/event-tracking'
import { useIdeContext } from '../../../../shared/context/ide-context'
import { useLayoutContext } from '../../../../shared/context/layout-context'
import useAsync from '../../../../shared/hooks/use-async'
import { restoreFile } from '../../services/api'
import { isFileRemoved } from '../../utils/file-diff'
import { useHistoryContext } from '../history-context'
import type { HistoryContextValue } from '../types/history-context-value'
export function useRestoreDeletedFile() {
const { runAsync } = useAsync()
const { setLoadingState, projectId } = useHistoryContext()
const ide = useIdeContext()
const { setView } = useLayoutContext()
return async (selection: HistoryContextValue['selection']) => {
const { selectedFile } = selection
if (selectedFile && selectedFile.pathname && isFileRemoved(selectedFile)) {
sendMB('history-v2-restore-deleted')
setLoadingState('restoringFile')
await runAsync(
restoreFile(projectId, selectedFile)
.then(data => {
const { id, type } = data
const entity = ide.fileTreeManager.findEntityById(id)
if (type === 'doc') {
ide.editorManager.openDoc(entity)
} else {
ide.binaryFilesManager.openFile(entity)
}
setView('editor')
})
.finally(() => {
setLoadingState('ready')
})
)
}
}
}

View file

@ -3,6 +3,12 @@ import { LoadedUpdate } from '../../services/types/update'
import { LoadedLabel } from '../../services/types/label'
import { Selection } from '../../services/types/selection'
type LoadingState =
| 'loadingInitial'
| 'loadingUpdates'
| 'restoringFile'
| 'ready'
export type HistoryContextValue = {
updatesInfo: {
updates: LoadedUpdate[]
@ -14,7 +20,10 @@ export type HistoryContextValue = {
React.SetStateAction<HistoryContextValue['updatesInfo']>
>
userHasFullFeature: boolean | undefined
loadingState: 'loadingInitial' | 'loadingUpdates' | 'ready'
loadingState: LoadingState
setLoadingState: React.Dispatch<
React.SetStateAction<HistoryContextValue['loadingState']>
>
error: Nullable<unknown>
labels: Nullable<LoadedLabel[]>
setLabels: React.Dispatch<React.SetStateAction<HistoryContextValue['labels']>>

View file

@ -3,10 +3,11 @@ import {
getJSON,
postJSON,
} from '../../../infrastructure/fetch-json'
import { FileDiff } from './types/file'
import { FileDiff, FileRemoved } from './types/file'
import { FetchUpdatesResponse } from './types/update'
import { Label } from './types/label'
import { DocDiffResponse } from './types/doc'
import { RestoreFileResponse } from './types/restore-file'
const BATCH_SIZE = 10
@ -70,3 +71,12 @@ export function diffDoc(
const diffUrl = `/project/${projectId}/diff?${queryParamsSerialized}`
return getJSON<DocDiffResponse>(diffUrl)
}
export function restoreFile(projectId: string, selectedFile: FileRemoved) {
return postJSON<RestoreFileResponse>(`/project/${projectId}/restore_file`, {
body: {
version: selectedFile.deletedAtV,
pathname: selectedFile.pathname,
},
})
}

View file

@ -1 +0,0 @@
export type DiffOperation = 'edited' | 'added' | 'renamed' | 'removed'

View file

@ -0,0 +1 @@
export type FileOperation = 'edited' | 'added' | 'renamed' | 'removed'

View file

@ -1,27 +1,27 @@
import { DiffOperation } from './diff-operation'
import { FileOperation } from './file-operation'
export interface FileUnchanged {
pathname: string
}
export interface FileAdded extends FileUnchanged {
operation: Extract<DiffOperation, 'added'>
operation: Extract<FileOperation, 'added'>
}
export interface FileRemoved extends FileUnchanged {
operation: Extract<DiffOperation, 'removed'>
operation: Extract<FileOperation, 'removed'>
newPathname?: string
deletedAtV: number
}
export interface FileEdited extends FileUnchanged {
operation: Extract<DiffOperation, 'edited'>
operation: Extract<FileOperation, 'edited'>
}
export interface FileRenamed extends FileUnchanged {
newPathname?: string
oldPathname?: string
operation: Extract<DiffOperation, 'renamed'>
operation: Extract<FileOperation, 'renamed'>
}
export type FileDiff =

View file

@ -0,0 +1,4 @@
export type RestoreFileResponse = {
id: string
type: 'doc' | 'file'
}

View file

@ -5,5 +5,5 @@ export interface Selection {
updateRange: UpdateRange | null
comparing: boolean
files: FileDiff[]
pathname: string | null
selectedFile?: FileDiff
}

View file

@ -1,7 +1,7 @@
import _ from 'lodash'
import type { Nullable } from '../../../../../types/utils'
import type { FileDiff } from '../services/types/file'
import type { DiffOperation } from '../services/types/diff-operation'
import type { FileOperation } from '../services/types/file-operation'
import type { LoadedUpdate, Version } from '../services/types/update'
function getUpdateForVersion(
@ -13,7 +13,7 @@ function getUpdateForVersion(
type FileWithOps = {
pathname: FileDiff['pathname']
operation: DiffOperation
operation: FileOperation
}
function getFilesWithOps(
@ -80,7 +80,7 @@ function getFilesWithOps(
}
}
const orderedOpTypes: DiffOperation[] = [
const orderedOpTypes: FileOperation[] = [
'edited',
'added',
'renamed',
@ -92,7 +92,7 @@ export function autoSelectFile(
toV: Version,
comparing: boolean,
updates: LoadedUpdate[]
) {
): FileDiff {
let fileToSelect: Nullable<FileDiff> = null
const filesWithOps = getFilesWithOps(files, toV, comparing, updates)
@ -131,5 +131,5 @@ export function autoSelectFile(
}
}
return fileToSelect.pathname
return fileToSelect
}

View file

@ -0,0 +1,9 @@
import type { FileDiff, FileRemoved, FileRenamed } from '../services/types/file'
export function isFileRenamed(fileDiff: FileDiff): fileDiff is FileRenamed {
return (fileDiff as FileRenamed).operation === 'renamed'
}
export function isFileRemoved(fileDiff: FileDiff): fileDiff is FileRemoved {
return (fileDiff as FileRemoved).operation === 'removed'
}

View file

@ -1,19 +1,12 @@
import _ from 'lodash'
import type { FileDiff, FileRenamed } from '../services/types/file'
import type { DiffOperation } from '../services/types/diff-operation'
import { isFileRemoved } from './file-diff'
// `Partial` because the `reducePathsToTree` function was copied directly
// from a javascript file without proper type system and the logic is not typescript-friendly.
// TODO: refactor the function to have a proper type system
type FileTreeEntity = Partial<{
name: string
type: 'file' | 'folder'
oldPathname: string
newPathname: string
pathname: string
children: FileTreeEntity[]
operation: DiffOperation
}>
export type FileTreeEntity = {
name?: string
type?: 'file' | 'folder'
children?: FileTreeEntity[]
} & FileDiff
export function reducePathsToTree(
currentFileTree: FileTreeEntity[],
@ -21,25 +14,31 @@ export function reducePathsToTree(
) {
const filePathParts = fileObject?.pathname?.split('/') ?? ''
let currentFileTreeLocation = currentFileTree
for (let index = 0; index < filePathParts.length; index++) {
let fileTreeEntity: FileTreeEntity | null = {}
const pathPart = filePathParts[index]
const isFile = index === filePathParts.length - 1
if (isFile) {
fileTreeEntity = _.clone(fileObject)
const fileTreeEntity: FileTreeEntity = _.clone(fileObject)
fileTreeEntity.name = pathPart
fileTreeEntity.type = 'file'
currentFileTreeLocation.push(fileTreeEntity)
} else {
fileTreeEntity =
_.find(currentFileTreeLocation, entity => entity.name === pathPart) ??
null
if (fileTreeEntity == null) {
let fileTreeEntity: FileTreeEntity | undefined = _.find(
currentFileTreeLocation,
entity => entity.name === pathPart
)
if (fileTreeEntity === undefined) {
fileTreeEntity = {
name: pathPart,
type: 'folder',
children: [],
children: <FileTreeEntity[]>[],
pathname: pathPart,
}
currentFileTreeLocation.push(fileTreeEntity)
}
currentFileTreeLocation = fileTreeEntity.children ?? []
@ -49,9 +48,8 @@ export function reducePathsToTree(
}
export type HistoryDoc = {
pathname: string
name: string
} & Pick<FileTreeEntity, 'operation'>
} & FileDiff
export type HistoryFileTree = {
docs?: HistoryDoc[]
@ -68,11 +66,22 @@ export function fileTreeDiffToFileTreeData(
for (const file of fileTreeDiff) {
if (file.type === 'file') {
docs.push({
const deletedAtV = isFileRemoved(file) ? file.deletedAtV : undefined
let newDoc: HistoryDoc = {
pathname: file.pathname ?? '',
name: file.name ?? '',
operation: file.operation,
})
deletedAtV,
}
if ('operation' in file) {
newDoc = {
...newDoc,
operation: file.operation,
}
}
docs.push(newDoc)
} else if (file.type === 'folder') {
if (file.children) {
const folder = fileTreeDiffToFileTreeData(file.children, file.name)
@ -108,7 +117,3 @@ export function renamePathnameKey(file: FileRenamed): FileRenamed {
operation: file.operation,
}
}
export function isFileRenamed(fileDiff: FileDiff): fileDiff is FileRenamed {
return (fileDiff as FileRenamed).operation === 'renamed'
}

View file

@ -252,13 +252,20 @@ history-root {
.history-react-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
.history-react-toolbar-file-info {
flex: 1;
text-align: right;
}
.history-react-toolbar-time {
font-weight: 700;
}
.history-react-toolbar-restore-file-button {
margin-left: 5px;
}
}
}
@ -391,6 +398,10 @@ history-root {
.history-file-tree-item-button-text {
display: inline-flex;
margin-right: 5px;
&.strikethrough {
text-decoration: line-through;
}
}
.history-file-tree-item-button-badge {

View file

@ -1252,6 +1252,7 @@
"resolve": "Resolve",
"resolved_comments": "Resolved comments",
"restore": "Restore",
"restore_file": "Restore file",
"restore_to_any_older_version": "Restore to any older version",
"restore_to_before_these_changes": "Restore to before these changes",
"restoring": "Restoring",

View file

@ -1,8 +1,14 @@
import Toolbar from '../../../../../frontend/js/features/history/components/diff-view/toolbar'
import Toolbar from '../../../../../frontend/js/features/history/components/diff-view/toolbar/toolbar'
import { HistoryProvider } from '../../../../../frontend/js/features/history/context/history-context'
import { HistoryContextValue } from '../../../../../frontend/js/features/history/context/types/history-context-value'
import { Diff } from '../../../../../frontend/js/features/history/services/types/doc'
import { EditorProviders } from '../../../helpers/editor-providers'
describe('history toolbar', function () {
const editorProvidersScope = {
ui: { view: 'history', pdfLayout: 'sideBySide', chatOpen: true },
}
const diff: Diff = {
binary: false,
docDiff: {
@ -42,20 +48,26 @@ describe('history toolbar', function () {
pathname: 'frog.jpg',
},
],
pathname: 'main.tex',
selectedFile: {
pathname: 'main.tex',
},
}
cy.mount(
<div className="history-react">
<Toolbar diff={diff} selection={selection} />
</div>
<EditorProviders scope={editorProvidersScope}>
<HistoryProvider>
<div className="history-react">
<Toolbar diff={diff} selection={selection} />
</div>
</HistoryProvider>
</EditorProviders>
)
cy.get('.history-react-toolbar').within(() => {
cy.get('div:first-child').contains('Viewing 13th April')
})
cy.get('.history-react-toolbar-changes').contains('1 change in main.tex')
cy.get('.history-react-toolbar-file-info').contains('1 change in main.tex')
})
it('renders comparing mode', function () {
@ -81,13 +93,19 @@ describe('history toolbar', function () {
operation: 'added',
},
],
pathname: 'main.tex',
selectedFile: {
pathname: 'main.tex',
},
}
cy.mount(
<div className="history-react">
<Toolbar diff={diff} selection={selection} />
</div>
<EditorProviders scope={editorProvidersScope}>
<HistoryProvider>
<div className="history-react">
<Toolbar diff={diff} selection={selection} />
</div>
</HistoryProvider>
</EditorProviders>
)
cy.get('.history-react-toolbar').within(() => {

View file

@ -259,7 +259,12 @@ describe('autoSelectFile', function () {
},
]
const pathname = autoSelectFile(files, updates[0].toV, comparing, updates)
const { pathname } = autoSelectFile(
files,
updates[0].toV,
comparing,
updates
)
expect(pathname).to.equal('newfolder1/newfile10.tex')
})
@ -324,7 +329,12 @@ describe('autoSelectFile', function () {
},
]
const pathname = autoSelectFile(files, updates[0].toV, comparing, updates)
const { pathname } = autoSelectFile(
files,
updates[0].toV,
comparing,
updates
)
expect(pathname).to.equal('newfile1.tex')
})
@ -420,7 +430,12 @@ describe('autoSelectFile', function () {
},
]
const pathname = autoSelectFile(files, updates[0].toV, comparing, updates)
const { pathname } = autoSelectFile(
files,
updates[0].toV,
comparing,
updates
)
expect(pathname).to.equal('main3.tex')
})
@ -586,7 +601,12 @@ describe('autoSelectFile', function () {
},
]
const pathname = autoSelectFile(files, updates[0].toV, comparing, updates)
const { pathname } = autoSelectFile(
files,
updates[0].toV,
comparing,
updates
)
expect(pathname).to.equal('main.tex')
})
@ -689,7 +709,12 @@ describe('autoSelectFile', function () {
},
]
const pathname = autoSelectFile(files, updates[0].toV, comparing, updates)
const { pathname } = autoSelectFile(
files,
updates[0].toV,
comparing,
updates
)
expect(pathname).to.equal('certainly_not_main.tex')
})