mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3550 from overleaf/ta-disconected-overlay
Add Disconnected Overlay Over File Tree GitOrigin-RevId: bdcd4f58effe841eb223abbb852e0b0f574efefd
This commit is contained in:
parent
e1ffeef06a
commit
dc48ba1d61
10 changed files with 83 additions and 4 deletions
|
@ -18,6 +18,7 @@ aside.editor-sidebar.full-size(
|
||||||
has-write-permissions="hasWritePermissions"
|
has-write-permissions="hasWritePermissions"
|
||||||
on-select="onSelect"
|
on-select="onSelect"
|
||||||
on-init="onInit"
|
on-init="onInit"
|
||||||
|
is-connected="isConnected"
|
||||||
)
|
)
|
||||||
|
|
||||||
div(ng-controller="FileTreeController")
|
div(ng-controller="FileTreeController")
|
||||||
|
|
|
@ -24,7 +24,8 @@ function FileTreeRoot({
|
||||||
rootDocId,
|
rootDocId,
|
||||||
hasWritePermissions,
|
hasWritePermissions,
|
||||||
onSelect,
|
onSelect,
|
||||||
onInit
|
onInit,
|
||||||
|
isConnected
|
||||||
}) {
|
}) {
|
||||||
const isReady = projectId && rootFolder
|
const isReady = projectId && rootFolder
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ function FileTreeRoot({
|
||||||
rootDocId={rootDocId}
|
rootDocId={rootDocId}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
>
|
>
|
||||||
|
{isConnected ? null : <div className="disconnected-overlay" />}
|
||||||
<FileTreeToolbar />
|
<FileTreeToolbar />
|
||||||
<FileTreeContextMenu />
|
<FileTreeContextMenu />
|
||||||
<div className="file-tree-inner">
|
<div className="file-tree-inner">
|
||||||
|
@ -83,7 +85,8 @@ FileTreeRoot.propTypes = {
|
||||||
rootDocId: PropTypes.string,
|
rootDocId: PropTypes.string,
|
||||||
hasWritePermissions: PropTypes.bool.isRequired,
|
hasWritePermissions: PropTypes.bool.isRequired,
|
||||||
onSelect: PropTypes.func.isRequired,
|
onSelect: PropTypes.func.isRequired,
|
||||||
onInit: PropTypes.func.isRequired
|
onInit: PropTypes.func.isRequired,
|
||||||
|
isConnected: PropTypes.bool.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withErrorBoundary(FileTreeRoot, FileTreeError)
|
export default withErrorBoundary(FileTreeRoot, FileTreeError)
|
||||||
|
|
|
@ -13,6 +13,7 @@ App.controller('ReactFileTreeController', function(
|
||||||
$scope.rootFolder = null
|
$scope.rootFolder = null
|
||||||
$scope.rootDocId = null
|
$scope.rootDocId = null
|
||||||
$scope.hasWritePermissions = false
|
$scope.hasWritePermissions = false
|
||||||
|
$scope.isConnected = true
|
||||||
|
|
||||||
$scope.$on('project:joined', () => {
|
$scope.$on('project:joined', () => {
|
||||||
$scope.rootFolder = $scope.project.rootFolder
|
$scope.rootFolder = $scope.project.rootFolder
|
||||||
|
@ -30,6 +31,23 @@ App.controller('ReactFileTreeController', function(
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Set isConnected to true if:
|
||||||
|
// - connection state is 'ready', OR
|
||||||
|
// - connection state is 'waitingCountdown' and reconnection_countdown is null
|
||||||
|
// The added complexity is needed because in Firefox on page reload the
|
||||||
|
// connection state goes into 'waitingCountdown' before being hidden and we
|
||||||
|
// don't want to show a disconnect UI.
|
||||||
|
function updateIsConnected() {
|
||||||
|
const isReady = $scope.connection.state === 'ready'
|
||||||
|
const willStartCountdown =
|
||||||
|
$scope.connection.state === 'waitingCountdown' &&
|
||||||
|
$scope.connection.reconnection_countdown === null
|
||||||
|
$scope.isConnected = isReady || willStartCountdown
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.$watch('connection.state', updateIsConnected)
|
||||||
|
$scope.$watch('connection.reconnection_countdown', updateIsConnected)
|
||||||
|
|
||||||
$scope.onInit = () => {
|
$scope.onInit = () => {
|
||||||
// HACK: resize the vertical pane on init after a 0ms timeout. We do not
|
// HACK: resize the vertical pane on init after a 0ms timeout. We do not
|
||||||
// understand why this is necessary but without this the resized handle is
|
// understand why this is necessary but without this the resized handle is
|
||||||
|
|
|
@ -86,6 +86,9 @@ FullTree.parameters = { setupMocks: defaultSetupMocks }
|
||||||
export const ReadOnly = args => <FileTreeRoot {...args} />
|
export const ReadOnly = args => <FileTreeRoot {...args} />
|
||||||
ReadOnly.args = { hasWritePermissions: false }
|
ReadOnly.args = { hasWritePermissions: false }
|
||||||
|
|
||||||
|
export const Disconnected = args => <FileTreeRoot {...args} />
|
||||||
|
Disconnected.args = { isConnected: false }
|
||||||
|
|
||||||
export const NetworkErrors = args => <FileTreeRoot {...args} />
|
export const NetworkErrors = args => <FileTreeRoot {...args} />
|
||||||
NetworkErrors.parameters = {
|
NetworkErrors.parameters = {
|
||||||
setupMocks: () => {
|
setupMocks: () => {
|
||||||
|
@ -116,7 +119,8 @@ export default {
|
||||||
rootFolder: rootFolderBase,
|
rootFolder: rootFolderBase,
|
||||||
hasWritePermissions: true,
|
hasWritePermissions: true,
|
||||||
projectId: '123abc',
|
projectId: '123abc',
|
||||||
rootDocId: '5e74f1a7ce17ae0041dfd056'
|
rootDocId: '5e74f1a7ce17ae0041dfd056',
|
||||||
|
isConnected: true
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
onInit: { action: 'onInit' },
|
onInit: { action: 'onInit' },
|
||||||
|
|
|
@ -347,6 +347,18 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disconnected-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
background: @file-tree-bg;
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-new-file {
|
.modal-new-file {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { screen, render, fireEvent, waitFor } from '@testing-library/react'
|
import { screen, render, fireEvent, waitFor } from '@testing-library/react'
|
||||||
|
@ -30,7 +31,7 @@ describe('<FileTreeRoot/>', function() {
|
||||||
fileRefs: []
|
fileRefs: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
render(
|
const { container } = render(
|
||||||
<FileTreeRoot
|
<FileTreeRoot
|
||||||
rootFolder={rootFolder}
|
rootFolder={rootFolder}
|
||||||
projectId="123abc"
|
projectId="123abc"
|
||||||
|
@ -38,12 +39,14 @@ describe('<FileTreeRoot/>', function() {
|
||||||
rootDocId="456def"
|
rootDocId="456def"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
screen.queryByRole('tree')
|
screen.queryByRole('tree')
|
||||||
screen.getByRole('treeitem')
|
screen.getByRole('treeitem')
|
||||||
screen.getByRole('treeitem', { name: 'main.tex', selected: true })
|
screen.getByRole('treeitem', { name: 'main.tex', selected: true })
|
||||||
|
expect(container.querySelector('.disconnected-overlay')).to.not.exist
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders with invalid selected doc in local storage', async function() {
|
it('renders with invalid selected doc in local storage', async function() {
|
||||||
|
@ -67,6 +70,7 @@ describe('<FileTreeRoot/>', function() {
|
||||||
rootDocId="456def"
|
rootDocId="456def"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,6 +84,31 @@ describe('<FileTreeRoot/>', function() {
|
||||||
await waitFor(() => screen.getByRole('button', { name: 'Cancel' }))
|
await waitFor(() => screen.getByRole('button', { name: 'Cancel' }))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('renders disconnected overlay', function() {
|
||||||
|
const rootFolder = [
|
||||||
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
|
folders: [],
|
||||||
|
fileRefs: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
<FileTreeRoot
|
||||||
|
rootFolder={rootFolder}
|
||||||
|
projectId="123abc"
|
||||||
|
hasWritePermissions={false}
|
||||||
|
rootDocId="456def"
|
||||||
|
onSelect={onSelect}
|
||||||
|
onInit={onInit}
|
||||||
|
isConnected={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(container.querySelector('.disconnected-overlay')).to.exist
|
||||||
|
})
|
||||||
|
|
||||||
it('fire onSelect', function() {
|
it('fire onSelect', function() {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
|
@ -100,6 +129,7 @@ describe('<FileTreeRoot/>', function() {
|
||||||
hasWritePermissions={false}
|
hasWritePermissions={false}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
sinon.assert.calledOnce(onSelect)
|
sinon.assert.calledOnce(onSelect)
|
||||||
|
@ -147,6 +177,7 @@ describe('<FileTreeRoot/>', function() {
|
||||||
hasWritePermissions={false}
|
hasWritePermissions={false}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ describe('FileTree Context Menu Flow', function() {
|
||||||
rootDocId="456def"
|
rootDocId="456def"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
||||||
|
@ -54,6 +55,7 @@ describe('FileTree Context Menu Flow', function() {
|
||||||
rootDocId="456def"
|
rootDocId="456def"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
const treeitem = screen.getByRole('button', { name: 'main.tex' })
|
||||||
|
|
|
@ -42,6 +42,7 @@ describe('FileTree Create Folder Flow', function() {
|
||||||
hasWritePermissions
|
hasWritePermissions
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,6 +98,7 @@ describe('FileTree Create Folder Flow', function() {
|
||||||
rootDocId="789ghi"
|
rootDocId="789ghi"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -161,6 +163,7 @@ describe('FileTree Create Folder Flow', function() {
|
||||||
rootDocId="456def"
|
rootDocId="456def"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -214,6 +217,7 @@ describe('FileTree Create Folder Flow', function() {
|
||||||
rootDocId="456def"
|
rootDocId="456def"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ describe('FileTree Delete Entity Flow', function() {
|
||||||
hasWritePermissions
|
hasWritePermissions
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -139,6 +140,7 @@ describe('FileTree Delete Entity Flow', function() {
|
||||||
hasWritePermissions
|
hasWritePermissions
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -187,6 +189,7 @@ describe('FileTree Delete Entity Flow', function() {
|
||||||
hasWritePermissions
|
hasWritePermissions
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ describe('FileTree Rename Entity Flow', function() {
|
||||||
hasWritePermissions
|
hasWritePermissions
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
|
isConnected
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue