Merge pull request #17373 from overleaf/jpa-figure-modal-options

[web] hide figure modal import options as configured server side

GitOrigin-RevId: f8907a33d413fcac238b00328eaba14d64f2f31b
This commit is contained in:
Miguel Serrano 2024-03-06 10:30:32 +01:00 committed by Copybot
parent ee4b8c0868
commit ac48d81987
7 changed files with 183 additions and 59 deletions

View file

@ -4,4 +4,7 @@ window.ExposedSettings = {
validRootDocExtensions: ['tex', 'Rtex', 'ltx', 'Rnw'],
fileIgnorePattern:
'**/{{__MACOSX,.git,.texpadtmp,.R}{,/**},.!(latexmkrc),*.{dvi,aux,log,toc,out,pdfsync,synctex,synctex(busy),fdb_latexmk,fls,nlo,ind,glo,gls,glg,bbl,blg,doc,docx,gz,swp}}',
hasLinkedProjectFileFeature: true,
hasLinkedProjectOutputFileFeature: true,
hasLinkUrlFeature: true,
} as typeof window.ExposedSettings

View file

@ -9,9 +9,14 @@ import { useTranslation } from 'react-i18next'
export const FigureModalSourcePicker: FC = () => {
const { t } = useTranslation()
const {
hasLinkedProjectFileFeature,
hasLinkedProjectOutputFileFeature,
hasLinkUrlFeature,
} = window.ExposedSettings
return (
<div className="figure-modal-source-selector">
<div className="figure-modal-source-button-row">
<div className="figure-modal-source-button-grid">
<FigureModalSourceButton
type={FigureModalSource.FILE_UPLOAD}
title={t('replace_from_computer')}
@ -22,18 +27,20 @@ export const FigureModalSourcePicker: FC = () => {
title={t('replace_from_project_files')}
icon="archive"
/>
</div>
<div className="figure-modal-source-button-row">
{(hasLinkedProjectFileFeature || hasLinkedProjectOutputFileFeature) && (
<FigureModalSourceButton
type={FigureModalSource.OTHER_PROJECT}
title={t('replace_from_another_project')}
icon="folder-open"
/>
)}
{hasLinkUrlFeature && (
<FigureModalSourceButton
type={FigureModalSource.FROM_URL}
title={t('replace_from_url')}
icon="globe"
/>
)}
</div>
</div>
)

View file

@ -35,7 +35,11 @@ export const FigureModalOtherProjectSource: FC = () => {
const { _id: projectId } = useProjectContext()
const { loading: projectsLoading, data: projects, error } = useUserProjects()
const [selectedProject, setSelectedProject] = useState<null | Project>(null)
const [usingOutputFiles, setUsingOutputFiles] = useState<boolean>(false)
const { hasLinkedProjectFileFeature, hasLinkedProjectOutputFileFeature } =
window.ExposedSettings
const [usingOutputFiles, setUsingOutputFiles] = useState<boolean>(
!hasLinkedProjectFileFeature
)
const [nameDirty, setNameDirty] = useState<boolean>(false)
const [name, setName] = useState<string>('')
const [folder, setFolder] = useState<File | null>(null)
@ -158,6 +162,7 @@ export const FigureModalOtherProjectSource: FC = () => {
})
}}
/>
{hasLinkedProjectFileFeature && hasLinkedProjectOutputFileFeature && (
<div>
or{' '}
<Button
@ -173,6 +178,7 @@ export const FigureModalOtherProjectSource: FC = () => {
</span>
</Button>
</div>
)}
<FileRelocator
folder={folder}
name={name}

View file

@ -22,6 +22,11 @@ export const InsertFigureDropdown = memo(function InsertFigureDropdown() {
},
[view]
)
const {
hasLinkedProjectFileFeature,
hasLinkedProjectOutputFileFeature,
hasLinkUrlFeature,
} = window.ExposedSettings
return (
<ToolbarButtonMenu
id="toolbar-figure"
@ -43,6 +48,7 @@ export const InsertFigureDropdown = memo(function InsertFigureDropdown() {
>
<Icon type="archive" fw /> From project files
</ListGroupItem>
{(hasLinkedProjectFileFeature || hasLinkedProjectOutputFileFeature) && (
<ListGroupItem
onClick={() =>
openFigureModal(FigureModalSource.OTHER_PROJECT, 'other-project')
@ -50,11 +56,16 @@ export const InsertFigureDropdown = memo(function InsertFigureDropdown() {
>
<Icon type="folder-open" fw /> From another project
</ListGroupItem>
)}
{hasLinkUrlFeature && (
<ListGroupItem
onClick={() => openFigureModal(FigureModalSource.FROM_URL, 'from-url')}
onClick={() =>
openFigureModal(FigureModalSource.FROM_URL, 'from-url')
}
>
<Icon type="globe" fw /> From URL
</ListGroupItem>
)}
</ToolbarButtonMenu>
)
})

View file

@ -136,7 +136,7 @@ const initialize = () => {
emailConfirmationDisabled: false,
enableSubscriptions: true,
hasAffiliationsFeature: false,
hasLinkUrlFeature: false,
hasLinkUrlFeature: true,
hasLinkedProjectFileFeature: true,
hasLinkedProjectOutputFileFeature: true,
hasSamlFeature: true,

View file

@ -99,9 +99,11 @@
cursor: pointer;
}
.figure-modal-source-button-row {
display: flex;
.figure-modal-source-button-grid {
display: grid;
justify-content: space-between;
gap: 8px;
grid-template-columns: 1fr 1fr;
margin: 0 auto;
&:not(:first-of-type) {
@ -120,14 +122,6 @@
border: none;
padding: 0 8px;
&:nth-child(even) {
margin-left: 4px;
}
&:nth-child(odd) {
margin-right: 4px;
}
&-title {
flex: 1 1 auto;
text-align: left;

View file

@ -3,6 +3,7 @@ import { EditorProviders } from '../../../helpers/editor-providers'
import { mockScope, rootFolderId } from '../helpers/mock-scope'
import { FC } from 'react'
import { FileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
import { ExposedSettings } from '../../../../../types/exposed-settings'
const Container: FC = ({ children }) => (
<div style={{ width: 1500, height: 785 }}>{children}</div>
@ -40,14 +41,8 @@ const matchUrl = (urlToMatch: RegExp | string) =>
describe('<FigureModal />', function () {
// TODO: rewrite these tests to be in source mode when toolbar is added there
// TODO: Write tests for width toggle, when we can match on source code
beforeEach(function () {
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
cy.interceptMathJax()
cy.interceptEvents()
cy.interceptSpelling()
function mount() {
const content = ''
const scope = mockScope(content)
scope.editor.showVisual = true
@ -74,6 +69,22 @@ describe('<FigureModal />', function () {
</EditorProviders>
</Container>
)
}
let previousExposedSettings: ExposedSettings
before(function () {
previousExposedSettings = window.ExposedSettings
})
afterEach(function () {
window.ExposedSettings = previousExposedSettings
})
beforeEach(function () {
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
cy.interceptMathJax()
cy.interceptEvents()
cy.interceptSpelling()
mount()
})
describe('Upload from computer source', function () {
@ -258,6 +269,98 @@ describe('<FigureModal />', function () {
})
})
describe('Feature flags', function () {
describe('with hasLinkUrlFeature=false', function () {
beforeEach(function () {
window.ExposedSettings = Object.assign({}, previousExposedSettings, {
hasLinkedProjectFileFeature: true,
hasLinkedProjectOutputFileFeature: true,
hasLinkUrlFeature: false,
})
mount()
clickToolbarButton('Insert Figure')
})
it('should not have import from url option', function () {
cy.findByRole('menu').within(() => {
cy.findByText('From URL').should('not.exist')
})
})
})
describe('with hasLinkedProjectFileFeature=false and hasLinkedProjectOutputFileFeature=false', function () {
beforeEach(function () {
window.ExposedSettings = Object.assign({}, previousExposedSettings, {
hasLinkedProjectFileFeature: false,
hasLinkedProjectOutputFileFeature: false,
hasLinkUrlFeature: true,
})
mount()
clickToolbarButton('Insert Figure')
})
it('should not have import from project file option', function () {
cy.findByRole('menu').within(() => {
cy.findByText('From another project').should('not.exist')
})
})
})
function setupFromAnotherProject() {
mount()
cy.interceptProjectListing()
clickToolbarButton('Insert Figure')
cy.findByRole('menu').within(() => {
cy.findByText('From another project').click()
})
cy.findByText('Select a project').click()
cy.findByRole('listbox').within(() => {
cy.findByText('My first project').click()
})
}
function expectNoOutputSwitch() {
it('should hide output switch', function () {
cy.findByText('select from output files').should('not.exist')
cy.findByText('select from source files').should('not.exist')
})
}
describe('with hasLinkedProjectFileFeature=false', function () {
beforeEach(function () {
window.ExposedSettings = Object.assign({}, previousExposedSettings, {
hasLinkedProjectFileFeature: false,
hasLinkedProjectOutputFileFeature: true,
hasLinkUrlFeature: true,
})
cy.interceptCompile()
setupFromAnotherProject()
})
expectNoOutputSwitch()
it('should show output file selector', function () {
cy.findByText('Select an output file').click()
cy.findByRole('listbox').within(() => {
cy.findByText('output.pdf').click()
})
})
})
describe('with hasLinkedProjectOutputFileFeature=false', function () {
beforeEach(function () {
window.ExposedSettings = Object.assign({}, previousExposedSettings, {
hasLinkedProjectFileFeature: true,
hasLinkedProjectOutputFileFeature: false,
hasLinkUrlFeature: true,
})
setupFromAnotherProject()
})
expectNoOutputSwitch()
it('should show source file selector', function () {
cy.findByText('Select a file').click()
cy.findByRole('listbox').within(() => {
cy.findByText('frog.jpg').click()
})
})
})
})
describe('From URL source', function () {
beforeEach(function () {
cy.interceptLinkedFile()