mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #6209 from overleaf/ta-file-tree-rework
File Tree Misc Code Changes GitOrigin-RevId: dce64a5378ecee5c8a2e25e02502ae631d87f36b
This commit is contained in:
parent
55829a3382
commit
392410390e
37 changed files with 509 additions and 429 deletions
|
@ -12,14 +12,9 @@ aside.editor-sidebar.full-size(
|
||||||
vertical-resizable-top
|
vertical-resizable-top
|
||||||
)
|
)
|
||||||
file-tree-root(
|
file-tree-root(
|
||||||
project-id="projectId"
|
|
||||||
root-folder="rootFolder"
|
|
||||||
root-doc-id="rootDocId"
|
|
||||||
has-write-permissions="hasWritePermissions"
|
|
||||||
on-select="onSelect"
|
on-select="onSelect"
|
||||||
on-init="onInit"
|
on-init="onInit"
|
||||||
is-connected="isConnected"
|
is-connected="isConnected"
|
||||||
user-has-feature="userHasFeature"
|
|
||||||
ref-providers="refProviders"
|
ref-providers="refProviders"
|
||||||
reindex-references="reindexReferences"
|
reindex-references="reindexReferences"
|
||||||
set-ref-provider-enabled="setRefProviderEnabled"
|
set-ref-provider-enabled="setRefProviderEnabled"
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { Dropdown } from 'react-bootstrap'
|
import { Dropdown } from 'react-bootstrap'
|
||||||
|
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||||
import { useFileTreeMainContext } from '../contexts/file-tree-main'
|
import { useFileTreeMainContext } from '../contexts/file-tree-main'
|
||||||
|
|
||||||
import FileTreeItemMenuItems from './file-tree-item/file-tree-item-menu-items'
|
import FileTreeItemMenuItems from './file-tree-item/file-tree-item-menu-items'
|
||||||
|
|
||||||
function FileTreeContextMenu() {
|
function FileTreeContextMenu() {
|
||||||
const { hasWritePermissions, contextMenuCoords, setContextMenuCoords } =
|
const { permissionsLevel } = useEditorContext(editorContextPropTypes)
|
||||||
useFileTreeMainContext()
|
const { contextMenuCoords, setContextMenuCoords } = useFileTreeMainContext()
|
||||||
|
|
||||||
if (!hasWritePermissions || !contextMenuCoords) return null
|
if (permissionsLevel === 'readOnly' || !contextMenuCoords) return null
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
// reset context menu
|
// reset context menu
|
||||||
|
@ -41,6 +43,10 @@ function FileTreeContextMenu() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editorContextPropTypes = {
|
||||||
|
permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
|
||||||
|
}
|
||||||
|
|
||||||
// fake component required as Dropdowns require a Toggle, even tho we don't want
|
// fake component required as Dropdowns require a Toggle, even tho we don't want
|
||||||
// one for the context menu
|
// one for the context menu
|
||||||
const FakeDropDownToggle = React.forwardRef((props, ref) => {
|
const FakeDropDownToggle = React.forwardRef((props, ref) => {
|
||||||
|
|
|
@ -12,11 +12,6 @@ import { FileTreeDraggableProvider } from '../contexts/file-tree-draggable'
|
||||||
// FileTreeMutable: provides entities mutation operations
|
// FileTreeMutable: provides entities mutation operations
|
||||||
// FileTreeSelectable: handles selection and multi-selection
|
// FileTreeSelectable: handles selection and multi-selection
|
||||||
function FileTreeContext({
|
function FileTreeContext({
|
||||||
projectId,
|
|
||||||
rootFolder,
|
|
||||||
hasWritePermissions,
|
|
||||||
rootDocId,
|
|
||||||
userHasFeature,
|
|
||||||
refProviders,
|
refProviders,
|
||||||
reindexReferences,
|
reindexReferences,
|
||||||
setRefProviderEnabled,
|
setRefProviderEnabled,
|
||||||
|
@ -26,21 +21,14 @@ function FileTreeContext({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<FileTreeMainProvider
|
<FileTreeMainProvider
|
||||||
projectId={projectId}
|
|
||||||
hasWritePermissions={hasWritePermissions}
|
|
||||||
userHasFeature={userHasFeature}
|
|
||||||
refProviders={refProviders}
|
refProviders={refProviders}
|
||||||
setRefProviderEnabled={setRefProviderEnabled}
|
setRefProviderEnabled={setRefProviderEnabled}
|
||||||
setStartedFreeTrial={setStartedFreeTrial}
|
setStartedFreeTrial={setStartedFreeTrial}
|
||||||
reindexReferences={reindexReferences}
|
reindexReferences={reindexReferences}
|
||||||
>
|
>
|
||||||
<FileTreeMutableProvider rootFolder={rootFolder}>
|
<FileTreeMutableProvider>
|
||||||
<FileTreeSelectableProvider
|
<FileTreeSelectableProvider onSelect={onSelect}>
|
||||||
hasWritePermissions={hasWritePermissions}
|
<FileTreeActionableProvider>
|
||||||
rootDocId={rootDocId}
|
|
||||||
onSelect={onSelect}
|
|
||||||
>
|
|
||||||
<FileTreeActionableProvider hasWritePermissions={hasWritePermissions}>
|
|
||||||
<FileTreeDraggableProvider>{children}</FileTreeDraggableProvider>
|
<FileTreeDraggableProvider>{children}</FileTreeDraggableProvider>
|
||||||
</FileTreeActionableProvider>
|
</FileTreeActionableProvider>
|
||||||
</FileTreeSelectableProvider>
|
</FileTreeSelectableProvider>
|
||||||
|
@ -50,15 +38,10 @@ function FileTreeContext({
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTreeContext.propTypes = {
|
FileTreeContext.propTypes = {
|
||||||
projectId: PropTypes.string.isRequired,
|
|
||||||
rootFolder: PropTypes.array.isRequired,
|
|
||||||
hasWritePermissions: PropTypes.bool.isRequired,
|
|
||||||
userHasFeature: PropTypes.func.isRequired,
|
|
||||||
reindexReferences: PropTypes.func.isRequired,
|
reindexReferences: PropTypes.func.isRequired,
|
||||||
refProviders: PropTypes.object.isRequired,
|
refProviders: PropTypes.object.isRequired,
|
||||||
setRefProviderEnabled: PropTypes.func.isRequired,
|
setRefProviderEnabled: PropTypes.func.isRequired,
|
||||||
setStartedFreeTrial: PropTypes.func.isRequired,
|
setStartedFreeTrial: PropTypes.func.isRequired,
|
||||||
rootDocId: PropTypes.string,
|
|
||||||
onSelect: PropTypes.func.isRequired,
|
onSelect: PropTypes.func.isRequired,
|
||||||
children: PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
PropTypes.arrayOf(PropTypes.node),
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { useProjectOutputFiles } from '../../../hooks/use-project-output-files'
|
||||||
import { useFileTreeActionable } from '../../../contexts/file-tree-actionable'
|
import { useFileTreeActionable } from '../../../contexts/file-tree-actionable'
|
||||||
import { useFileTreeCreateName } from '../../../contexts/file-tree-create-name'
|
import { useFileTreeCreateName } from '../../../contexts/file-tree-create-name'
|
||||||
import { useFileTreeCreateForm } from '../../../contexts/file-tree-create-form'
|
import { useFileTreeCreateForm } from '../../../contexts/file-tree-create-form'
|
||||||
import { useFileTreeMainContext } from '../../../contexts/file-tree-main'
|
import { useProjectContext } from '../../../../../shared/context/project-context'
|
||||||
import ErrorMessage from '../error-message'
|
import ErrorMessage from '../error-message'
|
||||||
|
|
||||||
export default function FileTreeImportFromProject() {
|
export default function FileTreeImportFromProject() {
|
||||||
|
@ -23,7 +23,6 @@ export default function FileTreeImportFromProject() {
|
||||||
|
|
||||||
const { name, setName, validName } = useFileTreeCreateName()
|
const { name, setName, validName } = useFileTreeCreateName()
|
||||||
const { setValid } = useFileTreeCreateForm()
|
const { setValid } = useFileTreeCreateForm()
|
||||||
const { projectId } = useFileTreeMainContext()
|
|
||||||
const { error, finishCreatingLinkedFile } = useFileTreeActionable()
|
const { error, finishCreatingLinkedFile } = useFileTreeActionable()
|
||||||
|
|
||||||
const [selectedProject, setSelectedProject] = useState()
|
const [selectedProject, setSelectedProject] = useState()
|
||||||
|
@ -112,7 +111,6 @@ export default function FileTreeImportFromProject() {
|
||||||
return (
|
return (
|
||||||
<form className="form-controls" id="create-file" onSubmit={handleSubmit}>
|
<form className="form-controls" id="create-file" onSubmit={handleSubmit}>
|
||||||
<SelectProject
|
<SelectProject
|
||||||
projectId={projectId}
|
|
||||||
selectedProject={selectedProject}
|
selectedProject={selectedProject}
|
||||||
setSelectedProject={setSelectedProject}
|
setSelectedProject={setSelectedProject}
|
||||||
/>
|
/>
|
||||||
|
@ -162,8 +160,9 @@ export default function FileTreeImportFromProject() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectProject({ projectId, selectedProject, setSelectedProject }) {
|
function SelectProject({ selectedProject, setSelectedProject }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { _id: projectId } = useProjectContext(projectContextPropTypes)
|
||||||
|
|
||||||
const { data, error, loading } = useUserProjects()
|
const { data, error, loading } = useUserProjects()
|
||||||
|
|
||||||
|
@ -219,11 +218,14 @@ function SelectProject({ projectId, selectedProject, setSelectedProject }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SelectProject.propTypes = {
|
SelectProject.propTypes = {
|
||||||
projectId: PropTypes.string.isRequired,
|
|
||||||
selectedProject: PropTypes.object,
|
selectedProject: PropTypes.object,
|
||||||
setSelectedProject: PropTypes.func.isRequired,
|
setSelectedProject: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectContextPropTypes = {
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
function SelectProjectOutputFile({
|
function SelectProjectOutputFile({
|
||||||
selectedProjectId,
|
selectedProjectId,
|
||||||
selectedProjectOutputFile,
|
selectedProjectOutputFile,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import Uppy from '@uppy/core'
|
||||||
import XHRUpload from '@uppy/xhr-upload'
|
import XHRUpload from '@uppy/xhr-upload'
|
||||||
import { Dashboard, useUppy } from '@uppy/react'
|
import { Dashboard, useUppy } from '@uppy/react'
|
||||||
import { useFileTreeActionable } from '../../../contexts/file-tree-actionable'
|
import { useFileTreeActionable } from '../../../contexts/file-tree-actionable'
|
||||||
import { useFileTreeMainContext } from '../../../contexts/file-tree-main'
|
import { useProjectContext } from '../../../../../shared/context/project-context'
|
||||||
|
|
||||||
import '@uppy/core/dist/style.css'
|
import '@uppy/core/dist/style.css'
|
||||||
import '@uppy/dashboard/dist/style.css'
|
import '@uppy/dashboard/dist/style.css'
|
||||||
|
@ -15,7 +15,7 @@ import ErrorMessage from '../error-message'
|
||||||
|
|
||||||
export default function FileTreeUploadDoc() {
|
export default function FileTreeUploadDoc() {
|
||||||
const { parentFolderId, cancel, isDuplicate } = useFileTreeActionable()
|
const { parentFolderId, cancel, isDuplicate } = useFileTreeActionable()
|
||||||
const { projectId } = useFileTreeMainContext()
|
const { _id: projectId } = useProjectContext(projectContextPropTypes)
|
||||||
|
|
||||||
const [error, setError] = useState()
|
const [error, setError] = useState()
|
||||||
|
|
||||||
|
@ -162,6 +162,10 @@ export default function FileTreeUploadDoc() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectContextPropTypes = {
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
function UploadErrorMessage({ error, maxNumberOfFiles }) {
|
function UploadErrorMessage({ error, maxNumberOfFiles }) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case 'too-many-files':
|
case 'too-many-files':
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
import { useFileTreeMainContext } from '../../contexts/file-tree-main'
|
import { useProjectContext } from '../../../../shared/context/project-context'
|
||||||
|
|
||||||
// handle "not-logged-in" errors by redirecting to the login page
|
// handle "not-logged-in" errors by redirecting to the login page
|
||||||
export default function RedirectToLogin() {
|
export default function RedirectToLogin() {
|
||||||
const { projectId } = useFileTreeMainContext()
|
const { _id: projectId } = useProjectContext(projectContextPropTypes)
|
||||||
|
|
||||||
const [secondsToRedirect, setSecondsToRedirect] = useState(10)
|
const [secondsToRedirect, setSecondsToRedirect] = useState(10)
|
||||||
|
|
||||||
|
@ -35,3 +36,7 @@ export default function RedirectToLogin() {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectContextPropTypes = {
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
|
import scrollIntoViewIfNeeded from 'scroll-into-view-if-needed'
|
||||||
|
|
||||||
|
import { useEditorContext } from '../../../../shared/context/editor-context'
|
||||||
import { useFileTreeMainContext } from '../../contexts/file-tree-main'
|
import { useFileTreeMainContext } from '../../contexts/file-tree-main'
|
||||||
import { useDraggable } from '../../contexts/file-tree-draggable'
|
import { useDraggable } from '../../contexts/file-tree-draggable'
|
||||||
|
|
||||||
|
@ -11,12 +12,15 @@ import FileTreeItemMenu from './file-tree-item-menu'
|
||||||
import { useFileTreeSelectable } from '../../contexts/file-tree-selectable'
|
import { useFileTreeSelectable } from '../../contexts/file-tree-selectable'
|
||||||
|
|
||||||
function FileTreeItemInner({ id, name, isSelected, icons }) {
|
function FileTreeItemInner({ id, name, isSelected, icons }) {
|
||||||
const { hasWritePermissions, setContextMenuCoords } = useFileTreeMainContext()
|
const { permissionsLevel } = useEditorContext(editorContextPropTypes)
|
||||||
|
const { setContextMenuCoords } = useFileTreeMainContext()
|
||||||
|
|
||||||
const { selectedEntityIds } = useFileTreeSelectable()
|
const { selectedEntityIds } = useFileTreeSelectable()
|
||||||
|
|
||||||
const hasMenu =
|
const hasMenu =
|
||||||
hasWritePermissions && isSelected && selectedEntityIds.size === 1
|
permissionsLevel !== 'readOnly' &&
|
||||||
|
isSelected &&
|
||||||
|
selectedEntityIds.size === 1
|
||||||
|
|
||||||
const { isDragging, dragRef, setIsDraggable } = useDraggable(id)
|
const { isDragging, dragRef, setIsDraggable } = useDraggable(id)
|
||||||
|
|
||||||
|
@ -82,4 +86,8 @@ FileTreeItemInner.propTypes = {
|
||||||
icons: PropTypes.node,
|
icons: PropTypes.node,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editorContextPropTypes = {
|
||||||
|
permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
|
||||||
|
}
|
||||||
|
|
||||||
export default FileTreeItemInner
|
export default FileTreeItemInner
|
||||||
|
|
|
@ -4,19 +4,16 @@ import PropTypes from 'prop-types'
|
||||||
import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-focus'
|
import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-focus'
|
||||||
|
|
||||||
import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
|
import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
|
||||||
import { useFileTreeMainContext } from '../../contexts/file-tree-main'
|
|
||||||
|
|
||||||
function FileTreeItemName({ name, isSelected, setIsDraggable }) {
|
function FileTreeItemName({ name, isSelected, setIsDraggable }) {
|
||||||
const { hasWritePermissions } = useFileTreeMainContext()
|
|
||||||
|
|
||||||
const { isRenaming, startRenaming, finishRenaming, error, cancel } =
|
const { isRenaming, startRenaming, finishRenaming, error, cancel } =
|
||||||
useFileTreeActionable()
|
useFileTreeActionable()
|
||||||
|
|
||||||
const isRenamingEntity = isRenaming && isSelected && !error
|
const isRenamingEntity = isRenaming && isSelected && !error
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsDraggable(hasWritePermissions && !isRenamingEntity)
|
setIsDraggable(!isRenamingEntity)
|
||||||
}, [setIsDraggable, hasWritePermissions, isRenamingEntity])
|
}, [setIsDraggable, isRenamingEntity])
|
||||||
|
|
||||||
if (isRenamingEntity) {
|
if (isRenamingEntity) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { useEffect } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||||
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
import FileTreeContext from './file-tree-context'
|
import FileTreeContext from './file-tree-context'
|
||||||
import FileTreeDraggablePreviewLayer from './file-tree-draggable-preview-layer'
|
import FileTreeDraggablePreviewLayer from './file-tree-draggable-preview-layer'
|
||||||
import FileTreeFolderList from './file-tree-folder-list'
|
import FileTreeFolderList from './file-tree-folder-list'
|
||||||
|
@ -19,11 +20,6 @@ import { useFileTreeSocketListener } from '../hooks/file-tree-socket-listener'
|
||||||
import FileTreeModalCreateFile from './modals/file-tree-modal-create-file'
|
import FileTreeModalCreateFile from './modals/file-tree-modal-create-file'
|
||||||
|
|
||||||
const FileTreeRoot = React.memo(function FileTreeRoot({
|
const FileTreeRoot = React.memo(function FileTreeRoot({
|
||||||
projectId,
|
|
||||||
rootFolder,
|
|
||||||
rootDocId,
|
|
||||||
hasWritePermissions,
|
|
||||||
userHasFeature,
|
|
||||||
refProviders,
|
refProviders,
|
||||||
reindexReferences,
|
reindexReferences,
|
||||||
setRefProviderEnabled,
|
setRefProviderEnabled,
|
||||||
|
@ -32,6 +28,9 @@ const FileTreeRoot = React.memo(function FileTreeRoot({
|
||||||
onInit,
|
onInit,
|
||||||
isConnected,
|
isConnected,
|
||||||
}) {
|
}) {
|
||||||
|
const { _id: projectId, rootFolder } = useProjectContext(
|
||||||
|
projectContextPropTypes
|
||||||
|
)
|
||||||
const isReady = projectId && rootFolder
|
const isReady = projectId && rootFolder
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -41,15 +40,10 @@ const FileTreeRoot = React.memo(function FileTreeRoot({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileTreeContext
|
<FileTreeContext
|
||||||
projectId={projectId}
|
|
||||||
hasWritePermissions={hasWritePermissions}
|
|
||||||
userHasFeature={userHasFeature}
|
|
||||||
refProviders={refProviders}
|
refProviders={refProviders}
|
||||||
setRefProviderEnabled={setRefProviderEnabled}
|
setRefProviderEnabled={setRefProviderEnabled}
|
||||||
setStartedFreeTrial={setStartedFreeTrial}
|
setStartedFreeTrial={setStartedFreeTrial}
|
||||||
reindexReferences={reindexReferences}
|
reindexReferences={reindexReferences}
|
||||||
rootFolder={rootFolder}
|
|
||||||
rootDocId={rootDocId}
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
>
|
>
|
||||||
{isConnected ? null : <div className="disconnected-overlay" />}
|
{isConnected ? null : <div className="disconnected-overlay" />}
|
||||||
|
@ -90,18 +84,18 @@ function FileTreeRootFolder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTreeRoot.propTypes = {
|
FileTreeRoot.propTypes = {
|
||||||
projectId: PropTypes.string,
|
|
||||||
rootFolder: PropTypes.array,
|
|
||||||
rootDocId: PropTypes.string,
|
|
||||||
hasWritePermissions: PropTypes.bool.isRequired,
|
|
||||||
onSelect: PropTypes.func.isRequired,
|
onSelect: PropTypes.func.isRequired,
|
||||||
onInit: PropTypes.func.isRequired,
|
onInit: PropTypes.func.isRequired,
|
||||||
isConnected: PropTypes.bool.isRequired,
|
isConnected: PropTypes.bool.isRequired,
|
||||||
setRefProviderEnabled: PropTypes.func.isRequired,
|
setRefProviderEnabled: PropTypes.func.isRequired,
|
||||||
userHasFeature: PropTypes.func.isRequired,
|
|
||||||
setStartedFreeTrial: PropTypes.func.isRequired,
|
setStartedFreeTrial: PropTypes.func.isRequired,
|
||||||
reindexReferences: PropTypes.func.isRequired,
|
reindexReferences: PropTypes.func.isRequired,
|
||||||
refProviders: PropTypes.object.isRequired,
|
refProviders: PropTypes.object.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectContextPropTypes = {
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
rootFolder: PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
export default withErrorBoundary(FileTreeRoot, FileTreeError)
|
export default withErrorBoundary(FileTreeRoot, FileTreeError)
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import TooltipButton from '../../../shared/components/tooltip-button'
|
import TooltipButton from '../../../shared/components/tooltip-button'
|
||||||
|
|
||||||
import { useFileTreeMainContext } from '../contexts/file-tree-main'
|
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||||
import { useFileTreeActionable } from '../contexts/file-tree-actionable'
|
import { useFileTreeActionable } from '../contexts/file-tree-actionable'
|
||||||
|
|
||||||
function FileTreeToolbar() {
|
function FileTreeToolbar() {
|
||||||
const { hasWritePermissions } = useFileTreeMainContext()
|
const { permissionsLevel } = useEditorContext(editorContextPropTypes)
|
||||||
|
|
||||||
if (!hasWritePermissions) return null
|
if (permissionsLevel === 'readOnly') return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="toolbar toolbar-filetree">
|
<div className="toolbar toolbar-filetree">
|
||||||
|
@ -19,6 +20,10 @@ function FileTreeToolbar() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editorContextPropTypes = {
|
||||||
|
permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
|
||||||
|
}
|
||||||
|
|
||||||
function FileTreeToolbarLeft() {
|
function FileTreeToolbarLeft() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -20,7 +20,8 @@ import { findInTree, findInTreeOrThrow } from '../util/find-in-tree'
|
||||||
import { isNameUniqueInFolder } from '../util/is-name-unique-in-folder'
|
import { isNameUniqueInFolder } from '../util/is-name-unique-in-folder'
|
||||||
import { isBlockedFilename, isCleanFilename } from '../util/safe-path'
|
import { isBlockedFilename, isCleanFilename } from '../util/safe-path'
|
||||||
|
|
||||||
import { useFileTreeMainContext } from './file-tree-main'
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
|
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||||
import { useFileTreeMutable } from './file-tree-mutable'
|
import { useFileTreeMutable } from './file-tree-mutable'
|
||||||
import { useFileTreeSelectable } from './file-tree-selectable'
|
import { useFileTreeSelectable } from './file-tree-selectable'
|
||||||
|
|
||||||
|
@ -117,15 +118,17 @@ function fileTreeActionableReducer(state, action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FileTreeActionableProvider({ hasWritePermissions, children }) {
|
export function FileTreeActionableProvider({ children }) {
|
||||||
|
const { _id: projectId } = useProjectContext(projectContextPropTypes)
|
||||||
|
const { permissionsLevel } = useEditorContext(editorContextPropTypes)
|
||||||
|
|
||||||
const [state, dispatch] = useReducer(
|
const [state, dispatch] = useReducer(
|
||||||
hasWritePermissions
|
permissionsLevel === 'readOnly'
|
||||||
? fileTreeActionableReducer
|
? fileTreeActionableReadOnlyReducer
|
||||||
: fileTreeActionableReadOnlyReducer,
|
: fileTreeActionableReducer,
|
||||||
defaultState
|
defaultState
|
||||||
)
|
)
|
||||||
|
|
||||||
const { projectId } = useFileTreeMainContext()
|
|
||||||
const { fileTreeData, dispatchRename, dispatchMove } = useFileTreeMutable()
|
const { fileTreeData, dispatchRename, dispatchMove } = useFileTreeMutable()
|
||||||
const { selectedEntityIds } = useFileTreeSelectable()
|
const { selectedEntityIds } = useFileTreeSelectable()
|
||||||
|
|
||||||
|
@ -370,13 +373,20 @@ export function FileTreeActionableProvider({ hasWritePermissions, children }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTreeActionableProvider.propTypes = {
|
FileTreeActionableProvider.propTypes = {
|
||||||
hasWritePermissions: PropTypes.bool.isRequired,
|
|
||||||
children: PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
PropTypes.arrayOf(PropTypes.node),
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
PropTypes.node,
|
PropTypes.node,
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectContextPropTypes = {
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
const editorContextPropTypes = {
|
||||||
|
permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
|
||||||
|
}
|
||||||
|
|
||||||
export function useFileTreeActionable() {
|
export function useFileTreeActionable() {
|
||||||
const context = useContext(FileTreeActionableContext)
|
const context = useContext(FileTreeActionableContext)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
import { useFileTreeActionable } from './file-tree-actionable'
|
import { useFileTreeActionable } from './file-tree-actionable'
|
||||||
import { useFileTreeMutable } from './file-tree-mutable'
|
import { useFileTreeMutable } from './file-tree-mutable'
|
||||||
import { useFileTreeSelectable } from '../contexts/file-tree-selectable'
|
import { useFileTreeSelectable } from '../contexts/file-tree-selectable'
|
||||||
import { useFileTreeMainContext } from './file-tree-main'
|
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||||
|
|
||||||
// HACK ALERT
|
// HACK ALERT
|
||||||
// DnD binds drag and drop events on window and stop propagation if the dragged
|
// DnD binds drag and drop events on window and stop propagation if the dragged
|
||||||
|
@ -76,11 +76,11 @@ FileTreeDraggableProvider.propTypes = {
|
||||||
export function useDraggable(draggedEntityId) {
|
export function useDraggable(draggedEntityId) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { hasWritePermissions } = useFileTreeMainContext()
|
const { permissionsLevel } = useEditorContext(editorContextPropTypes)
|
||||||
const { fileTreeData } = useFileTreeMutable()
|
const { fileTreeData } = useFileTreeMutable()
|
||||||
const { selectedEntityIds } = useFileTreeSelectable()
|
const { selectedEntityIds } = useFileTreeSelectable()
|
||||||
|
|
||||||
const [isDraggable, setIsDraggable] = useState(hasWritePermissions)
|
const [isDraggable, setIsDraggable] = useState(true)
|
||||||
|
|
||||||
const item = { type: DRAGGABLE_TYPE }
|
const item = { type: DRAGGABLE_TYPE }
|
||||||
const [{ isDragging }, dragRef, preview] = useDrag({
|
const [{ isDragging }, dragRef, preview] = useDrag({
|
||||||
|
@ -98,7 +98,7 @@ export function useDraggable(draggedEntityId) {
|
||||||
collect: monitor => ({
|
collect: monitor => ({
|
||||||
isDragging: !!monitor.isDragging(),
|
isDragging: !!monitor.isDragging(),
|
||||||
}),
|
}),
|
||||||
canDrag: () => isDraggable,
|
canDrag: () => permissionsLevel !== 'readOnly' && isDraggable,
|
||||||
})
|
})
|
||||||
|
|
||||||
// remove the automatic preview as we're using a custom preview via
|
// remove the automatic preview as we're using a custom preview via
|
||||||
|
@ -114,6 +114,10 @@ export function useDraggable(draggedEntityId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editorContextPropTypes = {
|
||||||
|
permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
|
||||||
|
}
|
||||||
|
|
||||||
export function useDroppable(droppedEntityId) {
|
export function useDroppable(droppedEntityId) {
|
||||||
const { finishMoving } = useFileTreeActionable()
|
const { finishMoving } = useFileTreeActionable()
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,6 @@ export function useFileTreeMainContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FileTreeMainProvider = function ({
|
export const FileTreeMainProvider = function ({
|
||||||
projectId,
|
|
||||||
hasWritePermissions,
|
|
||||||
userHasFeature,
|
|
||||||
refProviders,
|
refProviders,
|
||||||
reindexReferences,
|
reindexReferences,
|
||||||
setRefProviderEnabled,
|
setRefProviderEnabled,
|
||||||
|
@ -30,9 +27,6 @@ export const FileTreeMainProvider = function ({
|
||||||
return (
|
return (
|
||||||
<FileTreeMainContext.Provider
|
<FileTreeMainContext.Provider
|
||||||
value={{
|
value={{
|
||||||
projectId,
|
|
||||||
hasWritePermissions,
|
|
||||||
userHasFeature,
|
|
||||||
refProviders,
|
refProviders,
|
||||||
reindexReferences,
|
reindexReferences,
|
||||||
setRefProviderEnabled,
|
setRefProviderEnabled,
|
||||||
|
@ -47,9 +41,6 @@ export const FileTreeMainProvider = function ({
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTreeMainProvider.propTypes = {
|
FileTreeMainProvider.propTypes = {
|
||||||
projectId: PropTypes.string.isRequired,
|
|
||||||
hasWritePermissions: PropTypes.bool.isRequired,
|
|
||||||
userHasFeature: PropTypes.func.isRequired,
|
|
||||||
reindexReferences: PropTypes.func.isRequired,
|
reindexReferences: PropTypes.func.isRequired,
|
||||||
refProviders: PropTypes.object.isRequired,
|
refProviders: PropTypes.object.isRequired,
|
||||||
setRefProviderEnabled: PropTypes.func.isRequired,
|
setRefProviderEnabled: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
moveInTree,
|
moveInTree,
|
||||||
createEntityInTree,
|
createEntityInTree,
|
||||||
} from '../util/mutate-in-tree'
|
} from '../util/mutate-in-tree'
|
||||||
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
|
|
||||||
const FileTreeMutableContext = createContext()
|
const FileTreeMutableContext = createContext()
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ const ACTION_TYPES = {
|
||||||
RESET: 'RESET',
|
RESET: 'RESET',
|
||||||
DELETE: 'DELETE',
|
DELETE: 'DELETE',
|
||||||
MOVE: 'MOVE',
|
MOVE: 'MOVE',
|
||||||
CREATE_ENTITY: 'CREATE_ENTITY',
|
CREATE: 'CREATE',
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileTreeMutableReducer({ fileTreeData }, action) {
|
function fileTreeMutableReducer({ fileTreeData }, action) {
|
||||||
|
@ -68,7 +69,7 @@ function fileTreeMutableReducer({ fileTreeData }, action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case ACTION_TYPES.CREATE_ENTITY: {
|
case ACTION_TYPES.CREATE: {
|
||||||
const newFileTreeData = createEntityInTree(
|
const newFileTreeData = createEntityInTree(
|
||||||
fileTreeData,
|
fileTreeData,
|
||||||
action.parentFolderId,
|
action.parentFolderId,
|
||||||
|
@ -92,7 +93,9 @@ const initialState = rootFolder => ({
|
||||||
fileCount: countFiles(rootFolder[0]),
|
fileCount: countFiles(rootFolder[0]),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const FileTreeMutableProvider = function ({ rootFolder, children }) {
|
export const FileTreeMutableProvider = function ({ children }) {
|
||||||
|
const { rootFolder } = useProjectContext(projectContextPropTypes)
|
||||||
|
|
||||||
const [{ fileTreeData, fileCount }, dispatch] = useReducer(
|
const [{ fileTreeData, fileCount }, dispatch] = useReducer(
|
||||||
fileTreeMutableReducer,
|
fileTreeMutableReducer,
|
||||||
rootFolder,
|
rootFolder,
|
||||||
|
@ -109,7 +112,7 @@ export const FileTreeMutableProvider = function ({ rootFolder, children }) {
|
||||||
const dispatchCreateFolder = useCallback((parentFolderId, entity) => {
|
const dispatchCreateFolder = useCallback((parentFolderId, entity) => {
|
||||||
entity.type = 'folder'
|
entity.type = 'folder'
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTION_TYPES.CREATE_ENTITY,
|
type: ACTION_TYPES.CREATE,
|
||||||
parentFolderId,
|
parentFolderId,
|
||||||
entity,
|
entity,
|
||||||
})
|
})
|
||||||
|
@ -118,7 +121,7 @@ export const FileTreeMutableProvider = function ({ rootFolder, children }) {
|
||||||
const dispatchCreateDoc = useCallback((parentFolderId, entity) => {
|
const dispatchCreateDoc = useCallback((parentFolderId, entity) => {
|
||||||
entity.type = 'doc'
|
entity.type = 'doc'
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTION_TYPES.CREATE_ENTITY,
|
type: ACTION_TYPES.CREATE,
|
||||||
parentFolderId,
|
parentFolderId,
|
||||||
entity,
|
entity,
|
||||||
})
|
})
|
||||||
|
@ -127,7 +130,7 @@ export const FileTreeMutableProvider = function ({ rootFolder, children }) {
|
||||||
const dispatchCreateFile = useCallback((parentFolderId, entity) => {
|
const dispatchCreateFile = useCallback((parentFolderId, entity) => {
|
||||||
entity.type = 'fileRef'
|
entity.type = 'fileRef'
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTION_TYPES.CREATE_ENTITY,
|
type: ACTION_TYPES.CREATE,
|
||||||
parentFolderId,
|
parentFolderId,
|
||||||
entity,
|
entity,
|
||||||
})
|
})
|
||||||
|
@ -168,13 +171,24 @@ export const FileTreeMutableProvider = function ({ rootFolder, children }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTreeMutableProvider.propTypes = {
|
FileTreeMutableProvider.propTypes = {
|
||||||
rootFolder: PropTypes.array.isRequired,
|
|
||||||
children: PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
PropTypes.arrayOf(PropTypes.node),
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
PropTypes.node,
|
PropTypes.node,
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectContextPropTypes = {
|
||||||
|
rootFolder: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
docs: PropTypes.array.isRequired,
|
||||||
|
fileRefs: PropTypes.array.isRequired,
|
||||||
|
folders: PropTypes.array.isRequired,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
export function useFileTreeMutable() {
|
export function useFileTreeMutable() {
|
||||||
const context = useContext(FileTreeMutableContext)
|
const context = useContext(FileTreeMutableContext)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@ import _ from 'lodash'
|
||||||
|
|
||||||
import { findInTree } from '../util/find-in-tree'
|
import { findInTree } from '../util/find-in-tree'
|
||||||
import { useFileTreeMutable } from './file-tree-mutable'
|
import { useFileTreeMutable } from './file-tree-mutable'
|
||||||
import { useFileTreeMainContext } from './file-tree-main'
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
|
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||||
import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
||||||
import usePreviousValue from '../../../shared/hooks/use-previous-value'
|
import usePreviousValue from '../../../shared/hooks/use-previous-value'
|
||||||
|
|
||||||
|
@ -73,13 +74,11 @@ function fileTreeSelectableReadOnlyReducer(selectedEntityIds, action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FileTreeSelectableProvider({
|
export function FileTreeSelectableProvider({ onSelect, children }) {
|
||||||
hasWritePermissions,
|
const { _id: projectId, rootDoc_id: rootDocId } = useProjectContext(
|
||||||
rootDocId,
|
projectContextPropTypes
|
||||||
onSelect,
|
)
|
||||||
children,
|
const { permissionsLevel } = useEditorContext(editorContextPropTypes)
|
||||||
}) {
|
|
||||||
const { projectId } = useFileTreeMainContext()
|
|
||||||
|
|
||||||
const [initialSelectedEntityId] = usePersistedState(
|
const [initialSelectedEntityId] = usePersistedState(
|
||||||
`doc.open_id.${projectId}`,
|
`doc.open_id.${projectId}`,
|
||||||
|
@ -89,9 +88,9 @@ export function FileTreeSelectableProvider({
|
||||||
const { fileTreeData } = useFileTreeMutable()
|
const { fileTreeData } = useFileTreeMutable()
|
||||||
|
|
||||||
const [selectedEntityIds, dispatch] = useReducer(
|
const [selectedEntityIds, dispatch] = useReducer(
|
||||||
hasWritePermissions
|
permissionsLevel === 'readOnly'
|
||||||
? fileTreeSelectableReadWriteReducer
|
? fileTreeSelectableReadOnlyReducer
|
||||||
: fileTreeSelectableReadOnlyReducer,
|
: fileTreeSelectableReadWriteReducer,
|
||||||
null,
|
null,
|
||||||
() => {
|
() => {
|
||||||
if (!initialSelectedEntityId) return new Set()
|
if (!initialSelectedEntityId) return new Set()
|
||||||
|
@ -179,8 +178,6 @@ export function FileTreeSelectableProvider({
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTreeSelectableProvider.propTypes = {
|
FileTreeSelectableProvider.propTypes = {
|
||||||
hasWritePermissions: PropTypes.bool.isRequired,
|
|
||||||
rootDocId: PropTypes.string,
|
|
||||||
onSelect: PropTypes.func.isRequired,
|
onSelect: PropTypes.func.isRequired,
|
||||||
children: PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
PropTypes.arrayOf(PropTypes.node),
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
|
@ -188,6 +185,14 @@ FileTreeSelectableProvider.propTypes = {
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectContextPropTypes = {
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
rootDoc_id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const editorContextPropTypes = {
|
||||||
|
permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
|
||||||
|
}
|
||||||
export function useSelectableEntity(id) {
|
export function useSelectableEntity(id) {
|
||||||
const { selectedEntityIds, selectOrMultiSelectEntity } = useContext(
|
const { selectedEntityIds, selectOrMultiSelectEntity } = useContext(
|
||||||
FileTreeSelectableContext
|
FileTreeSelectableContext
|
||||||
|
|
|
@ -13,22 +13,12 @@ App.controller(
|
||||||
ide
|
ide
|
||||||
// eventTracking
|
// eventTracking
|
||||||
) {
|
) {
|
||||||
$scope.projectId = ide.project_id
|
|
||||||
$scope.rootFolder = null
|
|
||||||
$scope.rootDocId = null
|
|
||||||
$scope.hasWritePermissions = false
|
|
||||||
$scope.isConnected = true
|
$scope.isConnected = true
|
||||||
|
|
||||||
$scope.$on('project:joined', () => {
|
$scope.$on('project:joined', () => {
|
||||||
$scope.rootFolder = $scope.project.rootFolder
|
|
||||||
$scope.rootDocId = $scope.project.rootDoc_id
|
|
||||||
$scope.$emit('file-tree:initialized')
|
$scope.$emit('file-tree:initialized')
|
||||||
})
|
})
|
||||||
|
|
||||||
$scope.$watch('permissions.write', hasWritePermissions => {
|
|
||||||
$scope.hasWritePermissions = hasWritePermissions
|
|
||||||
})
|
|
||||||
|
|
||||||
$scope.$watch('editor.open_doc_id', openDocId => {
|
$scope.$watch('editor.open_doc_id', openDocId => {
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent('editor.openDoc', { detail: openDocId })
|
new CustomEvent('editor.openDoc', { detail: openDocId })
|
||||||
|
@ -85,12 +75,6 @@ App.controller(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.userHasFeature = feature => ide.$scope.user.features[feature]
|
|
||||||
|
|
||||||
$scope.$watch('permissions.write', hasWritePermissions => {
|
|
||||||
$scope.hasWritePermissions = hasWritePermissions
|
|
||||||
})
|
|
||||||
|
|
||||||
$scope.refProviders = ide.$scope.user.refProviders || {}
|
$scope.refProviders = ide.$scope.user.refProviders || {}
|
||||||
|
|
||||||
ide.$scope.$watch(
|
ide.$scope.$watch(
|
||||||
|
|
|
@ -4,6 +4,14 @@ import useScopeValue from '../hooks/use-scope-value'
|
||||||
|
|
||||||
const ProjectContext = createContext()
|
const ProjectContext = createContext()
|
||||||
|
|
||||||
|
const fileTreeDataPropType = PropTypes.shape({
|
||||||
|
_id: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
docs: PropTypes.array.isRequired,
|
||||||
|
fileRefs: PropTypes.array.isRequired,
|
||||||
|
folders: PropTypes.array.isRequired,
|
||||||
|
})
|
||||||
|
|
||||||
ProjectContext.Provider.propTypes = {
|
ProjectContext.Provider.propTypes = {
|
||||||
value: PropTypes.shape({
|
value: PropTypes.shape({
|
||||||
_id: PropTypes.string.isRequired,
|
_id: PropTypes.string.isRequired,
|
||||||
|
@ -23,6 +31,9 @@ ProjectContext.Provider.propTypes = {
|
||||||
collaborators: PropTypes.number,
|
collaborators: PropTypes.number,
|
||||||
compileGroup: PropTypes.oneOf(['alpha', 'standard', 'priority']),
|
compileGroup: PropTypes.oneOf(['alpha', 'standard', 'priority']),
|
||||||
trackChangesVisible: PropTypes.bool,
|
trackChangesVisible: PropTypes.bool,
|
||||||
|
references: PropTypes.bool,
|
||||||
|
mendeley: PropTypes.bool,
|
||||||
|
zotero: PropTypes.bool,
|
||||||
}),
|
}),
|
||||||
publicAccesLevel: PropTypes.string,
|
publicAccesLevel: PropTypes.string,
|
||||||
tokens: PropTypes.shape({
|
tokens: PropTypes.shape({
|
||||||
|
@ -33,6 +44,7 @@ ProjectContext.Provider.propTypes = {
|
||||||
_id: PropTypes.string.isRequired,
|
_id: PropTypes.string.isRequired,
|
||||||
email: PropTypes.string.isRequired,
|
email: PropTypes.string.isRequired,
|
||||||
}),
|
}),
|
||||||
|
rootFolder: PropTypes.arrayOf(fileTreeDataPropType),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import MockedSocket from 'socket.io-mock'
|
import MockedSocket from 'socket.io-mock'
|
||||||
|
|
||||||
import { ContextRoot } from '../js/shared/context/root-context'
|
import { withContextRoot } from './utils/with-context-root'
|
||||||
import { rootFolderBase } from './fixtures/file-tree-base'
|
import { rootFolderBase } from './fixtures/file-tree-base'
|
||||||
import { rootFolderLimit } from './fixtures/file-tree-limit'
|
import { rootFolderLimit } from './fixtures/file-tree-limit'
|
||||||
import FileTreeRoot from '../js/features/file-tree/components/file-tree-root'
|
import FileTreeRoot from '../js/features/file-tree/components/file-tree-root'
|
||||||
|
@ -12,6 +12,12 @@ const MOCK_DELAY = 2000
|
||||||
window._ide = {
|
window._ide = {
|
||||||
socket: new MockedSocket(),
|
socket: new MockedSocket(),
|
||||||
}
|
}
|
||||||
|
const DEFAULT_PROJECT = {
|
||||||
|
_id: '123abc',
|
||||||
|
name: 'Some Project',
|
||||||
|
rootDocId: '5e74f1a7ce17ae0041dfd056',
|
||||||
|
rootFolder: rootFolderBase,
|
||||||
|
}
|
||||||
|
|
||||||
function defaultSetupMocks(fetchMock) {
|
function defaultSetupMocks(fetchMock) {
|
||||||
fetchMock
|
fetchMock
|
||||||
|
@ -80,13 +86,25 @@ function defaultSetupMocks(fetchMock) {
|
||||||
export const FullTree = args => {
|
export const FullTree = args => {
|
||||||
useFetchMock(defaultSetupMocks)
|
useFetchMock(defaultSetupMocks)
|
||||||
|
|
||||||
return <FileTreeRoot {...args} />
|
return withContextRoot(<FileTreeRoot {...args} />, {
|
||||||
|
project: DEFAULT_PROJECT,
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ReadOnly = args => <FileTreeRoot {...args} />
|
export const ReadOnly = args => {
|
||||||
ReadOnly.args = { hasWritePermissions: false }
|
return withContextRoot(<FileTreeRoot {...args} />, {
|
||||||
|
project: DEFAULT_PROJECT,
|
||||||
|
permissionsLevel: 'readOnly',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const Disconnected = args => <FileTreeRoot {...args} />
|
export const Disconnected = args => {
|
||||||
|
return withContextRoot(<FileTreeRoot {...args} />, {
|
||||||
|
project: DEFAULT_PROJECT,
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
})
|
||||||
|
}
|
||||||
Disconnected.args = { isConnected: false }
|
Disconnected.args = { isConnected: false }
|
||||||
|
|
||||||
export const NetworkErrors = args => {
|
export const NetworkErrors = args => {
|
||||||
|
@ -106,24 +124,31 @@ export const NetworkErrors = args => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return <FileTreeRoot {...args} />
|
return withContextRoot(<FileTreeRoot {...args} />, {
|
||||||
|
project: DEFAULT_PROJECT,
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FallbackError = args => <FileTreeError {...args} />
|
export const FallbackError = args => {
|
||||||
|
return withContextRoot(<FileTreeError {...args} />, {
|
||||||
|
project: DEFAULT_PROJECT,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const FilesLimit = args => {
|
export const FilesLimit = args => {
|
||||||
useFetchMock(defaultSetupMocks)
|
useFetchMock(defaultSetupMocks)
|
||||||
|
|
||||||
return <FileTreeRoot {...args} />
|
return withContextRoot(<FileTreeRoot {...args} />, {
|
||||||
|
project: { ...DEFAULT_PROJECT, rootFolder: rootFolderLimit },
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
FilesLimit.args = { rootFolder: rootFolderLimit }
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'File Tree',
|
title: 'File Tree',
|
||||||
component: FileTreeRoot,
|
component: FileTreeRoot,
|
||||||
args: {
|
args: {
|
||||||
rootFolder: rootFolderBase,
|
|
||||||
hasWritePermissions: true,
|
|
||||||
setStartedFreeTrial: () => {
|
setStartedFreeTrial: () => {
|
||||||
console.log('started free trial')
|
console.log('started free trial')
|
||||||
},
|
},
|
||||||
|
@ -131,12 +156,9 @@ export default {
|
||||||
reindexReferences: () => {
|
reindexReferences: () => {
|
||||||
console.log('reindex references')
|
console.log('reindex references')
|
||||||
},
|
},
|
||||||
userHasFeature: () => true,
|
|
||||||
setRefProviderEnabled: provider => {
|
setRefProviderEnabled: provider => {
|
||||||
console.log(`ref provider ${provider} enabled`)
|
console.log(`ref provider ${provider} enabled`)
|
||||||
},
|
},
|
||||||
projectId: '123abc',
|
|
||||||
rootDocId: '5e74f1a7ce17ae0041dfd056',
|
|
||||||
isConnected: true,
|
isConnected: true,
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
|
@ -149,9 +171,7 @@ export default {
|
||||||
<style>{'html, body, .file-tree { height: 100%; width: 100%; }'}</style>
|
<style>{'html, body, .file-tree { height: 100%; width: 100%; }'}</style>
|
||||||
<div className="editor-sidebar full-size">
|
<div className="editor-sidebar full-size">
|
||||||
<div className="file-tree">
|
<div className="file-tree">
|
||||||
<ContextRoot ide={window._ide} settings={{}}>
|
<Story />
|
||||||
<Story />
|
|
||||||
</ContextRoot>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -13,6 +13,15 @@ export function setupContext() {
|
||||||
user: window.user,
|
user: window.user,
|
||||||
project: {
|
project: {
|
||||||
features: {},
|
features: {},
|
||||||
|
rootFolder: [
|
||||||
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
|
docs: [],
|
||||||
|
folders: [],
|
||||||
|
fileRefs: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
$watch: () => {},
|
$watch: () => {},
|
||||||
$applyAsync: () => {},
|
$applyAsync: () => {},
|
||||||
|
@ -25,7 +34,6 @@ export function setupContext() {
|
||||||
pdfViewer: 'js',
|
pdfViewer: 'js',
|
||||||
},
|
},
|
||||||
toggleHistory: () => {},
|
toggleHistory: () => {},
|
||||||
rootFolder: { type: 'folder', children: [] },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window._ide = {
|
window._ide = {
|
||||||
|
|
|
@ -1,15 +1,29 @@
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
import { withContextRoot } from './../../utils/with-context-root'
|
||||||
import FileTreeContext from '../../../js/features/file-tree/components/file-tree-context'
|
import FileTreeContext from '../../../js/features/file-tree/components/file-tree-context'
|
||||||
import FileTreeCreateNameProvider from '../../../js/features/file-tree/contexts/file-tree-create-name'
|
import FileTreeCreateNameProvider from '../../../js/features/file-tree/contexts/file-tree-create-name'
|
||||||
import FileTreeCreateFormProvider from '../../../js/features/file-tree/contexts/file-tree-create-form'
|
import FileTreeCreateFormProvider from '../../../js/features/file-tree/contexts/file-tree-create-form'
|
||||||
import { useFileTreeActionable } from '../../../js/features/file-tree/contexts/file-tree-actionable'
|
import { useFileTreeActionable } from '../../../js/features/file-tree/contexts/file-tree-actionable'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
const defaultContextProps = {
|
export const DEFAULT_PROJECT = {
|
||||||
projectId: 'project-1',
|
_id: '123abc',
|
||||||
hasWritePermissions: true,
|
name: 'Some Project',
|
||||||
userHasFeature: () => true,
|
rootDocId: '5e74f1a7ce17ae0041dfd056',
|
||||||
refProviders: {},
|
rootFolder: [
|
||||||
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
|
docs: [],
|
||||||
|
folders: [],
|
||||||
|
fileRefs: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
features: { mendeley: true, zotero: true },
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultFileTreeContextProps = {
|
||||||
|
refProviders: { mendeley: false, zotero: false },
|
||||||
reindexReferences: () => {
|
reindexReferences: () => {
|
||||||
console.log('reindex references')
|
console.log('reindex references')
|
||||||
},
|
},
|
||||||
|
@ -19,17 +33,6 @@ const defaultContextProps = {
|
||||||
setStartedFreeTrial: () => {
|
setStartedFreeTrial: () => {
|
||||||
console.log('started free trial')
|
console.log('started free trial')
|
||||||
},
|
},
|
||||||
rootFolder: [
|
|
||||||
{
|
|
||||||
docs: [
|
|
||||||
{
|
|
||||||
_id: 'entity-1',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fileRefs: [],
|
|
||||||
folders: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
initialSelectedEntityId: 'entity-1',
|
initialSelectedEntityId: 'entity-1',
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
console.log('selected')
|
console.log('selected')
|
||||||
|
@ -99,11 +102,18 @@ export const mockCreateFileModalFetch = fetchMock =>
|
||||||
})
|
})
|
||||||
|
|
||||||
export const createFileModalDecorator =
|
export const createFileModalDecorator =
|
||||||
(contextProps = {}, createMode = 'doc') =>
|
(
|
||||||
// eslint-disable-next-line react/display-name
|
fileTreeContextProps = {},
|
||||||
|
projectProps = {},
|
||||||
|
createMode = 'doc'
|
||||||
|
// eslint-disable-next-line react/display-name
|
||||||
|
) =>
|
||||||
Story => {
|
Story => {
|
||||||
return (
|
return withContextRoot(
|
||||||
<FileTreeContext {...defaultContextProps} {...contextProps}>
|
<FileTreeContext
|
||||||
|
{...defaultFileTreeContextProps}
|
||||||
|
{...fileTreeContextProps}
|
||||||
|
>
|
||||||
<FileTreeCreateNameProvider>
|
<FileTreeCreateNameProvider>
|
||||||
<FileTreeCreateFormProvider>
|
<FileTreeCreateFormProvider>
|
||||||
<OpenCreateFileModal createMode={createMode}>
|
<OpenCreateFileModal createMode={createMode}>
|
||||||
|
@ -111,7 +121,11 @@ export const createFileModalDecorator =
|
||||||
</OpenCreateFileModal>
|
</OpenCreateFileModal>
|
||||||
</FileTreeCreateFormProvider>
|
</FileTreeCreateFormProvider>
|
||||||
</FileTreeCreateNameProvider>
|
</FileTreeCreateNameProvider>
|
||||||
</FileTreeContext>
|
</FileTreeContext>,
|
||||||
|
{
|
||||||
|
project: { ...DEFAULT_PROJECT, ...projectProps },
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,7 @@ export const MinimalFeatures = args => {
|
||||||
|
|
||||||
return <FileTreeModalCreateFile {...args} />
|
return <FileTreeModalCreateFile {...args} />
|
||||||
}
|
}
|
||||||
MinimalFeatures.decorators = [
|
MinimalFeatures.decorators = [createFileModalDecorator()]
|
||||||
createFileModalDecorator({
|
|
||||||
userHasFeature: () => false,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
export const WithExtraFeatures = args => {
|
export const WithExtraFeatures = args => {
|
||||||
useFetchMock(mockCreateFileModalFetch)
|
useFetchMock(mockCreateFileModalFetch)
|
||||||
|
@ -82,17 +78,22 @@ export const FileLimitReached = args => {
|
||||||
return <FileTreeModalCreateFile {...args} />
|
return <FileTreeModalCreateFile {...args} />
|
||||||
}
|
}
|
||||||
FileLimitReached.decorators = [
|
FileLimitReached.decorators = [
|
||||||
createFileModalDecorator({
|
createFileModalDecorator(
|
||||||
rootFolder: [
|
{},
|
||||||
{
|
{
|
||||||
docs: Array.from({ length: 10 }, (_, index) => ({
|
rootFolder: [
|
||||||
_id: `entity-${index}`,
|
{
|
||||||
})),
|
_id: 'root-folder-id',
|
||||||
fileRefs: [],
|
name: 'rootFolder',
|
||||||
folders: [],
|
docs: Array.from({ length: 10 }, (_, index) => ({
|
||||||
},
|
_id: `entity-${index}`,
|
||||||
],
|
})),
|
||||||
}),
|
fileRefs: [],
|
||||||
|
folders: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import sinon from 'sinon'
|
|
||||||
|
|
||||||
export const contextProps = {
|
|
||||||
projectId: 'test-project',
|
|
||||||
hasWritePermissions: true,
|
|
||||||
userHasFeature: () => true,
|
|
||||||
refProviders: { mendeley: false, zotero: false },
|
|
||||||
reindexReferences: () => {
|
|
||||||
console.log('reindex references')
|
|
||||||
},
|
|
||||||
setRefProviderEnabled: provider => {
|
|
||||||
console.log(`ref provider ${provider} enabled`)
|
|
||||||
},
|
|
||||||
setStartedFreeTrial: () => {
|
|
||||||
console.log('started free trial')
|
|
||||||
},
|
|
||||||
rootFolder: [
|
|
||||||
{
|
|
||||||
docs: [{ _id: 'entity-1' }],
|
|
||||||
fileRefs: [],
|
|
||||||
folders: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
initialSelectedEntityId: 'entity-1',
|
|
||||||
onSelect: sinon.stub(),
|
|
||||||
}
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { screen, render, waitFor, cleanup } from '@testing-library/react'
|
import { screen, waitFor, cleanup } from '@testing-library/react'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
|
|
||||||
import { contextProps } from './context-props'
|
import renderWithContext from '../../helpers/render-with-context'
|
||||||
|
|
||||||
import FileTreeCreateNameInput from '../../../../../../frontend/js/features/file-tree/components/file-tree-create/file-tree-create-name-input'
|
import FileTreeCreateNameInput from '../../../../../../frontend/js/features/file-tree/components/file-tree-create/file-tree-create-name-input'
|
||||||
import FileTreeContext from '../../../../../../frontend/js/features/file-tree/components/file-tree-context'
|
|
||||||
import FileTreeCreateNameProvider from '../../../../../../frontend/js/features/file-tree/contexts/file-tree-create-name'
|
import FileTreeCreateNameProvider from '../../../../../../frontend/js/features/file-tree/contexts/file-tree-create-name'
|
||||||
|
|
||||||
describe('<FileTreeCreateNameInput/>', function () {
|
describe('<FileTreeCreateNameInput/>', function () {
|
||||||
|
@ -21,12 +20,10 @@ describe('<FileTreeCreateNameInput/>', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders an empty input', async function () {
|
it('renders an empty input', async function () {
|
||||||
render(
|
renderWithContext(
|
||||||
<FileTreeContext {...contextProps}>
|
<FileTreeCreateNameProvider>
|
||||||
<FileTreeCreateNameProvider>
|
<FileTreeCreateNameInput />
|
||||||
<FileTreeCreateNameInput />
|
</FileTreeCreateNameProvider>
|
||||||
</FileTreeCreateNameProvider>
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await screen.getByLabelText('File Name')
|
await screen.getByLabelText('File Name')
|
||||||
|
@ -34,15 +31,13 @@ describe('<FileTreeCreateNameInput/>', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders a custom label and placeholder', async function () {
|
it('renders a custom label and placeholder', async function () {
|
||||||
render(
|
renderWithContext(
|
||||||
<FileTreeContext {...contextProps}>
|
<FileTreeCreateNameProvider>
|
||||||
<FileTreeCreateNameProvider>
|
<FileTreeCreateNameInput
|
||||||
<FileTreeCreateNameInput
|
label="File name in this project"
|
||||||
label="File name in this project"
|
placeholder="Enter a file name…"
|
||||||
placeholder="Enter a file name…"
|
/>
|
||||||
/>
|
</FileTreeCreateNameProvider>
|
||||||
</FileTreeCreateNameProvider>
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await screen.getByLabelText('File name in this project')
|
await screen.getByLabelText('File name in this project')
|
||||||
|
@ -50,12 +45,10 @@ describe('<FileTreeCreateNameInput/>', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('uses an initial name', async function () {
|
it('uses an initial name', async function () {
|
||||||
render(
|
renderWithContext(
|
||||||
<FileTreeContext {...contextProps}>
|
<FileTreeCreateNameProvider initialName="test.tex">
|
||||||
<FileTreeCreateNameProvider initialName="test.tex">
|
<FileTreeCreateNameInput />
|
||||||
<FileTreeCreateNameInput />
|
</FileTreeCreateNameProvider>
|
||||||
</FileTreeCreateNameProvider>
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const input = await screen.getByLabelText('File Name')
|
const input = await screen.getByLabelText('File Name')
|
||||||
|
@ -63,12 +56,10 @@ describe('<FileTreeCreateNameInput/>', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('focuses the name', async function () {
|
it('focuses the name', async function () {
|
||||||
render(
|
renderWithContext(
|
||||||
<FileTreeContext {...contextProps}>
|
<FileTreeCreateNameProvider initialName="test.tex">
|
||||||
<FileTreeCreateNameProvider initialName="test.tex">
|
<FileTreeCreateNameInput focusName />
|
||||||
<FileTreeCreateNameInput focusName />
|
</FileTreeCreateNameProvider>
|
||||||
</FileTreeCreateNameProvider>
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const input = await screen.getByLabelText('File Name')
|
const input = await screen.getByLabelText('File Name')
|
||||||
|
|
|
@ -1,19 +1,12 @@
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import * as sinon from 'sinon'
|
import * as sinon from 'sinon'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import {
|
import { screen, fireEvent, cleanup, waitFor } from '@testing-library/react'
|
||||||
screen,
|
|
||||||
render,
|
|
||||||
fireEvent,
|
|
||||||
cleanup,
|
|
||||||
waitFor,
|
|
||||||
} from '@testing-library/react'
|
|
||||||
import fetchMock from 'fetch-mock'
|
import fetchMock from 'fetch-mock'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { contextProps } from './context-props'
|
import renderWithContext from '../../helpers/render-with-context'
|
||||||
import FileTreeModalCreateFile from '../../../../../../frontend/js/features/file-tree/components/modals/file-tree-modal-create-file'
|
import FileTreeModalCreateFile from '../../../../../../frontend/js/features/file-tree/components/modals/file-tree-modal-create-file'
|
||||||
import FileTreeContext from '../../../../../../frontend/js/features/file-tree/components/file-tree-context'
|
|
||||||
import { useFileTreeActionable } from '../../../../../../frontend/js/features/file-tree/contexts/file-tree-actionable'
|
import { useFileTreeActionable } from '../../../../../../frontend/js/features/file-tree/contexts/file-tree-actionable'
|
||||||
import { useFileTreeMutable } from '../../../../../../frontend/js/features/file-tree/contexts/file-tree-mutable'
|
import { useFileTreeMutable } from '../../../../../../frontend/js/features/file-tree/contexts/file-tree-mutable'
|
||||||
|
|
||||||
|
@ -29,11 +22,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handles invalid file names', async function () {
|
it('handles invalid file names', async function () {
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="doc" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="doc" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
const submitButton = screen.getByRole('button', { name: 'Create' })
|
const submitButton = screen.getByRole('button', { name: 'Create' })
|
||||||
|
|
||||||
|
@ -65,6 +54,8 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
it('displays an error when the file limit is reached', async function () {
|
it('displays an error when the file limit is reached', async function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: Array.from({ length: 10 }, (_, index) => ({
|
docs: Array.from({ length: 10 }, (_, index) => ({
|
||||||
_id: `entity-${index}`,
|
_id: `entity-${index}`,
|
||||||
})),
|
})),
|
||||||
|
@ -73,15 +64,9 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="doc" />, {
|
||||||
<FileTreeContext
|
contextProps: { projectRootFolder: rootFolder },
|
||||||
{...contextProps}
|
})
|
||||||
rootFolder={rootFolder}
|
|
||||||
initialSelectedEntityId="entity-1"
|
|
||||||
>
|
|
||||||
<OpenWithMode mode="doc" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
screen.getByRole(
|
screen.getByRole(
|
||||||
(role, element) =>
|
(role, element) =>
|
||||||
|
@ -93,6 +78,8 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
it('displays a warning when the file limit is nearly reached', async function () {
|
it('displays a warning when the file limit is nearly reached', async function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: Array.from({ length: 9 }, (_, index) => ({
|
docs: Array.from({ length: 9 }, (_, index) => ({
|
||||||
_id: `entity-${index}`,
|
_id: `entity-${index}`,
|
||||||
})),
|
})),
|
||||||
|
@ -101,15 +88,9 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="doc" />, {
|
||||||
<FileTreeContext
|
contextProps: { projectRootFolder: rootFolder },
|
||||||
{...contextProps}
|
})
|
||||||
rootFolder={rootFolder}
|
|
||||||
initialSelectedEntityId="entity-1"
|
|
||||||
>
|
|
||||||
<OpenWithMode mode="doc" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
screen.getByText(/This project is approaching the file limit \(\d+\/\d+\)/)
|
screen.getByText(/This project is approaching the file limit \(\d+\/\d+\)/)
|
||||||
})
|
})
|
||||||
|
@ -117,6 +98,8 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
it('counts files in nested folders', async function () {
|
it('counts files in nested folders', async function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: 'entity-1' }],
|
docs: [{ _id: 'entity-1' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [
|
folders: [
|
||||||
|
@ -143,15 +126,9 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="doc" />, {
|
||||||
<FileTreeContext
|
contextProps: { projectRootFolder: rootFolder },
|
||||||
{...contextProps}
|
})
|
||||||
rootFolder={rootFolder}
|
|
||||||
initialSelectedEntityId="entity-1"
|
|
||||||
>
|
|
||||||
<OpenWithMode mode="doc" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
screen.getByText(/This project is approaching the file limit \(\d+\/\d+\)/)
|
screen.getByText(/This project is approaching the file limit \(\d+\/\d+\)/)
|
||||||
})
|
})
|
||||||
|
@ -159,11 +136,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
it('creates a new file when the form is submitted', async function () {
|
it('creates a new file when the form is submitted', async function () {
|
||||||
fetchMock.post('express:/project/:projectId/doc', () => 204)
|
fetchMock.post('express:/project/:projectId/doc', () => 204)
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="doc" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="doc" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
const input = screen.getByLabelText('File Name')
|
const input = screen.getByLabelText('File Name')
|
||||||
await fireEvent.change(input, { target: { value: 'test.tex' } })
|
await fireEvent.change(input, { target: { value: 'test.tex' } })
|
||||||
|
@ -174,7 +147,10 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
fetchMock.called('express:/project/:projectId/doc', {
|
fetchMock.called('express:/project/:projectId/doc', {
|
||||||
body: { name: 'test.tex' },
|
body: {
|
||||||
|
parent_folder_id: 'root-folder-id',
|
||||||
|
name: 'test.tex',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
).to.be.true
|
).to.be.true
|
||||||
})
|
})
|
||||||
|
@ -222,11 +198,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
})
|
})
|
||||||
.post('express:/project/:projectId/linked_file', () => 204)
|
.post('express:/project/:projectId/linked_file', () => 204)
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="project" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="project" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
// initial state, no project selected
|
// initial state, no project selected
|
||||||
const projectInput = screen.getByLabelText('Select a Project')
|
const projectInput = screen.getByLabelText('Select a Project')
|
||||||
|
@ -286,6 +258,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
body: {
|
body: {
|
||||||
name: 'ball.jpg',
|
name: 'ball.jpg',
|
||||||
provider: 'project_output_file',
|
provider: 'project_output_file',
|
||||||
|
parent_folder_id: 'root-folder-id',
|
||||||
data: {
|
data: {
|
||||||
source_project_id: 'project-2',
|
source_project_id: 'project-2',
|
||||||
source_output_file_path: 'ball.jpg',
|
source_output_file_path: 'ball.jpg',
|
||||||
|
@ -323,11 +296,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="project" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="project" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
// should not show the toggle
|
// should not show the toggle
|
||||||
expect(
|
expect(
|
||||||
|
@ -341,11 +310,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
it('import from a URL when the form is submitted', async function () {
|
it('import from a URL when the form is submitted', async function () {
|
||||||
fetchMock.post('express:/project/:projectId/linked_file', () => 204)
|
fetchMock.post('express:/project/:projectId/linked_file', () => 204)
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="url" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="url" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
const urlInput = screen.getByLabelText('URL to fetch the file from')
|
const urlInput = screen.getByLabelText('URL to fetch the file from')
|
||||||
const nameInput = screen.getByLabelText('File Name In This Project')
|
const nameInput = screen.getByLabelText('File Name In This Project')
|
||||||
|
@ -373,6 +338,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
body: {
|
body: {
|
||||||
name: 'test.tex',
|
name: 'test.tex',
|
||||||
provider: 'url',
|
provider: 'url',
|
||||||
|
parent_folder_id: 'root-folder-id',
|
||||||
data: { url: 'https://example.com/example.tex' },
|
data: { url: 'https://example.com/example.tex' },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -386,11 +352,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
requests.push(request)
|
requests.push(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="upload" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="upload" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
// the submit button should not be present
|
// the submit button should not be present
|
||||||
expect(screen.queryByRole('button', { name: 'Create' })).to.be.null
|
expect(screen.queryByRole('button', { name: 'Create' })).to.be.null
|
||||||
|
@ -408,7 +370,9 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
await waitFor(() => expect(requests).to.have.length(1))
|
await waitFor(() => expect(requests).to.have.length(1))
|
||||||
|
|
||||||
const [request] = requests
|
const [request] = requests
|
||||||
expect(request.url).to.equal('/project/test-project/upload')
|
expect(request.url).to.equal(
|
||||||
|
'/project/123abc/upload?folder_id=root-folder-id'
|
||||||
|
)
|
||||||
expect(request.method).to.equal('POST')
|
expect(request.method).to.equal('POST')
|
||||||
|
|
||||||
xhr.restore()
|
xhr.restore()
|
||||||
|
@ -421,11 +385,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
requests.push(request)
|
requests.push(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="upload" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="upload" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
// the submit button should not be present
|
// the submit button should not be present
|
||||||
expect(screen.queryByRole('button', { name: 'Create' })).to.be.null
|
expect(screen.queryByRole('button', { name: 'Create' })).to.be.null
|
||||||
|
@ -443,7 +403,9 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
await waitFor(() => expect(requests).to.have.length(1))
|
await waitFor(() => expect(requests).to.have.length(1))
|
||||||
|
|
||||||
const [request] = requests
|
const [request] = requests
|
||||||
expect(request.url).to.equal('/project/test-project/upload')
|
expect(request.url).to.equal(
|
||||||
|
'/project/123abc/upload?folder_id=root-folder-id'
|
||||||
|
)
|
||||||
expect(request.method).to.equal('POST')
|
expect(request.method).to.equal('POST')
|
||||||
|
|
||||||
xhr.restore()
|
xhr.restore()
|
||||||
|
@ -456,11 +418,7 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
requests.push(request)
|
requests.push(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
renderWithContext(<OpenWithMode mode="upload" />)
|
||||||
<FileTreeContext {...contextProps}>
|
|
||||||
<OpenWithMode mode="upload" />
|
|
||||||
</FileTreeContext>
|
|
||||||
)
|
|
||||||
|
|
||||||
// the submit button should not be present
|
// the submit button should not be present
|
||||||
expect(screen.queryByRole('button', { name: 'Create' })).to.be.null
|
expect(screen.queryByRole('button', { name: 'Create' })).to.be.null
|
||||||
|
@ -478,7 +436,9 @@ describe('<FileTreeModalCreateFile/>', function () {
|
||||||
await waitFor(() => expect(requests).to.have.length(1))
|
await waitFor(() => expect(requests).to.have.length(1))
|
||||||
|
|
||||||
const [request] = requests
|
const [request] = requests
|
||||||
expect(request.url).to.equal('/project/test-project/upload')
|
expect(request.url).to.equal(
|
||||||
|
'/project/123abc/upload?folder_id=root-folder-id'
|
||||||
|
)
|
||||||
expect(request.method).to.equal('POST')
|
expect(request.method).to.equal('POST')
|
||||||
|
|
||||||
request.respond(
|
request.respond(
|
||||||
|
|
|
@ -19,8 +19,10 @@ describe('<FileTreeDoc/>', function () {
|
||||||
<FileTreeDoc name="foo.tex" id="123abc" isLinkedFile={false} />,
|
<FileTreeDoc name="foo.tex" id="123abc" isLinkedFile={false} />,
|
||||||
{
|
{
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '123abc' }],
|
docs: [{ _id: '123abc' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
@ -48,8 +50,10 @@ describe('<FileTreeDoc/>', function () {
|
||||||
it('selects', function () {
|
it('selects', function () {
|
||||||
renderWithContext(<FileTreeDoc name="foo.tex" id="123abc" expanded />, {
|
renderWithContext(<FileTreeDoc name="foo.tex" id="123abc" expanded />, {
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '123abc' }],
|
docs: [{ _id: '123abc' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
@ -67,8 +71,10 @@ describe('<FileTreeDoc/>', function () {
|
||||||
it('multi-selects', function () {
|
it('multi-selects', function () {
|
||||||
renderWithContext(<FileTreeDoc name="foo.tex" id="123abc" expanded />, {
|
renderWithContext(<FileTreeDoc name="foo.tex" id="123abc" expanded />, {
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '123abc' }],
|
docs: [{ _id: '123abc' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
|
|
@ -42,9 +42,11 @@ describe('<FileTreeFolderList/>', function () {
|
||||||
<FileTreeFolderList folders={[]} docs={docs} files={[]} />,
|
<FileTreeFolderList folders={[]} docs={docs} files={[]} />,
|
||||||
{
|
{
|
||||||
contextProps: {
|
contextProps: {
|
||||||
hasWritePermissions: false,
|
permissionsLevel: 'readOnly',
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '1' }, { _id: '2' }],
|
docs: [{ _id: '1' }, { _id: '2' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
@ -78,8 +80,10 @@ describe('<FileTreeFolderList/>', function () {
|
||||||
<FileTreeFolderList folders={[]} docs={docs} files={[]} />,
|
<FileTreeFolderList folders={[]} docs={docs} files={[]} />,
|
||||||
{
|
{
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '1' }, { _id: '2' }, { _id: '3' }],
|
docs: [{ _id: '1' }, { _id: '2' }, { _id: '3' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
|
|
@ -35,8 +35,10 @@ describe('<FileTreeFolder/>', function () {
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '123abc' }],
|
docs: [{ _id: '123abc' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
@ -64,8 +66,10 @@ describe('<FileTreeFolder/>', function () {
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '123abc' }],
|
docs: [{ _id: '123abc' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
@ -93,8 +97,10 @@ describe('<FileTreeFolder/>', function () {
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '123abc' }],
|
docs: [{ _id: '123abc' }],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
|
|
|
@ -31,12 +31,19 @@ describe('<FileTreeitemInner />', function () {
|
||||||
|
|
||||||
describe('context menu', function () {
|
describe('context menu', function () {
|
||||||
it('does not display without write permissions', function () {
|
it('does not display without write permissions', function () {
|
||||||
renderWithContext(
|
const { container } = renderWithContext(
|
||||||
<FileTreeitemInner id="123abc" name="bar.tex" isSelected />,
|
<>
|
||||||
{ contextProps: { hasWritePermissions: false } }
|
<FileTreeitemInner id="123abc" name="bar.tex" isSelected />
|
||||||
|
<FileTreeContextMenu />
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
contextProps: { permissionsLevel: 'readOnly' },
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(screen.queryByRole('menu', { visible: false })).to.not.exist
|
const entityElement = container.querySelector('div.entity')
|
||||||
|
fireEvent.contextMenu(entityElement)
|
||||||
|
expect(screen.queryByRole('menu')).to.not.exist
|
||||||
})
|
})
|
||||||
|
|
||||||
it('open / close', function () {
|
it('open / close', function () {
|
||||||
|
@ -79,9 +86,10 @@ describe('<FileTreeitemInner />', function () {
|
||||||
{
|
{
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootDocId: '123abc',
|
rootDocId: '123abc',
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '123abc', name: 'bar.tex' }],
|
docs: [{ _id: '123abc', name: 'bar.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
|
|
@ -75,7 +75,7 @@ describe('<FileTreeItemName />', function () {
|
||||||
setIsDraggable={setIsDraggable}
|
setIsDraggable={setIsDraggable}
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
contextProps: { hasWritePermissions: false },
|
contextProps: { permissionsLevel: 'readOnly' },
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ describe('<FileTreeRoot/>', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -37,19 +38,21 @@ describe('<FileTreeRoot/>', function () {
|
||||||
]
|
]
|
||||||
const { container } = renderWithEditorContext(
|
const { container } = renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions={false}
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
rootDocId="456def"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
features: {},
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
screen.queryByRole('tree')
|
screen.queryByRole('tree')
|
||||||
|
@ -66,6 +69,7 @@ describe('<FileTreeRoot/>', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -73,19 +77,21 @@ describe('<FileTreeRoot/>', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
rootDocId="456def"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
features: {},
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// as a proxy to check that the invalid entity ha not been select we start
|
// as a proxy to check that the invalid entity ha not been select we start
|
||||||
|
@ -104,6 +110,7 @@ describe('<FileTreeRoot/>', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -112,19 +119,21 @@ describe('<FileTreeRoot/>', function () {
|
||||||
|
|
||||||
const { container } = renderWithEditorContext(
|
const { container } = renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions={false}
|
|
||||||
rootDocId="456def"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected={false}
|
isConnected={false}
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
features: {},
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(container.querySelector('.disconnected-overlay')).to.exist
|
expect(container.querySelector('.disconnected-overlay')).to.exist
|
||||||
|
@ -134,6 +143,7 @@ describe('<FileTreeRoot/>', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [
|
docs: [
|
||||||
{ _id: '456def', name: 'main.tex' },
|
{ _id: '456def', name: 'main.tex' },
|
||||||
{ _id: '789ghi', name: 'other.tex' },
|
{ _id: '789ghi', name: 'other.tex' },
|
||||||
|
@ -144,11 +154,6 @@ describe('<FileTreeRoot/>', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
rootDocId="456def"
|
|
||||||
hasWritePermissions={false}
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -156,7 +161,14 @@ describe('<FileTreeRoot/>', function () {
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
features: {},
|
||||||
|
permissionsLevel: 'readOnly',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledOnce(onSelect)
|
sinon.assert.calledOnce(onSelect)
|
||||||
sinon.assert.calledWithMatch(onSelect, [
|
sinon.assert.calledWithMatch(onSelect, [
|
||||||
|
@ -187,6 +199,7 @@ describe('<FileTreeRoot/>', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [
|
docs: [
|
||||||
{ _id: '456def', name: 'main.tex' },
|
{ _id: '456def', name: 'main.tex' },
|
||||||
{ _id: '789ghi', name: 'other.tex' },
|
{ _id: '789ghi', name: 'other.tex' },
|
||||||
|
@ -197,11 +210,6 @@ describe('<FileTreeRoot/>', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
rootDocId="456def"
|
|
||||||
hasWritePermissions={false}
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -209,7 +217,14 @@ describe('<FileTreeRoot/>', function () {
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
features: {},
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
screen.getByRole('treeitem', { name: 'main.tex', selected: true })
|
screen.getByRole('treeitem', { name: 'main.tex', selected: true })
|
||||||
|
@ -231,6 +246,7 @@ describe('<FileTreeRoot/>', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [
|
docs: [
|
||||||
{ _id: '456def', name: 'main.tex' },
|
{ _id: '456def', name: 'main.tex' },
|
||||||
{ _id: '789ghi', name: 'other.tex' },
|
{ _id: '789ghi', name: 'other.tex' },
|
||||||
|
@ -241,11 +257,6 @@ describe('<FileTreeRoot/>', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
rootDocId="456def"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -253,7 +264,14 @@ describe('<FileTreeRoot/>', function () {
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
features: {},
|
||||||
|
permissionsLevel: 'owner',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const main = screen.getByRole('treeitem', {
|
const main = screen.getByRole('treeitem', {
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe('<FileTreeToolbar/>', function () {
|
||||||
|
|
||||||
it('read-only', function () {
|
it('read-only', function () {
|
||||||
renderWithContext(<FileTreeToolbar />, {
|
renderWithContext(<FileTreeToolbar />, {
|
||||||
contextProps: { hasWritePermissions: false },
|
contextProps: { permissionsLevel: 'readOnly' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(screen.queryByRole('button')).to.not.exist
|
expect(screen.queryByRole('button')).to.not.exist
|
||||||
|
@ -31,9 +31,10 @@ describe('<FileTreeToolbar/>', function () {
|
||||||
renderWithContext(<FileTreeToolbar />, {
|
renderWithContext(<FileTreeToolbar />, {
|
||||||
contextProps: {
|
contextProps: {
|
||||||
rootDocId: '456def',
|
rootDocId: '456def',
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
|
|
@ -22,6 +22,7 @@ describe('FileTree Context Menu Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -29,19 +30,19 @@ describe('FileTree Context Menu Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
rootDocId="456def"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ describe('FileTree Context Menu Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -63,19 +65,20 @@ describe('FileTree Context Menu Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions={false}
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
rootDocId="456def"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>
|
/>,
|
||||||
|
{
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
permissionsLevel: 'readOnly',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -37,10 +38,6 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -49,7 +46,11 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const newFolderName = 'Foo Bar In Root'
|
const newFolderName = 'Foo Bar In Root'
|
||||||
|
@ -83,6 +84,7 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [],
|
docs: [],
|
||||||
folders: [
|
folders: [
|
||||||
{
|
{
|
||||||
|
@ -98,20 +100,20 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
rootDocId="789ghi"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '789ghi',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const expandButton = screen.getByRole('button', { name: 'Expand' })
|
const expandButton = screen.getByRole('button', { name: 'Expand' })
|
||||||
|
@ -154,6 +156,7 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [],
|
docs: [],
|
||||||
folders: [
|
folders: [
|
||||||
{
|
{
|
||||||
|
@ -169,20 +172,20 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
rootDocId="456def"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const newFolderName = 'Foo Bar In thefolder'
|
const newFolderName = 'Foo Bar In thefolder'
|
||||||
|
@ -222,6 +225,7 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'existingFile' }],
|
docs: [{ _id: '456def', name: 'existingFile' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -229,20 +233,20 @@ describe('FileTree Create Folder Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
setStartedFreeTrial={() => null}
|
setStartedFreeTrial={() => null}
|
||||||
rootDocId="456def"
|
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
rootDocId: '456def',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
let newFolderName = 'existingFile'
|
let newFolderName = 'existingFile'
|
||||||
|
|
|
@ -26,6 +26,7 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
|
@ -33,10 +34,6 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -45,7 +42,11 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const treeitem = screen.getByRole('treeitem', { name: 'main.tex' })
|
const treeitem = screen.getByRole('treeitem', { name: 'main.tex' })
|
||||||
|
@ -136,6 +137,8 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [
|
folders: [
|
||||||
{
|
{
|
||||||
|
@ -151,10 +154,6 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -163,7 +162,11 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const expandButton = screen.queryByRole('button', { name: 'Expand' })
|
const expandButton = screen.queryByRole('button', { name: 'Expand' })
|
||||||
|
@ -201,6 +204,7 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
folders: [],
|
folders: [],
|
||||||
fileRefs: [{ _id: '789ghi', name: 'my.bib' }],
|
fileRefs: [{ _id: '789ghi', name: 'my.bib' }],
|
||||||
|
@ -209,10 +213,6 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
|
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -221,7 +221,11 @@ describe('FileTree Delete Entity Flow', function () {
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// select two files
|
// select two files
|
||||||
|
|
|
@ -30,6 +30,7 @@ describe('FileTree Rename Entity Flow', function () {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
_id: 'root-folder-id',
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [{ _id: '456def', name: 'a.tex' }],
|
docs: [{ _id: '456def', name: 'a.tex' }],
|
||||||
folders: [
|
folders: [
|
||||||
{
|
{
|
||||||
|
@ -48,10 +49,6 @@ describe('FileTree Rename Entity Flow', function () {
|
||||||
]
|
]
|
||||||
renderWithEditorContext(
|
renderWithEditorContext(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
|
||||||
projectId="123abc"
|
|
||||||
hasWritePermissions
|
|
||||||
userHasFeature={() => true}
|
|
||||||
refProviders={{}}
|
refProviders={{}}
|
||||||
reindexReferences={() => null}
|
reindexReferences={() => null}
|
||||||
setRefProviderEnabled={() => null}
|
setRefProviderEnabled={() => null}
|
||||||
|
@ -60,7 +57,11 @@ describe('FileTree Rename Entity Flow', function () {
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
isConnected
|
isConnected
|
||||||
/>,
|
/>,
|
||||||
{ socket: new MockedSocket() }
|
{
|
||||||
|
socket: new MockedSocket(),
|
||||||
|
projectRootFolder: rootFolder,
|
||||||
|
projectId: '123abc',
|
||||||
|
}
|
||||||
)
|
)
|
||||||
onSelect.reset()
|
onSelect.reset()
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { render } from '@testing-library/react'
|
|
||||||
import FileTreeContext from '../../../../../frontend/js/features/file-tree/components/file-tree-context'
|
import FileTreeContext from '../../../../../frontend/js/features/file-tree/components/file-tree-context'
|
||||||
|
import { renderWithEditorContext } from '../../../helpers/render-with-context'
|
||||||
|
|
||||||
export default (children, options = {}) => {
|
export default (children, options = {}) => {
|
||||||
let { contextProps = {}, ...renderOptions } = options
|
let { contextProps = {}, ...renderOptions } = options
|
||||||
contextProps = {
|
contextProps = {
|
||||||
projectId: '123abc',
|
projectId: '123abc',
|
||||||
rootFolder: [
|
projectRootFolder: [
|
||||||
{
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
docs: [],
|
docs: [],
|
||||||
fileRefs: [],
|
fileRefs: [],
|
||||||
folders: [],
|
folders: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hasWritePermissions: true,
|
|
||||||
userHasFeature: () => true,
|
|
||||||
refProviders: {},
|
refProviders: {},
|
||||||
reindexReferences: () => {
|
reindexReferences: () => {
|
||||||
console.log('reindex references')
|
console.log('reindex references')
|
||||||
|
@ -27,8 +27,25 @@ export default (children, options = {}) => {
|
||||||
onSelect: () => {},
|
onSelect: () => {},
|
||||||
...contextProps,
|
...contextProps,
|
||||||
}
|
}
|
||||||
return render(
|
const {
|
||||||
<FileTreeContext {...contextProps}>{children}</FileTreeContext>,
|
refProviders,
|
||||||
|
reindexReferences,
|
||||||
|
setRefProviderEnabled,
|
||||||
|
setStartedFreeTrial,
|
||||||
|
onSelect,
|
||||||
|
...editorContextProps
|
||||||
|
} = contextProps
|
||||||
|
return renderWithEditorContext(
|
||||||
|
<FileTreeContext
|
||||||
|
refProviders={refProviders}
|
||||||
|
reindexReferences={reindexReferences}
|
||||||
|
setRefProviderEnabled={setRefProviderEnabled}
|
||||||
|
setStartedFreeTrial={setStartedFreeTrial}
|
||||||
|
onSelect={onSelect}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</FileTreeContext>,
|
||||||
|
editorContextProps,
|
||||||
renderOptions
|
renderOptions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ export const PROJECT_NAME = 'project-name'
|
||||||
export function EditorProviders({
|
export function EditorProviders({
|
||||||
user = { id: '123abd', email: 'testuser@example.com' },
|
user = { id: '123abd', email: 'testuser@example.com' },
|
||||||
projectId = PROJECT_ID,
|
projectId = PROJECT_ID,
|
||||||
|
rootDocId = '_root_doc_id',
|
||||||
socket = {
|
socket = {
|
||||||
on: sinon.stub(),
|
on: sinon.stub(),
|
||||||
removeListener: sinon.stub(),
|
removeListener: sinon.stub(),
|
||||||
|
@ -30,8 +31,21 @@ export function EditorProviders({
|
||||||
isRestrictedTokenMember = false,
|
isRestrictedTokenMember = false,
|
||||||
clsiServerId = '1234',
|
clsiServerId = '1234',
|
||||||
scope,
|
scope,
|
||||||
|
features = {
|
||||||
|
referencesSearch: true,
|
||||||
|
},
|
||||||
|
permissionsLevel = 'owner',
|
||||||
children,
|
children,
|
||||||
rootFolder,
|
rootFolder,
|
||||||
|
projectRootFolder = [
|
||||||
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
name: 'rootFolder',
|
||||||
|
docs: [],
|
||||||
|
folders: [],
|
||||||
|
fileRefs: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
ui = { view: null, pdfLayout: 'flat', chatOpen: true },
|
ui = { view: null, pdfLayout: 'flat', chatOpen: true },
|
||||||
fileTreeManager = {
|
fileTreeManager = {
|
||||||
findEntityById: () => null,
|
findEntityById: () => null,
|
||||||
|
@ -59,10 +73,9 @@ export function EditorProviders({
|
||||||
_id: '124abd',
|
_id: '124abd',
|
||||||
email: 'owner@example.com',
|
email: 'owner@example.com',
|
||||||
},
|
},
|
||||||
features: {
|
features,
|
||||||
referencesSearch: true,
|
rootDoc_id: rootDocId,
|
||||||
},
|
rootFolder: projectRootFolder,
|
||||||
rootDoc_id: '_root_doc_id',
|
|
||||||
},
|
},
|
||||||
rootFolder: rootFolder || {
|
rootFolder: rootFolder || {
|
||||||
children: [],
|
children: [],
|
||||||
|
@ -74,6 +87,7 @@ export function EditorProviders({
|
||||||
},
|
},
|
||||||
$applyAsync: sinon.stub(),
|
$applyAsync: sinon.stub(),
|
||||||
toggleHistory: sinon.stub(),
|
toggleHistory: sinon.stub(),
|
||||||
|
permissionsLevel,
|
||||||
...scope,
|
...scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,12 +127,19 @@ export function EditorProviders({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderWithEditorContext(component, contextProps) {
|
export function renderWithEditorContext(
|
||||||
|
component,
|
||||||
|
contextProps,
|
||||||
|
renderOptions = {}
|
||||||
|
) {
|
||||||
const EditorProvidersWrapper = ({ children }) => (
|
const EditorProvidersWrapper = ({ children }) => (
|
||||||
<EditorProviders {...contextProps}>{children}</EditorProviders>
|
<EditorProviders {...contextProps}>{children}</EditorProviders>
|
||||||
)
|
)
|
||||||
|
|
||||||
return render(component, { wrapper: EditorProvidersWrapper })
|
return render(component, {
|
||||||
|
wrapper: EditorProvidersWrapper,
|
||||||
|
...renderOptions,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderHookWithEditorContext(hook, contextProps) {
|
export function renderHookWithEditorContext(hook, contextProps) {
|
||||||
|
|
Loading…
Reference in a new issue