-
+ return (
+
- ,
- scope
+
+
)
}
diff --git a/services/web/frontend/stories/settings/linking.stories.js b/services/web/frontend/stories/settings/linking.stories.js
index 8de85654d9..353c6867e8 100644
--- a/services/web/frontend/stories/settings/linking.stories.js
+++ b/services/web/frontend/stories/settings/linking.stories.js
@@ -3,6 +3,9 @@ import LinkingSection from '../../js/features/settings/components/linking-sectio
import { setDefaultMeta, defaultSetupMocks } from './helpers/linking'
import { UserProvider } from '../../js/shared/context/user-context'
import { SSOProvider } from '../../js/features/settings/context/sso-context'
+import { ScopeDecorator } from '../decorators/scope'
+import { useEffect } from 'react'
+import { useMeta } from '../hooks/use-meta'
const MOCK_DELAY = 1000
@@ -21,17 +24,23 @@ export const Section = args => {
export const SectionAllUnlinked = args => {
useFetchMock(defaultSetupMocks)
- setDefaultMeta()
- window.metaAttributesCache.set('ol-thirdPartyIds', {})
- window.metaAttributesCache.set('ol-user', {
- features: { github: true, dropbox: true, mendeley: true, zotero: true },
- refProviders: {
- mendeley: false,
- zotero: false,
+
+ useMeta({
+ 'ol-thirdPartyIds': {},
+ 'ol-user': {
+ features: { github: true, dropbox: true, mendeley: true, zotero: true },
+ refProviders: {
+ mendeley: false,
+ zotero: false,
+ },
},
+ 'ol-github': { enabled: false },
+ 'ol-dropbox': { registered: false },
})
- window.metaAttributesCache.set('ol-github', { enabled: false })
- window.metaAttributesCache.set('ol-dropbox', { registered: false })
+
+ useEffect(() => {
+ setDefaultMeta()
+ }, [])
return (
@@ -84,4 +93,5 @@ export const SectionProjetSyncSuccess = args => {
export default {
title: 'Account Settings / Linking',
component: LinkingSection,
+ decorators: [ScopeDecorator],
}
diff --git a/services/web/frontend/stories/share-project-modal.stories.js b/services/web/frontend/stories/share-project-modal.stories.js
index 9c9daacf56..07615320ad 100644
--- a/services/web/frontend/stories/share-project-modal.stories.js
+++ b/services/web/frontend/stories/share-project-modal.stories.js
@@ -1,62 +1,71 @@
import { useEffect } from 'react'
import ShareProjectModal from '../js/features/share-project-modal/components/share-project-modal'
import useFetchMock from './hooks/use-fetch-mock'
-import { withContextRoot } from './utils/with-context-root'
+import { useScope } from './hooks/use-scope'
+import { ScopeDecorator } from './decorators/scope'
+import { contacts } from './fixtures/contacts'
+import { project } from './fixtures/project'
export const LinkSharingOff = args => {
useFetchMock(setupFetchMock)
- const project = {
- ...args.project,
- publicAccesLevel: 'private',
- }
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'private',
+ },
+ })
- return withContextRoot(, { project })
+ return
}
export const LinkSharingOn = args => {
useFetchMock(setupFetchMock)
- const project = {
- ...args.project,
- publicAccesLevel: 'tokenBased',
- }
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'tokenBased',
+ },
+ })
- return withContextRoot(, { project })
+ return
}
export const LinkSharingLoading = args => {
useFetchMock(setupFetchMock)
- const project = {
- ...args.project,
- publicAccesLevel: 'tokenBased',
- tokens: undefined,
- }
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'tokenBased',
+ tokens: undefined,
+ },
+ })
- return withContextRoot(, { project })
+ return
}
export const NonAdminLinkSharingOff = args => {
- const project = {
- ...args.project,
- publicAccesLevel: 'private',
- }
-
- return withContextRoot(, {
- project,
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'private',
+ },
})
+
+ return
}
export const NonAdminLinkSharingOn = args => {
- const project = {
- ...args.project,
- publicAccesLevel: 'tokenBased',
- }
-
- return withContextRoot(, {
- project,
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'tokenBased',
+ },
})
+
+ return
}
export const RestrictedTokenMember = args => {
@@ -64,103 +73,64 @@ export const RestrictedTokenMember = args => {
// original value on unmount
// Currently this is necessary because the context value is set from window,
// however in the future we should change this to set via props
- const originalIsRestrictedTokenMember = window.isRestrictedTokenMember
- window.isRestrictedTokenMember = true
useEffect(() => {
+ const originalIsRestrictedTokenMember = window.isRestrictedTokenMember
+ window.isRestrictedTokenMember = true
return () => {
window.isRestrictedTokenMember = originalIsRestrictedTokenMember
}
})
- const project = {
- ...args.project,
- publicAccesLevel: 'tokenBased',
- }
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'tokenBased',
+ },
+ })
- return withContextRoot(, { project })
+ return
}
export const LegacyLinkSharingReadAndWrite = args => {
useFetchMock(setupFetchMock)
- const project = {
- ...args.project,
- publicAccesLevel: 'readAndWrite',
- }
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'readAndWrite',
+ },
+ })
- return withContextRoot(, { project })
+ return
}
export const LegacyLinkSharingReadOnly = args => {
useFetchMock(setupFetchMock)
- const project = {
- ...args.project,
- publicAccesLevel: 'readOnly',
- }
+ useScope({
+ project: {
+ ...args.project,
+ publicAccesLevel: 'readOnly',
+ },
+ })
- return withContextRoot(, { project })
+ return
}
export const LimitedCollaborators = args => {
useFetchMock(setupFetchMock)
- const project = {
- ...args.project,
- features: {
- ...args.project.features,
- collaborators: 3,
+ useScope({
+ project: {
+ ...args.project,
+ features: {
+ ...args.project.features,
+ collaborators: 3,
+ },
},
- }
+ })
- return withContextRoot(, { project })
-}
-
-const project = {
- _id: 'a-project',
- name: 'A Project',
- features: {
- collaborators: -1, // unlimited
- },
- publicAccesLevel: 'private',
- tokens: {
- readOnly: 'ro-token',
- readAndWrite: 'rw-token',
- },
- owner: {
- _id: 'fakeOwnerId',
- email: 'stories@overleaf.com',
- },
- members: [
- {
- _id: 'viewer-member',
- type: 'user',
- privileges: 'readOnly',
- name: 'Viewer User',
- email: 'viewer@example.com',
- },
- {
- _id: 'author-member',
- type: 'user',
- privileges: 'readAndWrite',
- name: 'Author User',
- email: 'author@example.com',
- },
- ],
- invites: [
- {
- _id: 'test-invite-1',
- privileges: 'readOnly',
- name: 'Invited Viewer',
- email: 'invited-viewer@example.com',
- },
- {
- _id: 'test-invite-2',
- privileges: 'readAndWrite',
- name: 'Invited Author',
- email: 'invited-author@example.com',
- },
- ],
+ return
}
export default {
@@ -176,50 +146,9 @@ export default {
argTypes: {
handleHide: { action: 'hide' },
},
+ decorators: [ScopeDecorator],
}
-const contacts = [
- // user with edited name
- {
- type: 'user',
- email: 'test-user@example.com',
- first_name: 'Test',
- last_name: 'User',
- name: 'Test User',
- },
- // user with default name (email prefix)
- {
- type: 'user',
- email: 'test@example.com',
- first_name: 'test',
- },
- // no last name
- {
- type: 'user',
- first_name: 'Eratosthenes',
- email: 'eratosthenes@example.com',
- },
- // more users
- {
- type: 'user',
- first_name: 'Claudius',
- last_name: 'Ptolemy',
- email: 'ptolemy@example.com',
- },
- {
- type: 'user',
- first_name: 'Abd al-Rahman',
- last_name: 'Al-Sufi',
- email: 'al-sufi@example.com',
- },
- {
- type: 'user',
- first_name: 'Nicolaus',
- last_name: 'Copernicus',
- email: 'copernicus@example.com',
- },
-]
-
function setupFetchMock(fetchMock) {
const delay = 1000
diff --git a/services/web/frontend/stories/utils/with-context-root.js b/services/web/frontend/stories/utils/with-context-root.js
deleted file mode 100644
index 18020ab5d9..0000000000
--- a/services/web/frontend/stories/utils/with-context-root.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { ContextRoot } from '../../js/shared/context/root-context'
-import _ from 'lodash'
-
-// Unfortunately, we cannot currently use decorators here, since we need to
-// set a value on window, before the contexts are rendered.
-// When using decorators, the contexts are rendered before the story, so we
-// don't have the opportunity to set the window value first.
-export function withContextRoot(Story, scope) {
- const scopeWatchers = []
-
- const ide = {
- ...window._ide,
- $scope: {
- ...window._ide.$scope,
- ...scope,
- $watch: (key, callback) => {
- scopeWatchers.push([key, callback])
- },
- $applyAsync: callback => {
- window.setTimeout(() => {
- callback()
- for (const [key, watcher] of scopeWatchers) {
- watcher(_.get(ide.$scope, key))
- }
- }, 0)
- },
- $on: (eventName, callback) => {
- //
- },
- },
- }
-
- return (
-
- {Story}
-
- )
-}
diff --git a/services/web/frontend/stories/word-count-modal.stories.js b/services/web/frontend/stories/word-count-modal.stories.js
index 79f0ca7e16..7742815ea6 100644
--- a/services/web/frontend/stories/word-count-modal.stories.js
+++ b/services/web/frontend/stories/word-count-modal.stories.js
@@ -1,6 +1,6 @@
import useFetchMock from './hooks/use-fetch-mock'
-import { withContextRoot } from './utils/with-context-root'
import WordCountModal from '../js/features/word-count-modal/components/word-count-modal'
+import { ScopeDecorator } from './decorators/scope'
const counts = {
headers: 4,
@@ -14,11 +14,6 @@ const messages = [
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
].join('\n')
-const project = {
- _id: 'project-id',
- name: 'A Project',
-}
-
export const WordCount = args => {
useFetchMock(fetchMock => {
fetchMock.get(
@@ -28,7 +23,7 @@ export const WordCount = args => {
)
})
- return withContextRoot(, { project })
+ return
}
export const WordCountWithMessages = args => {
@@ -40,7 +35,7 @@ export const WordCountWithMessages = args => {
)
})
- return withContextRoot(, { project })
+ return
}
export const ErrorResponse = args => {
@@ -52,7 +47,7 @@ export const ErrorResponse = args => {
)
})
- return withContextRoot(, { project })
+ return
}
export default {
@@ -61,4 +56,8 @@ export default {
args: {
show: true,
},
+ argTypes: {
+ handleHide: { action: 'close modal' },
+ },
+ decorators: [ScopeDecorator],
}
diff --git a/services/web/tsconfig.json b/services/web/tsconfig.json
index 5b033da62b..a0b5e28ffd 100644
--- a/services/web/tsconfig.json
+++ b/services/web/tsconfig.json
@@ -18,6 +18,8 @@
"modules/**/frontend/js/**/*.*",
"test/frontend/**/*.*",
"modules/**/test/frontend/**/*.*",
+ "frontend/stories/**/*.*",
+ "modules/**/stories/**/*.*",
"cypress",
"types"
]
diff --git a/services/web/types/folder.ts b/services/web/types/folder.ts
new file mode 100644
index 0000000000..ee12a87a55
--- /dev/null
+++ b/services/web/types/folder.ts
@@ -0,0 +1,7 @@
+export type Folder = {
+ _id: string
+ name: string
+ docs: []
+ folders: []
+ fileRefs: []
+}
diff --git a/services/web/types/project.ts b/services/web/types/project.ts
new file mode 100644
index 0000000000..c6b01a6e53
--- /dev/null
+++ b/services/web/types/project.ts
@@ -0,0 +1,30 @@
+import { MongoUser } from './user'
+import { Folder } from './folder'
+
+type ProjectMember = {
+ _id: string
+ type: 'user'
+ privileges: 'readOnly' | 'readAndWrite'
+ name: string
+ email: string
+}
+
+type ProjectInvite = {
+ _id: string
+ privileges: 'readOnly' | 'readAndWrite'
+ name: string
+ email: string
+}
+
+export type Project = {
+ _id: string
+ name: string
+ features: Record
+ publicAccesLevel?: string
+ tokens: Record
+ owner: MongoUser
+ members: ProjectMember[]
+ invites: ProjectInvite[]
+ rootDocId?: string
+ rootFolder?: Folder[]
+}
diff --git a/services/web/types/user.ts b/services/web/types/user.ts
new file mode 100644
index 0000000000..f657355480
--- /dev/null
+++ b/services/web/types/user.ts
@@ -0,0 +1,8 @@
+export type User = {
+ id: string
+ email: string
+ allowedFreeTrial?: boolean
+ features?: Record
+}
+
+export type MongoUser = Pick> & { _id: string }
diff --git a/services/web/types/window.ts b/services/web/types/window.ts
index de5401b495..55e6eacba4 100644
--- a/services/web/types/window.ts
+++ b/services/web/types/window.ts
@@ -15,5 +15,8 @@ declare global {
currentLangCode: string
}
ExposedSettings: ExposedSettings
+ project_id: string
+ gitBridgePublicBaseUrl: string
+ _ide: Record
}
}