mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Replaced application-context
with user-context
(#4246)
* Replaced `application-context` with `user-context` * deleted `user` initialization with `window.user` * fixed tests and storybook GitOrigin-RevId: 0ed4b9070d7c6d370fee2112f310c4bcfea519e7
This commit is contained in:
parent
b7802674d5
commit
9b59c0813c
15 changed files with 104 additions and 89 deletions
|
@ -8,7 +8,7 @@ import InfiniteScroll from './infinite-scroll'
|
||||||
import ChatFallbackError from './chat-fallback-error'
|
import ChatFallbackError from './chat-fallback-error'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { useLayoutContext } from '../../../shared/context/layout-context'
|
import { useLayoutContext } from '../../../shared/context/layout-context'
|
||||||
import { useApplicationContext } from '../../../shared/context/application-context'
|
import { useUserContext } from '../../../shared/context/user-context'
|
||||||
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
import withErrorBoundary from '../../../infrastructure/error-boundary'
|
||||||
import { FetchError } from '../../../infrastructure/fetch-json'
|
import { FetchError } from '../../../infrastructure/fetch-json'
|
||||||
import { useChatContext } from '../context/chat-context'
|
import { useChatContext } from '../context/chat-context'
|
||||||
|
@ -17,8 +17,8 @@ const ChatPane = React.memo(function ChatPane() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { chatIsOpen } = useLayoutContext({ chatIsOpen: PropTypes.bool })
|
const { chatIsOpen } = useLayoutContext({ chatIsOpen: PropTypes.bool })
|
||||||
const { user } = useApplicationContext({
|
const user = useUserContext({
|
||||||
user: PropTypes.shape({ id: PropTypes.string.isRequired }),
|
id: PropTypes.string.isRequired,
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
import { useApplicationContext } from '../../../shared/context/application-context'
|
import { useUserContext } from '../../../shared/context/user-context'
|
||||||
import { useProjectContext } from '../../../shared/context/project-context'
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
import { getJSON, postJSON } from '../../../infrastructure/fetch-json'
|
import { getJSON, postJSON } from '../../../infrastructure/fetch-json'
|
||||||
import { appendMessage, prependMessages } from '../utils/message-list-appender'
|
import { appendMessage, prependMessages } from '../utils/message-list-appender'
|
||||||
|
@ -117,8 +117,8 @@ ChatContext.Provider.propTypes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatProvider({ children }) {
|
export function ChatProvider({ children }) {
|
||||||
const { user } = useApplicationContext({
|
const user = useUserContext({
|
||||||
user: PropTypes.shape({ id: PropTypes.string.isRequired }),
|
id: PropTypes.string.isRequired,
|
||||||
})
|
})
|
||||||
const { _id: projectId } = useProjectContext({
|
const { _id: projectId } = useProjectContext({
|
||||||
_id: PropTypes.string.isRequired,
|
_id: PropTypes.string.isRequired,
|
||||||
|
|
|
@ -4,11 +4,11 @@ import ToolbarHeader from './toolbar-header'
|
||||||
import { useEditorContext } from '../../../shared/context/editor-context'
|
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||||
import { useChatContext } from '../../chat/context/chat-context'
|
import { useChatContext } from '../../chat/context/chat-context'
|
||||||
import { useLayoutContext } from '../../../shared/context/layout-context'
|
import { useLayoutContext } from '../../../shared/context/layout-context'
|
||||||
import { useApplicationContext } from '../../../shared/context/application-context'
|
import { useUserContext } from '../../../shared/context/user-context'
|
||||||
import { useProjectContext } from '../../../shared/context/project-context'
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
|
|
||||||
const applicationContextPropTypes = {
|
const userContextPropTypes = {
|
||||||
user: PropTypes.object,
|
id: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectContextPropTypes = {
|
const projectContextPropTypes = {
|
||||||
|
@ -21,6 +21,7 @@ const editorContextPropTypes = {
|
||||||
isRestrictedTokenMember: PropTypes.bool,
|
isRestrictedTokenMember: PropTypes.bool,
|
||||||
renameProject: PropTypes.func.isRequired,
|
renameProject: PropTypes.func.isRequired,
|
||||||
isProjectOwner: PropTypes.bool,
|
isProjectOwner: PropTypes.bool,
|
||||||
|
permissionsLevel: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const layoutContextPropTypes = {
|
const layoutContextPropTypes = {
|
||||||
|
@ -45,7 +46,7 @@ const EditorNavigationToolbarRoot = React.memo(
|
||||||
openDoc,
|
openDoc,
|
||||||
openShareProjectModal,
|
openShareProjectModal,
|
||||||
}) {
|
}) {
|
||||||
const { user } = useApplicationContext(applicationContextPropTypes)
|
const user = useUserContext(userContextPropTypes)
|
||||||
|
|
||||||
const { name: projectName } = useProjectContext(projectContextPropTypes)
|
const { name: projectName } = useProjectContext(projectContextPropTypes)
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ const EditorNavigationToolbarRoot = React.memo(
|
||||||
isRestrictedTokenMember,
|
isRestrictedTokenMember,
|
||||||
renameProject,
|
renameProject,
|
||||||
isProjectOwner,
|
isProjectOwner,
|
||||||
|
permissionsLevel,
|
||||||
} = useEditorContext(editorContextPropTypes)
|
} = useEditorContext(editorContextPropTypes)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -110,6 +112,12 @@ const EditorNavigationToolbarRoot = React.memo(
|
||||||
[openDoc]
|
[openDoc]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// the existing angular implementation prevents collaborators from updating a
|
||||||
|
// project's name, but the backend allows that with the following logic:
|
||||||
|
// `const hasRenamePermissions = permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite'`
|
||||||
|
// See https://github.com/overleaf/issues/issues/4492
|
||||||
|
const hasRenamePermissions = permissionsLevel === 'owner'
|
||||||
|
|
||||||
// using {display: 'none'} as 1:1 migration from Angular's ng-hide. Using
|
// using {display: 'none'} as 1:1 migration from Angular's ng-hide. Using
|
||||||
// `loading ? null : <ToolbarHeader/>` causes UI glitches
|
// `loading ? null : <ToolbarHeader/>` causes UI glitches
|
||||||
return (
|
return (
|
||||||
|
@ -130,6 +138,7 @@ const EditorNavigationToolbarRoot = React.memo(
|
||||||
isAnonymousUser={user == null}
|
isAnonymousUser={user == null}
|
||||||
projectName={projectName}
|
projectName={projectName}
|
||||||
renameProject={renameProject}
|
renameProject={renameProject}
|
||||||
|
hasRenamePermissions={hasRenamePermissions}
|
||||||
openShareModal={openShareModal}
|
openShareModal={openShareModal}
|
||||||
pdfViewIsOpen={view === 'pdf'}
|
pdfViewIsOpen={view === 'pdf'}
|
||||||
pdfButtonIsVisible={pdfLayout === 'flat'}
|
pdfButtonIsVisible={pdfLayout === 'flat'}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Icon from '../../../shared/components/icon'
|
||||||
|
|
||||||
function ProjectNameEditableLabel({
|
function ProjectNameEditableLabel({
|
||||||
projectName,
|
projectName,
|
||||||
userIsAdmin,
|
hasRenamePermissions,
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
}) {
|
}) {
|
||||||
|
@ -15,7 +15,7 @@ function ProjectNameEditableLabel({
|
||||||
|
|
||||||
const [isRenaming, setIsRenaming] = useState(false)
|
const [isRenaming, setIsRenaming] = useState(false)
|
||||||
|
|
||||||
const canRename = userIsAdmin && !isRenaming
|
const canRename = hasRenamePermissions && !isRenaming
|
||||||
|
|
||||||
const [inputContent, setInputContent] = useState(projectName)
|
const [inputContent, setInputContent] = useState(projectName)
|
||||||
|
|
||||||
|
@ -28,9 +28,11 @@ function ProjectNameEditableLabel({
|
||||||
}, [isRenaming])
|
}, [isRenaming])
|
||||||
|
|
||||||
function startRenaming() {
|
function startRenaming() {
|
||||||
|
if (canRename) {
|
||||||
setInputContent(projectName)
|
setInputContent(projectName)
|
||||||
setIsRenaming(true)
|
setIsRenaming(true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleKeyDown(event) {
|
function handleKeyDown(event) {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
|
@ -84,7 +86,7 @@ function ProjectNameEditableLabel({
|
||||||
|
|
||||||
ProjectNameEditableLabel.propTypes = {
|
ProjectNameEditableLabel.propTypes = {
|
||||||
projectName: PropTypes.string.isRequired,
|
projectName: PropTypes.string.isRequired,
|
||||||
userIsAdmin: PropTypes.bool,
|
hasRenamePermissions: PropTypes.bool,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ const ToolbarHeader = React.memo(function ToolbarHeader({
|
||||||
isAnonymousUser,
|
isAnonymousUser,
|
||||||
projectName,
|
projectName,
|
||||||
renameProject,
|
renameProject,
|
||||||
|
hasRenamePermissions,
|
||||||
openShareModal,
|
openShareModal,
|
||||||
pdfViewIsOpen,
|
pdfViewIsOpen,
|
||||||
pdfButtonIsVisible,
|
pdfButtonIsVisible,
|
||||||
|
@ -56,7 +57,7 @@ const ToolbarHeader = React.memo(function ToolbarHeader({
|
||||||
<ProjectNameEditableLabel
|
<ProjectNameEditableLabel
|
||||||
className="toolbar-center"
|
className="toolbar-center"
|
||||||
projectName={projectName}
|
projectName={projectName}
|
||||||
userIsAdmin
|
hasRenamePermissions={hasRenamePermissions}
|
||||||
onChange={renameProject}
|
onChange={renameProject}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -108,6 +109,7 @@ ToolbarHeader.propTypes = {
|
||||||
isAnonymousUser: PropTypes.bool,
|
isAnonymousUser: PropTypes.bool,
|
||||||
projectName: PropTypes.string.isRequired,
|
projectName: PropTypes.string.isRequired,
|
||||||
renameProject: PropTypes.func.isRequired,
|
renameProject: PropTypes.func.isRequired,
|
||||||
|
hasRenamePermissions: PropTypes.bool,
|
||||||
openShareModal: PropTypes.func.isRequired,
|
openShareModal: PropTypes.func.isRequired,
|
||||||
pdfViewIsOpen: PropTypes.bool,
|
pdfViewIsOpen: PropTypes.bool,
|
||||||
pdfButtonIsVisible: PropTypes.bool,
|
pdfButtonIsVisible: PropTypes.bool,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { useCallback, useEffect } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { useApplicationContext } from '../../../shared/context/application-context'
|
import { useUserContext } from '../../../shared/context/user-context'
|
||||||
import { useFileTreeMutable } from '../contexts/file-tree-mutable'
|
import { useFileTreeMutable } from '../contexts/file-tree-mutable'
|
||||||
import { useFileTreeSelectable } from '../contexts/file-tree-selectable'
|
import { useFileTreeSelectable } from '../contexts/file-tree-selectable'
|
||||||
import { findInTreeOrThrow } from '../util/find-in-tree'
|
import { findInTreeOrThrow } from '../util/find-in-tree'
|
||||||
|
|
||||||
export function useFileTreeSocketListener() {
|
export function useFileTreeSocketListener() {
|
||||||
const { user } = useApplicationContext({
|
const user = useUserContext({
|
||||||
user: PropTypes.shape({ id: PropTypes.string.isRequired }),
|
id: PropTypes.string.isRequired,
|
||||||
})
|
})
|
||||||
const {
|
const {
|
||||||
dispatchRename,
|
dispatchRename,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Trans, useTranslation } from 'react-i18next'
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { useApplicationContext } from '../../../shared/context/application-context'
|
import { useUserContext } from '../../../shared/context/user-context'
|
||||||
|
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { upgradePlan } from '../../../main/account-upgrade'
|
import { upgradePlan } from '../../../main/account-upgrade'
|
||||||
|
@ -11,8 +11,8 @@ import StartFreeTrialButton from '../../../shared/components/start-free-trial-bu
|
||||||
|
|
||||||
export default function AddCollaboratorsUpgrade() {
|
export default function AddCollaboratorsUpgrade() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { user } = useApplicationContext({
|
const user = useUserContext({
|
||||||
user: PropTypes.shape({ allowedFreeTrial: PropTypes.boolean }),
|
allowedFreeTrial: PropTypes.bool,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [startedFreeTrial, setStartedFreeTrial] = useState(false)
|
const [startedFreeTrial, setStartedFreeTrial] = useState(false)
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { createContext, useContext, useMemo } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
export const ApplicationContext = createContext()
|
|
||||||
|
|
||||||
ApplicationContext.Provider.propTypes = {
|
|
||||||
value: PropTypes.shape({
|
|
||||||
user: PropTypes.shape({
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
firstName: PropTypes.string,
|
|
||||||
lastName: PropTypes.string,
|
|
||||||
}),
|
|
||||||
gitBridgePublicBaseUrl: PropTypes.string.isRequired,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ApplicationProvider({ children }) {
|
|
||||||
const value = useMemo(() => {
|
|
||||||
const value = {
|
|
||||||
gitBridgePublicBaseUrl: window.gitBridgePublicBaseUrl,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.user.id) {
|
|
||||||
value.user = window.user
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ApplicationContext.Provider value={value}>
|
|
||||||
{children}
|
|
||||||
</ApplicationContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplicationProvider.propTypes = {
|
|
||||||
children: PropTypes.any,
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useApplicationContext(propTypes) {
|
|
||||||
const data = useContext(ApplicationContext)
|
|
||||||
PropTypes.checkPropTypes(
|
|
||||||
propTypes,
|
|
||||||
data,
|
|
||||||
'data',
|
|
||||||
'ApplicationContext.Provider'
|
|
||||||
)
|
|
||||||
return data
|
|
||||||
}
|
|
|
@ -34,6 +34,7 @@ EditorContext.Provider.propTypes = {
|
||||||
rootFolder: PropTypes.shape({
|
rootFolder: PropTypes.shape({
|
||||||
children: PropTypes.arrayOf(PropTypes.shape({ type: PropTypes.string })),
|
children: PropTypes.arrayOf(PropTypes.shape({ type: PropTypes.string })),
|
||||||
}),
|
}),
|
||||||
|
permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +71,15 @@ export function EditorProvider({ children, settings }) {
|
||||||
const [loading] = useScopeValue('state.loading')
|
const [loading] = useScopeValue('state.loading')
|
||||||
const [projectName, setProjectName] = useScopeValue('project.name')
|
const [projectName, setProjectName] = useScopeValue('project.name')
|
||||||
const [rootFolder] = useScopeValue('rootFolder')
|
const [rootFolder] = useScopeValue('rootFolder')
|
||||||
|
const [permissionsLevel] = useScopeValue('permissionsLevel')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ide?.socket) {
|
||||||
|
ide.socket.on('projectNameUpdated', setProjectName)
|
||||||
|
return () =>
|
||||||
|
ide.socket.removeListener('projectNameUpdated', setProjectName)
|
||||||
|
}
|
||||||
|
}, [ide?.socket, setProjectName])
|
||||||
|
|
||||||
const renameProject = useCallback(
|
const renameProject = useCallback(
|
||||||
newName => {
|
newName => {
|
||||||
|
@ -109,6 +119,7 @@ export function EditorProvider({ children, settings }) {
|
||||||
hasPremiumCompile: features?.compileGroup === 'priority',
|
hasPremiumCompile: features?.compileGroup === 'priority',
|
||||||
loading,
|
loading,
|
||||||
renameProject,
|
renameProject,
|
||||||
|
permissionsLevel,
|
||||||
isProjectOwner: owner?._id === window.user.id,
|
isProjectOwner: owner?._id === window.user.id,
|
||||||
isRestrictedTokenMember: window.isRestrictedTokenMember,
|
isRestrictedTokenMember: window.isRestrictedTokenMember,
|
||||||
rootFolder,
|
rootFolder,
|
||||||
|
@ -118,6 +129,7 @@ export function EditorProvider({ children, settings }) {
|
||||||
features?.compileGroup,
|
features?.compileGroup,
|
||||||
loading,
|
loading,
|
||||||
renameProject,
|
renameProject,
|
||||||
|
permissionsLevel,
|
||||||
owner?._id,
|
owner?._id,
|
||||||
rootFolder,
|
rootFolder,
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import createSharedContext from 'react2angular-shared-context'
|
import createSharedContext from 'react2angular-shared-context'
|
||||||
|
|
||||||
import { ApplicationProvider } from './application-context'
|
import { UserProvider } from './user-context'
|
||||||
import { IdeProvider } from './ide-context'
|
import { IdeProvider } from './ide-context'
|
||||||
import { EditorProvider } from './editor-context'
|
import { EditorProvider } from './editor-context'
|
||||||
import { CompileProvider } from './compile-context'
|
import { CompileProvider } from './compile-context'
|
||||||
|
@ -11,8 +11,8 @@ import { ProjectProvider } from './project-context'
|
||||||
|
|
||||||
export function ContextRoot({ children, ide, settings }) {
|
export function ContextRoot({ children, ide, settings }) {
|
||||||
return (
|
return (
|
||||||
<ApplicationProvider>
|
|
||||||
<IdeProvider ide={ide}>
|
<IdeProvider ide={ide}>
|
||||||
|
<UserProvider>
|
||||||
<ProjectProvider>
|
<ProjectProvider>
|
||||||
<EditorProvider settings={settings}>
|
<EditorProvider settings={settings}>
|
||||||
<CompileProvider>
|
<CompileProvider>
|
||||||
|
@ -22,8 +22,8 @@ export function ContextRoot({ children, ide, settings }) {
|
||||||
</CompileProvider>
|
</CompileProvider>
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</ProjectProvider>
|
</ProjectProvider>
|
||||||
|
</UserProvider>
|
||||||
</IdeProvider>
|
</IdeProvider>
|
||||||
</ApplicationProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
services/web/frontend/js/shared/context/user-context.js
Normal file
32
services/web/frontend/js/shared/context/user-context.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { createContext, useContext } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import useScopeValue from './util/scope-value-hook'
|
||||||
|
|
||||||
|
export const UserContext = createContext()
|
||||||
|
|
||||||
|
UserContext.Provider.propTypes = {
|
||||||
|
value: PropTypes.shape({
|
||||||
|
user: PropTypes.shape({
|
||||||
|
id: PropTypes.string,
|
||||||
|
allowedFreeTrial: PropTypes.boolean,
|
||||||
|
first_name: PropTypes.string,
|
||||||
|
last_name: PropTypes.string,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UserProvider({ children }) {
|
||||||
|
const [user] = useScopeValue('user', true)
|
||||||
|
|
||||||
|
return <UserContext.Provider value={user}>{children}</UserContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProvider.propTypes = {
|
||||||
|
children: PropTypes.any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUserContext(propTypes) {
|
||||||
|
const data = useContext(UserContext)
|
||||||
|
PropTypes.checkPropTypes(propTypes, data, 'data', 'UserContext.Provider')
|
||||||
|
return data
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ export function setupContext() {
|
||||||
if (window._ide) {
|
if (window._ide) {
|
||||||
$scope = {
|
$scope = {
|
||||||
...window._ide.$scope,
|
...window._ide.$scope,
|
||||||
|
user: window.user,
|
||||||
project: {},
|
project: {},
|
||||||
$watch: () => {},
|
$watch: () => {},
|
||||||
ui: {
|
ui: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import PreviewLogsPane from '../js/features/preview/components/preview-logs-pane'
|
import PreviewLogsPane from '../js/features/preview/components/preview-logs-pane'
|
||||||
import { EditorProvider } from '../js/shared/context/editor-context'
|
import { EditorProvider } from '../js/shared/context/editor-context'
|
||||||
import { ApplicationProvider } from '../js/shared/context/application-context'
|
import { UserProvider } from '../js/shared/context/user-context'
|
||||||
import useFetchMock from './hooks/use-fetch-mock'
|
import useFetchMock from './hooks/use-fetch-mock'
|
||||||
import { IdeProvider } from '../js/shared/context/ide-context'
|
import { IdeProvider } from '../js/shared/context/ide-context'
|
||||||
|
|
||||||
|
@ -24,13 +24,13 @@ export const TimedOutError = args => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApplicationProvider>
|
<UserProvider>
|
||||||
<IdeProvider ide={ide}>
|
<IdeProvider ide={ide}>
|
||||||
<EditorProvider settings={{}}>
|
<EditorProvider settings={{}}>
|
||||||
<PreviewLogsPane {...args} />
|
<PreviewLogsPane {...args} />
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</IdeProvider>
|
</IdeProvider>
|
||||||
</ApplicationProvider>
|
</UserProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TimedOutError.args = {
|
TimedOutError.args = {
|
||||||
|
@ -59,13 +59,13 @@ export const TimedOutErrorWithPriorityCompile = args => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApplicationProvider>
|
<UserProvider>
|
||||||
<IdeProvider ide={ide}>
|
<IdeProvider ide={ide}>
|
||||||
<EditorProvider settings={{}}>
|
<EditorProvider settings={{}}>
|
||||||
<PreviewLogsPane {...args} />
|
<PreviewLogsPane {...args} />
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</IdeProvider>
|
</IdeProvider>
|
||||||
</ApplicationProvider>
|
</UserProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TimedOutErrorWithPriorityCompile.args = {
|
TimedOutErrorWithPriorityCompile.args = {
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe('<ProjectNameEditableLabel />', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when the name is editable', function () {
|
describe('when the name is editable', function () {
|
||||||
const editableProps = { ...defaultProps, userIsAdmin: true }
|
const editableProps = { ...defaultProps, hasRenamePermissions: true }
|
||||||
|
|
||||||
it('displays an editable input when the edit button is clicked', function () {
|
it('displays an editable input when the edit button is clicked', function () {
|
||||||
render(<ProjectNameEditableLabel {...editableProps} />)
|
render(<ProjectNameEditableLabel {...editableProps} />)
|
||||||
|
@ -52,11 +52,17 @@ describe('<ProjectNameEditableLabel />', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when the name is not editable', function () {
|
describe('when the name is not editable', function () {
|
||||||
const nonEditableProps = { userIsAdmin: false, ...defaultProps }
|
const nonEditableProps = { hasRenamePermissions: false, ...defaultProps }
|
||||||
|
|
||||||
it('the edit button is not displayed', function () {
|
it('the edit button is not displayed', function () {
|
||||||
render(<ProjectNameEditableLabel {...nonEditableProps} />)
|
render(<ProjectNameEditableLabel {...nonEditableProps} />)
|
||||||
expect(screen.queryByRole('button')).to.not.exist
|
expect(screen.queryByRole('button')).to.not.exist
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('does not display an editable input when the project name is double clicked', function () {
|
||||||
|
render(<ProjectNameEditableLabel {...nonEditableProps} />)
|
||||||
|
fireEvent.doubleClick(screen.getByText('test-project'))
|
||||||
|
expect(screen.queryByRole('textbox')).to.not.exist
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { ApplicationProvider } from '../../../frontend/js/shared/context/application-context'
|
import { UserProvider } from '../../../frontend/js/shared/context/user-context'
|
||||||
import { EditorProvider } from '../../../frontend/js/shared/context/editor-context'
|
import { EditorProvider } from '../../../frontend/js/shared/context/editor-context'
|
||||||
import { LayoutProvider } from '../../../frontend/js/shared/context/layout-context'
|
import { LayoutProvider } from '../../../frontend/js/shared/context/layout-context'
|
||||||
import { ChatProvider } from '../../../frontend/js/features/chat/context/chat-context'
|
import { ChatProvider } from '../../../frontend/js/features/chat/context/chat-context'
|
||||||
|
@ -28,6 +28,7 @@ export function EditorProviders({
|
||||||
window.isRestrictedTokenMember = isRestrictedTokenMember
|
window.isRestrictedTokenMember = isRestrictedTokenMember
|
||||||
|
|
||||||
const $scope = {
|
const $scope = {
|
||||||
|
user: window.user,
|
||||||
project: {
|
project: {
|
||||||
_id: window.project_id,
|
_id: window.project_id,
|
||||||
name: 'project-name',
|
name: 'project-name',
|
||||||
|
@ -52,15 +53,15 @@ export function EditorProviders({
|
||||||
window._ide = { $scope, socket }
|
window._ide = { $scope, socket }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApplicationProvider>
|
|
||||||
<IdeProvider ide={window._ide}>
|
<IdeProvider ide={window._ide}>
|
||||||
|
<UserProvider>
|
||||||
<ProjectProvider>
|
<ProjectProvider>
|
||||||
<EditorProvider settings={{}}>
|
<EditorProvider settings={{}}>
|
||||||
<LayoutProvider>{children}</LayoutProvider>
|
<LayoutProvider>{children}</LayoutProvider>
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</ProjectProvider>
|
</ProjectProvider>
|
||||||
|
</UserProvider>
|
||||||
</IdeProvider>
|
</IdeProvider>
|
||||||
</ApplicationProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue