mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #15684 from overleaf/td-ide-page-main-doc-left-menu
Add docs to FileTreeDataContext to replace 'docs' scope value in React code GitOrigin-RevId: 430f795eb0cd17f0f4fab9c61e46fb04ff3030b3
This commit is contained in:
parent
1c820de200
commit
6f34a84ebd
14 changed files with 90 additions and 60 deletions
|
@ -2,16 +2,16 @@ import { useMemo } from 'react'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import isValidTeXFile from '../../../../main/is-valid-tex-file'
|
||||
import { useEditorContext } from '../../../../shared/context/editor-context'
|
||||
import useScopeValue from '../../../../shared/hooks/use-scope-value'
|
||||
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||
import SettingsMenuSelect from './settings-menu-select'
|
||||
import type { Option } from './settings-menu-select'
|
||||
import type { MainDocument } from '../../../../../../types/project-settings'
|
||||
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
|
||||
import { MainDocument } from '../../../../../../types/project-settings'
|
||||
|
||||
export default function SettingsDocument() {
|
||||
const { t } = useTranslation()
|
||||
const { permissionsLevel } = useEditorContext()
|
||||
const [docs] = useScopeValue<MainDocument[] | undefined>('docs')
|
||||
const docs: MainDocument[] = useFileTreeData().docs
|
||||
const { rootDocId, setRootDocId } = useProjectSettingsContext()
|
||||
|
||||
const validDocsOptions = useMemo(() => {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { Folder } from '../../../../../types/folder'
|
||||
import { DocId, MainDocument } from '../../../../../types/project-settings'
|
||||
|
||||
function findAllDocsInFolder(folder: Folder, path = '') {
|
||||
const docs = folder.docs.map<MainDocument>(doc => ({
|
||||
doc: { id: doc._id as DocId, name: doc.name },
|
||||
path: path + doc.name,
|
||||
}))
|
||||
for (const subFolder of folder.folders) {
|
||||
docs.push(...findAllDocsInFolder(subFolder, `${path}${subFolder.name}/`))
|
||||
}
|
||||
return docs
|
||||
}
|
||||
|
||||
export function docsInFolder(folder: Folder) {
|
||||
const docsInTree = findAllDocsInFolder(folder)
|
||||
docsInTree.sort(function (a, b) {
|
||||
const aDepth = (a.path.match(/\//g) || []).length
|
||||
const bDepth = (b.path.match(/\//g) || []).length
|
||||
if (aDepth - bDepth !== 0) {
|
||||
return -(aDepth - bDepth) // Deeper path == folder first
|
||||
} else if (a.path < b.path) {
|
||||
return -1
|
||||
} else if (a.path > b.path) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
return docsInTree
|
||||
}
|
|
@ -56,10 +56,6 @@ function populatePdfScope(store: ReactScopeValueStore) {
|
|||
store.allowNonExistentPath('pdf', true)
|
||||
}
|
||||
|
||||
function populateFileTreeScope(store: ReactScopeValueStore) {
|
||||
store.set('docs', [])
|
||||
}
|
||||
|
||||
function createReactScopeValueStore(projectId: string) {
|
||||
const scopeStore = new ReactScopeValueStore()
|
||||
|
||||
|
@ -76,7 +72,6 @@ function createReactScopeValueStore(projectId: string) {
|
|||
populateSettingsScope(scopeStore)
|
||||
populateOnlineUsersScope(scopeStore)
|
||||
populateReferenceScope(scopeStore)
|
||||
populateFileTreeScope(scopeStore)
|
||||
populateReviewPanelScope(scopeStore)
|
||||
|
||||
scopeStore.allowNonExistentPath('hasLintingError')
|
||||
|
|
|
@ -33,7 +33,6 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
|
|||
'reviewPanel.commentThreads',
|
||||
true
|
||||
)
|
||||
const [docs] = useScopeValue<ReviewPanel.Value<'docs'>>('docs')
|
||||
const [entries] = useScopeValue<ReviewPanel.Value<'entries'>>(
|
||||
'reviewPanel.entries',
|
||||
true
|
||||
|
@ -156,7 +155,6 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
|
|||
() => ({
|
||||
collapsed,
|
||||
commentThreads,
|
||||
docs,
|
||||
entries,
|
||||
entryHover,
|
||||
isAddingComment,
|
||||
|
@ -183,7 +181,6 @@ function useReviewPanelState(): ReviewPanelStateReactIde {
|
|||
[
|
||||
collapsed,
|
||||
commentThreads,
|
||||
docs,
|
||||
entries,
|
||||
entryHover,
|
||||
isAddingComment,
|
||||
|
|
|
@ -6,7 +6,6 @@ export default function populateReviewPanelScope(store: ReactScopeValueStore) {
|
|||
store.set('reviewPanel.overview.loading', false)
|
||||
store.set('reviewPanel.nVisibleSelectedChanges', 0)
|
||||
store.set('reviewPanel.commentThreads', {})
|
||||
store.set('docs', undefined)
|
||||
store.set('reviewPanel.entries', {})
|
||||
store.set('loadingThreads', true)
|
||||
store.set('permissions', {
|
||||
|
|
|
@ -5,9 +5,12 @@ import Nav from './nav'
|
|||
import Icon from '../../../../shared/components/icon'
|
||||
import OverviewFile from './overview-file'
|
||||
import { useReviewPanelValueContext } from '../../context/review-panel/review-panel-context'
|
||||
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
|
||||
import { MainDocument } from '../../../../../../types/project-settings'
|
||||
|
||||
function OverviewContainer() {
|
||||
const { loading, docs } = useReviewPanelValueContext()
|
||||
const { loading } = useReviewPanelValueContext()
|
||||
const docs: MainDocument[] = useFileTreeData().docs
|
||||
|
||||
return (
|
||||
<Container>
|
||||
|
|
|
@ -13,8 +13,12 @@ import {
|
|||
ThreadId,
|
||||
} from '../../../../../../../types/review-panel/review-panel'
|
||||
import { ReviewPanelResolvedCommentThread } from '../../../../../../../types/review-panel/comment-thread'
|
||||
import { DocId } from '../../../../../../../types/project-settings'
|
||||
import {
|
||||
DocId,
|
||||
MainDocument,
|
||||
} from '../../../../../../../types/project-settings'
|
||||
import { ReviewPanelEntry } from '../../../../../../../types/review-panel/entry'
|
||||
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
|
||||
|
||||
export interface FilteredResolvedComments
|
||||
extends ReviewPanelResolvedCommentThread {
|
||||
|
@ -29,8 +33,9 @@ function ResolvedCommentsDropdown() {
|
|||
const { t } = useTranslation()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { docs, commentThreads, resolvedComments, permissions } =
|
||||
const { commentThreads, resolvedComments, permissions } =
|
||||
useReviewPanelValueContext()
|
||||
const docs: MainDocument[] = useFileTreeData().docs
|
||||
|
||||
const { refreshResolvedCommentsDropdown } = useReviewPanelUpdaterFnsContext()
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
'reviewPanel.commentThreads',
|
||||
true
|
||||
)
|
||||
const [docs] = useScopeValue<ReviewPanel.Value<'docs'>>('docs')
|
||||
const [entries] = useScopeValue<ReviewPanel.Value<'entries'>>(
|
||||
'reviewPanel.entries',
|
||||
true
|
||||
|
@ -143,7 +142,6 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
() => ({
|
||||
collapsed,
|
||||
commentThreads,
|
||||
docs,
|
||||
entries,
|
||||
entryHover,
|
||||
isAddingComment,
|
||||
|
@ -170,7 +168,6 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
[
|
||||
collapsed,
|
||||
commentThreads,
|
||||
docs,
|
||||
entries,
|
||||
entryHover,
|
||||
isAddingComment,
|
||||
|
|
|
@ -7,10 +7,7 @@ import {
|
|||
SubView,
|
||||
ThreadId,
|
||||
} from '../../../../../../../types/review-panel/review-panel'
|
||||
import {
|
||||
DocId,
|
||||
MainDocument,
|
||||
} from '../../../../../../../types/project-settings'
|
||||
import { DocId } from '../../../../../../../types/project-settings'
|
||||
import { dispatchReviewPanelLayout } from '../../../extensions/changes/change-manager'
|
||||
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
@ -18,7 +15,6 @@ export interface ReviewPanelState {
|
|||
values: {
|
||||
collapsed: Record<DocId, boolean>
|
||||
commentThreads: ReviewPanelCommentThreads
|
||||
docs: MainDocument[] | undefined
|
||||
entries: ReviewPanelEntries
|
||||
entryHover: boolean
|
||||
isAddingComment: boolean
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from '../../features/file-tree/util/mutate-in-tree'
|
||||
import { countFiles } from '../../features/file-tree/util/count-in-tree'
|
||||
import useDeepCompareEffect from '../../shared/hooks/use-deep-compare-effect'
|
||||
import { docsInFolder } from '@/features/file-tree/util/docs-in-folder'
|
||||
|
||||
const FileTreeDataContext = createContext()
|
||||
|
||||
|
@ -147,6 +148,11 @@ export function FileTreeDataProvider({ children }) {
|
|||
|
||||
const [selectedEntities, setSelectedEntities] = useState([])
|
||||
|
||||
const docs = useMemo(
|
||||
() => (fileTreeData ? docsInFolder(fileTreeData) : undefined),
|
||||
[fileTreeData]
|
||||
)
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
dispatch({
|
||||
type: ACTION_TYPES.RESET,
|
||||
|
@ -210,6 +216,7 @@ export function FileTreeDataProvider({ children }) {
|
|||
hasFolders: fileTreeData?.folders.length > 0,
|
||||
selectedEntities,
|
||||
setSelectedEntities,
|
||||
docs,
|
||||
}
|
||||
}, [
|
||||
dispatchCreateDoc,
|
||||
|
@ -222,6 +229,7 @@ export function FileTreeDataProvider({ children }) {
|
|||
fileTreeData,
|
||||
selectedEntities,
|
||||
setSelectedEntities,
|
||||
docs,
|
||||
])
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import EditorLeftMenu from '../../../../frontend/js/features/editor-left-menu/components/editor-left-menu'
|
||||
import {
|
||||
AllowedImageName,
|
||||
MainDocument,
|
||||
OverallThemeMeta,
|
||||
SpellCheckLanguage,
|
||||
} from '../../../../types/project-settings'
|
||||
import { EditorProviders } from '../../helpers/editor-providers'
|
||||
import { mockScope } from './scope'
|
||||
import { Folder } from '../../../../types/folder'
|
||||
import { docsInFolder } from '@/features/file-tree/util/docs-in-folder'
|
||||
|
||||
describe('<EditorLeftMenu />', function () {
|
||||
beforeEach(function () {
|
||||
|
@ -390,40 +391,37 @@ describe('<EditorLeftMenu />', function () {
|
|||
})
|
||||
|
||||
it('shows document menu correctly', function () {
|
||||
const docs: MainDocument[] = [
|
||||
const rootFolder: Folder = {
|
||||
_id: 'root-folder-id',
|
||||
name: 'rootFolder',
|
||||
docs: [
|
||||
{
|
||||
path: 'main.tex',
|
||||
doc: {
|
||||
_id: 'id1',
|
||||
name: 'main.tex',
|
||||
id: 'id1',
|
||||
type: 'doc',
|
||||
selected: false,
|
||||
} as MainDocument['doc'],
|
||||
},
|
||||
{
|
||||
path: 'folder/main2.tex',
|
||||
doc: {
|
||||
_id: 'id2',
|
||||
name: 'main2.tex',
|
||||
id: 'id2',
|
||||
type: 'doc',
|
||||
selected: false,
|
||||
} as MainDocument['doc'],
|
||||
},
|
||||
]
|
||||
],
|
||||
fileRefs: [],
|
||||
folders: [],
|
||||
}
|
||||
|
||||
const scope = mockScope({
|
||||
ui: {
|
||||
leftMenuShown: true,
|
||||
},
|
||||
docs,
|
||||
})
|
||||
|
||||
cy.mount(
|
||||
<EditorProviders scope={scope}>
|
||||
<EditorProviders scope={scope} rootFolder={[rootFolder as any]}>
|
||||
<EditorLeftMenu />
|
||||
</EditorProviders>
|
||||
)
|
||||
|
||||
const docs = docsInFolder(rootFolder)
|
||||
|
||||
cy.get<HTMLOptionElement>('#settings-menu-rootDocId option').then(
|
||||
options => {
|
||||
const values = [...options].map(o => o.value)
|
||||
|
|
|
@ -5,21 +5,23 @@ import fetchMock from 'fetch-mock'
|
|||
import SettingsDocument from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-document'
|
||||
import * as isValidTeXFileModule from '../../../../../../frontend/js/main/is-valid-tex-file'
|
||||
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||
import type { MainDocument } from '../../../../../../types/project-settings'
|
||||
import { Folder } from '../../../../../../types/folder'
|
||||
|
||||
describe('<SettingsDocument />', function () {
|
||||
let isValidTeXFileStub: sinon.SinonStub
|
||||
const docs: MainDocument[] = [
|
||||
|
||||
const rootFolder: Folder = {
|
||||
_id: 'root-folder-id',
|
||||
name: 'rootFolder',
|
||||
docs: [
|
||||
{
|
||||
path: 'main.tex',
|
||||
doc: {
|
||||
_id: '123abc',
|
||||
name: 'main.tex',
|
||||
id: '123abc',
|
||||
type: 'doc',
|
||||
selected: false,
|
||||
} as MainDocument['doc'],
|
||||
},
|
||||
]
|
||||
],
|
||||
fileRefs: [],
|
||||
folders: [],
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
isValidTeXFileStub = sinon
|
||||
|
@ -34,9 +36,7 @@ describe('<SettingsDocument />', function () {
|
|||
|
||||
it('shows correct menu', async function () {
|
||||
renderWithEditorContext(<SettingsDocument />, {
|
||||
scope: {
|
||||
docs,
|
||||
},
|
||||
rootFolder: [rootFolder],
|
||||
})
|
||||
|
||||
const select = screen.getByLabelText('Main document')
|
||||
|
|
|
@ -22,6 +22,9 @@ describe('<ReviewPanel />', function () {
|
|||
const scope = mockScope('')
|
||||
scope.editor.showVisual = true
|
||||
|
||||
// The tests expect no documents, so remove them from the scope
|
||||
scope.project.rootFolder = []
|
||||
|
||||
cy.wrap(scope).as('scope')
|
||||
|
||||
cy.mount(
|
||||
|
|
|
@ -11,8 +11,6 @@ export type MainDocument = {
|
|||
doc: {
|
||||
name: string
|
||||
id: DocId
|
||||
type: string
|
||||
selected: boolean
|
||||
}
|
||||
path: string
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue