mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-05 01:26:55 +00:00
Merge pull request #7723 from overleaf/ta-settings-refactor
[SettingsPage] Refactor Linking Section GitOrigin-RevId: 49aa27cdcb3669c59c9a9c46edd3249cee876dd0
This commit is contained in:
parent
bc574ef9e9
commit
146a207fd1
16 changed files with 217 additions and 205 deletions
|
@ -774,6 +774,7 @@ module.exports = {
|
|||
sourceEditorExtensions: [],
|
||||
sourceEditorComponents: [],
|
||||
integrationLinkingWidgets: [],
|
||||
referenceLinkingWidgets: [],
|
||||
},
|
||||
|
||||
moduleImportSequence: ['launchpad', 'server-ce-scripts', 'user-activate'],
|
||||
|
|
|
@ -69,9 +69,12 @@ function EmailsSection() {
|
|||
}
|
||||
|
||||
return (
|
||||
<UserEmailsProvider>
|
||||
<EmailsSectionContent />
|
||||
</UserEmailsProvider>
|
||||
<>
|
||||
<UserEmailsProvider>
|
||||
<EmailsSectionContent />
|
||||
</UserEmailsProvider>
|
||||
<hr />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||
const integrationLinkingWidgets = importOverleafModules(
|
||||
'integrationLinkingWidgets'
|
||||
)
|
||||
|
||||
function IntegrationLinkingSection() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>{t('integrations')}</h3>
|
||||
<p>{t('linked_accounts_explained')}</p>
|
||||
<div className="settings-widgets-container">
|
||||
{integrationLinkingWidgets.map(
|
||||
({ import: importObject, path }, widgetIndex) => (
|
||||
<IntegrationLinkingWidget
|
||||
key={Object.keys(importObject)[0]}
|
||||
ModuleComponent={Object.values(importObject)[0]}
|
||||
isLast={widgetIndex === integrationLinkingWidgets.length - 1}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type IntegrationLinkingWidgetProps = {
|
||||
ModuleComponent: any
|
||||
isLast: boolean
|
||||
}
|
||||
|
||||
function IntegrationLinkingWidget({
|
||||
ModuleComponent,
|
||||
isLast,
|
||||
}: IntegrationLinkingWidgetProps) {
|
||||
return (
|
||||
<>
|
||||
<ModuleComponent />
|
||||
{isLast ? null : <hr />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default IntegrationLinkingSection
|
|
@ -0,0 +1,135 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||
import { useSSOContext, SSOSubscription } from '../context/sso-context'
|
||||
import { SSOLinkingWidget } from './linking/sso-widget'
|
||||
|
||||
const integrationLinkingWidgets = importOverleafModules(
|
||||
'integrationLinkingWidgets'
|
||||
)
|
||||
const referenceLinkingWidgets = importOverleafModules('referenceLinkingWidgets')
|
||||
|
||||
function LinkingSection() {
|
||||
const { t } = useTranslation()
|
||||
const { subscriptions } = useSSOContext()
|
||||
|
||||
const hasIntegrationLinkingSection = integrationLinkingWidgets.length
|
||||
const hasReferencesLinkingSection = referenceLinkingWidgets.length
|
||||
const hasSSOLinkingSection = Object.keys(subscriptions).length > 0
|
||||
|
||||
if (
|
||||
!hasIntegrationLinkingSection &&
|
||||
!hasReferencesLinkingSection &&
|
||||
!hasSSOLinkingSection
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>{t('integrations')}</h3>
|
||||
<p>{t('linked_accounts_explained')}</p>
|
||||
{hasIntegrationLinkingSection ? (
|
||||
<>
|
||||
<h3 className="text-capitalize">{t('sync_dropbox_github')}</h3>
|
||||
<div className="settings-widgets-container">
|
||||
{integrationLinkingWidgets.map(
|
||||
({ import: importObject, path }, widgetIndex) => (
|
||||
<ModuleLinkingWidget
|
||||
key={Object.keys(importObject)[0]}
|
||||
ModuleComponent={Object.values(importObject)[0]}
|
||||
isLast={widgetIndex === integrationLinkingWidgets.length - 1}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{hasReferencesLinkingSection ? (
|
||||
<>
|
||||
<h3 className="text-capitalize">{t('reference_sync')}</h3>
|
||||
<div className="settings-widgets-container">
|
||||
{referenceLinkingWidgets.map(
|
||||
({ import: importObject, path }, widgetIndex) => (
|
||||
<ModuleLinkingWidget
|
||||
key={Object.keys(importObject)[0]}
|
||||
ModuleComponent={Object.values(importObject)[0]}
|
||||
isLast={widgetIndex === referenceLinkingWidgets.length - 1}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{hasSSOLinkingSection ? (
|
||||
<>
|
||||
<h3 className="text-capitalize">{t('linked_accounts')}</h3>
|
||||
<div className="settings-widgets-container">
|
||||
{Object.values(subscriptions).map(
|
||||
(subscription, subscriptionIndex) => (
|
||||
<SSOLinkingWidgetContainer
|
||||
key={subscription.providerId}
|
||||
subscription={subscription}
|
||||
isLast={
|
||||
subscriptionIndex === Object.keys(subscriptions).length - 1
|
||||
}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{hasIntegrationLinkingSection ||
|
||||
hasReferencesLinkingSection ||
|
||||
hasSSOLinkingSection ? (
|
||||
<hr />
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type LinkingWidgetProps = {
|
||||
ModuleComponent: any
|
||||
isLast: boolean
|
||||
}
|
||||
|
||||
function ModuleLinkingWidget({ ModuleComponent, isLast }: LinkingWidgetProps) {
|
||||
return (
|
||||
<>
|
||||
<ModuleComponent />
|
||||
{isLast ? null : <hr />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type SSOLinkingWidgetContainerProps = {
|
||||
subscription: SSOSubscription
|
||||
isLast: boolean
|
||||
}
|
||||
|
||||
function SSOLinkingWidgetContainer({
|
||||
subscription,
|
||||
isLast,
|
||||
}: SSOLinkingWidgetContainerProps) {
|
||||
const { t } = useTranslation()
|
||||
const { unlink } = useSSOContext()
|
||||
|
||||
return (
|
||||
<>
|
||||
<SSOLinkingWidget
|
||||
providerId={subscription.providerId}
|
||||
title={subscription.provider.name}
|
||||
description={t(
|
||||
subscription.provider.descriptionKey,
|
||||
subscription.provider.descriptionOptions
|
||||
)}
|
||||
helpPath={subscription.provider.descriptionOptions?.link}
|
||||
linked={subscription.linked}
|
||||
linkPath={subscription.provider.linkPath}
|
||||
onUnlink={() => unlink(subscription.providerId)}
|
||||
/>
|
||||
{isLast ? null : <hr />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LinkingSection
|
|
@ -5,12 +5,12 @@ import getMeta from '../../../utils/meta'
|
|||
import EmailsSection from './emails-section'
|
||||
import AccountInfoSection from './account-info-section'
|
||||
import PasswordSection from './password-section'
|
||||
import IntegrationLinkingSection from './integration-linking-section'
|
||||
import SSOLinkingSection from './sso-linking-section'
|
||||
import LinkingSection from './linking-section'
|
||||
import MiscSection from './misc-section'
|
||||
import LeaveSection from './leave-section'
|
||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||
import { UserProvider } from '../../../shared/context/user-context'
|
||||
import { SSOProvider } from '../context/sso-context'
|
||||
|
||||
function SettingsPageRoot() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -34,7 +34,7 @@ function SettingsPageRoot() {
|
|||
<div className="page-header">
|
||||
<h1>{t('account_settings')}</h1>
|
||||
</div>
|
||||
<div className="account-settings">
|
||||
<div>
|
||||
<EmailsSection />
|
||||
<div className="row">
|
||||
<div className="col-md-5">
|
||||
|
@ -45,10 +45,9 @@ function SettingsPageRoot() {
|
|||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<IntegrationLinkingSection />
|
||||
<hr />
|
||||
<SSOLinkingSection />
|
||||
<hr />
|
||||
<SSOProvider>
|
||||
<LinkingSection />
|
||||
</SSOProvider>
|
||||
<MiscSection />
|
||||
<hr />
|
||||
<LeaveSection />
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
SSOProvider,
|
||||
useSSOContext,
|
||||
SSOSubscription,
|
||||
} from '../context/sso-context'
|
||||
import { SSOLinkingWidget } from './sso-linking/widget'
|
||||
|
||||
function SSOLinkingSection() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<SSOProvider>
|
||||
<h3 className="text-capitalize">{t('linked_accounts')}</h3>
|
||||
<p>{t('linked_accounts_explained')}</p>
|
||||
<SSOLinkingWidgets />
|
||||
</SSOProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function SSOLinkingWidgets() {
|
||||
const { subscriptions } = useSSOContext()
|
||||
|
||||
return (
|
||||
<div className="settings-widgets-container">
|
||||
{Object.values(subscriptions).map((subscription, subscriptionIndex) => (
|
||||
<SSOLinkingWidgetContainer
|
||||
key={subscription.providerId}
|
||||
subscription={subscription}
|
||||
isLast={subscriptionIndex === Object.keys(subscriptions).length - 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type SSOLinkingWidgetContainerProps = {
|
||||
subscription: SSOSubscription
|
||||
isLast: boolean
|
||||
}
|
||||
|
||||
function SSOLinkingWidgetContainer({
|
||||
subscription,
|
||||
isLast,
|
||||
}: SSOLinkingWidgetContainerProps) {
|
||||
const { t } = useTranslation()
|
||||
const { unlink } = useSSOContext()
|
||||
|
||||
return (
|
||||
<>
|
||||
<SSOLinkingWidget
|
||||
providerId={subscription.providerId}
|
||||
title={subscription.provider.name}
|
||||
description={t(
|
||||
subscription.provider.descriptionKey,
|
||||
subscription.provider.descriptionOptions
|
||||
)}
|
||||
helpPath={subscription.provider.descriptionOptions?.link}
|
||||
linked={subscription.linked}
|
||||
linkPath={subscription.provider.linkPath}
|
||||
onUnlink={() => unlink(subscription.providerId)}
|
||||
/>
|
||||
{isLast ? null : <hr />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SSOLinkingSection
|
|
@ -1,21 +0,0 @@
|
|||
const MOCK_DELAY = 1000
|
||||
|
||||
export function defaultSetupMocks(fetchMock) {
|
||||
fetchMock.get(
|
||||
'express:/user/tpds/queues',
|
||||
{ tpdsToWeb: 0, webToTpds: 0 },
|
||||
{ delay: MOCK_DELAY }
|
||||
)
|
||||
}
|
||||
|
||||
export function setDefaultMeta() {
|
||||
window.metaAttributesCache.set('ol-user', {
|
||||
features: { github: true, dropbox: true, mendeley: false, zotero: false },
|
||||
refProviders: {
|
||||
mendeley: true,
|
||||
zotero: true,
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-github', { enabled: false })
|
||||
window.metaAttributesCache.set('ol-dropbox', { registered: true })
|
||||
}
|
|
@ -1,10 +1,25 @@
|
|||
const MOCK_DELAY = 1000
|
||||
|
||||
export function defaultSetupMocks(fetchMock) {
|
||||
fetchMock.post('/user/oauth-unlink', 200, { delay: MOCK_DELAY })
|
||||
fetchMock
|
||||
.post('/user/oauth-unlink', 200, { delay: MOCK_DELAY })
|
||||
.get(
|
||||
'express:/user/tpds/queues',
|
||||
{ tpdsToWeb: 0, webToTpds: 0 },
|
||||
{ delay: MOCK_DELAY }
|
||||
)
|
||||
}
|
||||
|
||||
export function setDefaultMeta() {
|
||||
window.metaAttributesCache.set('ol-user', {
|
||||
features: { github: true, dropbox: true, mendeley: false, zotero: false },
|
||||
refProviders: {
|
||||
mendeley: true,
|
||||
zotero: true,
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-github', { enabled: false })
|
||||
window.metaAttributesCache.set('ol-dropbox', { registered: true })
|
||||
window.metaAttributesCache.set('ol-thirdPartyIds', {
|
||||
collabratec: 'collabratec-id',
|
||||
google: 'google-id',
|
|
@ -1,23 +0,0 @@
|
|||
import useFetchMock from '../hooks/use-fetch-mock'
|
||||
import IntegrationLinkingSection from '../../js/features/settings/components/integration-linking-section'
|
||||
import {
|
||||
setDefaultMeta,
|
||||
defaultSetupMocks,
|
||||
} from './helpers/integration-linking'
|
||||
import { UserProvider } from '../../js/shared/context/user-context'
|
||||
|
||||
export const Section = args => {
|
||||
useFetchMock(defaultSetupMocks)
|
||||
setDefaultMeta()
|
||||
|
||||
return (
|
||||
<UserProvider>
|
||||
<IntegrationLinkingSection {...args} />
|
||||
</UserProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Account Settings / Integration Linking / Section',
|
||||
component: IntegrationLinkingSection,
|
||||
}
|
46
services/web/frontend/stories/settings/linking.stories.js
Normal file
46
services/web/frontend/stories/settings/linking.stories.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
import useFetchMock from '../hooks/use-fetch-mock'
|
||||
import LinkingSection from '../../js/features/settings/components/linking-section'
|
||||
import { setDefaultMeta, defaultSetupMocks } from './helpers/linking'
|
||||
import { UserProvider } from '../../js/shared/context/user-context'
|
||||
import { SSOProvider } from '../../js/features/settings/context/sso-context'
|
||||
|
||||
export const Section = args => {
|
||||
useFetchMock(defaultSetupMocks)
|
||||
setDefaultMeta()
|
||||
|
||||
return (
|
||||
<UserProvider>
|
||||
<SSOProvider>
|
||||
<LinkingSection {...args} />
|
||||
</SSOProvider>
|
||||
</UserProvider>
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-github', { enabled: false })
|
||||
window.metaAttributesCache.set('ol-dropbox', { registered: false })
|
||||
|
||||
return (
|
||||
<UserProvider>
|
||||
<SSOProvider>
|
||||
<LinkingSection {...args} />
|
||||
</SSOProvider>
|
||||
</UserProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Account Settings / Linking',
|
||||
component: LinkingSection,
|
||||
}
|
|
@ -17,13 +17,9 @@ import {
|
|||
defaultSetupMocks as defaultSetupEmailsMocks,
|
||||
} from './helpers/emails'
|
||||
import {
|
||||
setDefaultMeta as setDefaultIntegrationLinkingMeta,
|
||||
defaultSetupMocks as defaultSetupIntegrationLinkingMocks,
|
||||
} from './helpers/integration-linking'
|
||||
import {
|
||||
setDefaultMeta as setDefaultSSOMeta,
|
||||
defaultSetupMocks as defaultSetupSSOMocks,
|
||||
} from './helpers/sso-linking'
|
||||
setDefaultMeta as setDefaultLinkingMeta,
|
||||
defaultSetupMocks as defaultSetupLinkingMocks,
|
||||
} from './helpers/linking'
|
||||
import { UserProvider } from '../../js/shared/context/user-context'
|
||||
|
||||
export const Root = args => {
|
||||
|
@ -31,14 +27,12 @@ export const Root = args => {
|
|||
setDefaultAccountInfoMeta()
|
||||
setDefaultPasswordMeta()
|
||||
setDefaultEmailsMeta()
|
||||
setDefaultIntegrationLinkingMeta()
|
||||
setDefaultSSOMeta()
|
||||
setDefaultLinkingMeta()
|
||||
useFetchMock(defaultSetupLeaveMocks)
|
||||
useFetchMock(defaultSetupAccountInfoMocks)
|
||||
useFetchMock(defaultSetupPasswordMocks)
|
||||
useFetchMock(defaultSetupEmailsMocks)
|
||||
useFetchMock(defaultSetupIntegrationLinkingMocks)
|
||||
useFetchMock(defaultSetupSSOMocks)
|
||||
useFetchMock(defaultSetupLinkingMocks)
|
||||
|
||||
return (
|
||||
<UserProvider>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import useFetchMock from '../hooks/use-fetch-mock'
|
||||
import SSOLinkingSection from '../../js/features/settings/components/sso-linking-section'
|
||||
import { setDefaultMeta, defaultSetupMocks } from './helpers/sso-linking'
|
||||
|
||||
export const Section = args => {
|
||||
useFetchMock(defaultSetupMocks)
|
||||
setDefaultMeta()
|
||||
|
||||
return <SSOLinkingSection {...args} />
|
||||
}
|
||||
|
||||
export const SectionAllUnlinked = args => {
|
||||
useFetchMock(defaultSetupMocks)
|
||||
setDefaultMeta()
|
||||
window.metaAttributesCache.set('ol-thirdPartyIds', {})
|
||||
|
||||
return <SSOLinkingSection {...args} />
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Account Settings / SSO Linking / Section',
|
||||
component: SSOLinkingSection,
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { expect } from 'chai'
|
||||
import { screen, fireEvent, render, waitFor } from '@testing-library/react'
|
||||
import { IntegrationLinkingWidget } from '../../../../../../frontend/js/features/settings/components/integration-linking/widget'
|
||||
import { IntegrationLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/integration-widget'
|
||||
|
||||
describe('<IntegrationLinkingWidgetTest/>', function () {
|
||||
const defaultProps = {
|
|
@ -1,7 +1,7 @@
|
|||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import { screen, fireEvent, render, waitFor } from '@testing-library/react'
|
||||
import { SSOLinkingWidget } from '../../../../../../frontend/js/features/settings/components/sso-linking/widget'
|
||||
import { SSOLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/sso-widget'
|
||||
|
||||
describe('<SSOLinkingWidget />', function () {
|
||||
const defaultProps = {
|
Loading…
Reference in a new issue