mirror of
https://github.com/overleaf/overleaf.git
synced 2024-09-16 02:52:31 -04:00
Data handling for settings in editor left menu react migration (#10470)
- Importing SettingsController from the angular code, which enables post requests for every select menu and socket handler for compiler, texlive version, and main document select option - New context for the data handling infrastructure between react and angular. The data is still located in the angular version, and I use the context only as a proxy to fetch/post new data. GitOrigin-RevId: 59009bceb128d82969a2318e90036aacf79f9887
This commit is contained in:
parent
079a0dcae4
commit
f27562eb12
34 changed files with 753 additions and 83 deletions
|
@ -1 +1,2 @@
|
||||||
|
div(ng-controller="SettingsController")
|
||||||
editor-left-menu()
|
editor-left-menu()
|
|
@ -1,18 +1,23 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsAutoCloseBrackets() {
|
export default function SettingsAutoCloseBrackets() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { autoPairDelimiters, setAutoPairDelimiters } =
|
||||||
|
useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect
|
||||||
|
onChange={setAutoPairDelimiters}
|
||||||
|
value={autoPairDelimiters}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'true',
|
value: true,
|
||||||
label: t('on'),
|
label: t('on'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'false',
|
value: false,
|
||||||
label: t('off'),
|
label: t('off'),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsAutoComplete() {
|
export default function SettingsAutoComplete() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { autoComplete, setAutoComplete } = useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect
|
||||||
|
onChange={setAutoComplete}
|
||||||
|
value={autoComplete}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'true',
|
value: true,
|
||||||
label: t('on'),
|
label: t('on'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'false',
|
value: false,
|
||||||
label: t('off'),
|
label: t('off'),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type { ProjectCompiler } from '../../../../../../types/project-settings'
|
||||||
import { useEditorContext } from '../../../../shared/context/editor-context'
|
import { useEditorContext } from '../../../../shared/context/editor-context'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsCompiler() {
|
export default function SettingsCompiler() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { permissionsLevel } = useEditorContext()
|
const { permissionsLevel } = useEditorContext()
|
||||||
|
const { compiler, setCompiler } = useProjectSettingsContext()
|
||||||
|
|
||||||
if (permissionsLevel === 'readOnly') {
|
if (permissionsLevel === 'readOnly') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect<ProjectCompiler>
|
||||||
|
onChange={setCompiler}
|
||||||
|
value={compiler}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'pdflatex',
|
value: 'pdflatex',
|
||||||
|
|
|
@ -2,28 +2,17 @@ import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import isValidTeXFile from '../../../../main/is-valid-tex-file'
|
import isValidTeXFile from '../../../../main/is-valid-tex-file'
|
||||||
import { useEditorContext } from '../../../../shared/context/editor-context'
|
import { useEditorContext } from '../../../../shared/context/editor-context'
|
||||||
import { useProjectContext } from '../../../../shared/context/project-context'
|
|
||||||
import useScopeValue from '../../../../shared/hooks/use-scope-value'
|
import useScopeValue from '../../../../shared/hooks/use-scope-value'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
import type { Option } from './settings-menu-select'
|
import type { Option } from './settings-menu-select'
|
||||||
|
import type { MainDocument } from '../../../../../../types/project-settings'
|
||||||
type Doc = {
|
|
||||||
doc: {
|
|
||||||
name: string
|
|
||||||
id: string
|
|
||||||
type: string
|
|
||||||
selected: boolean
|
|
||||||
}
|
|
||||||
path: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SettingsDocument() {
|
export default function SettingsDocument() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { permissionsLevel } = useEditorContext()
|
const { permissionsLevel } = useEditorContext()
|
||||||
|
const [docs] = useScopeValue<MainDocument[] | undefined>('docs')
|
||||||
const { rootDocId } = useProjectContext()
|
const { rootDocId, setRootDocId } = useProjectSettingsContext()
|
||||||
const [docs] = useScopeValue<Doc[] | undefined>('docs')
|
|
||||||
|
|
||||||
const validDocsOptions = useMemo(() => {
|
const validDocsOptions = useMemo(() => {
|
||||||
const filteredDocs =
|
const filteredDocs =
|
||||||
|
@ -45,6 +34,8 @@ export default function SettingsDocument() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect
|
||||||
|
onChange={setRootDocId}
|
||||||
|
value={rootDocId}
|
||||||
options={validDocsOptions}
|
options={validDocsOptions}
|
||||||
label={t('main_document')}
|
label={t('main_document')}
|
||||||
name="rootDoc_id"
|
name="rootDoc_id"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import getMeta from '../../../../utils/meta'
|
import getMeta from '../../../../utils/meta'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
import type { Option } from './settings-menu-select'
|
import type { Option } from './settings-menu-select'
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ export default function SettingsEditorTheme() {
|
||||||
const legacyEditorThemes = getMeta('ol-legacyEditorThemes') as
|
const legacyEditorThemes = getMeta('ol-legacyEditorThemes') as
|
||||||
| string[]
|
| string[]
|
||||||
| undefined
|
| undefined
|
||||||
|
const { editorTheme, setEditorTheme } = useProjectSettingsContext()
|
||||||
|
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
const editorThemeOptions: Array<Option> =
|
const editorThemeOptions: Array<Option> =
|
||||||
|
@ -36,9 +38,11 @@ export default function SettingsEditorTheme() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect
|
||||||
|
onChange={setEditorTheme}
|
||||||
|
value={editorTheme}
|
||||||
|
options={options}
|
||||||
label={t('editor_theme')}
|
label={t('editor_theme')}
|
||||||
name="editorTheme"
|
name="editorTheme"
|
||||||
options={options}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { FontFamily } from '../../../../../../modules/source-editor/frontend/js/extensions/theme'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsFontFamily() {
|
export default function SettingsFontFamily() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { fontFamily, setFontFamily } = useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect<FontFamily>
|
||||||
|
onChange={setFontFamily}
|
||||||
|
value={fontFamily}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'monaco',
|
value: 'monaco',
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
import type { Option } from './settings-menu-select'
|
import type { Option } from './settings-menu-select'
|
||||||
|
|
||||||
|
@ -10,9 +11,12 @@ const options: Array<Option> = sizes.map(size => ({
|
||||||
|
|
||||||
export default function SettingsFontSize() {
|
export default function SettingsFontSize() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { fontSize, setFontSize } = useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect
|
||||||
|
onChange={setFontSize}
|
||||||
|
value={fontSize}
|
||||||
options={options}
|
options={options}
|
||||||
label={t('font_size')}
|
label={t('font_size')}
|
||||||
name="fontSize"
|
name="fontSize"
|
||||||
|
|
|
@ -3,16 +3,13 @@ import { useTranslation } from 'react-i18next'
|
||||||
import getMeta from '../../../../utils/meta'
|
import getMeta from '../../../../utils/meta'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
import type { Option } from './settings-menu-select'
|
import type { Option } from './settings-menu-select'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
/* eslint-disable react/no-unused-prop-types */
|
import type { AllowedImageName } from '../../../../../../types/project-settings'
|
||||||
type AllowedImageName = {
|
|
||||||
imageDesc: string
|
|
||||||
imageName: string
|
|
||||||
}
|
|
||||||
/* eslint-enable */
|
|
||||||
|
|
||||||
export default function SettingsImageName() {
|
export default function SettingsImageName() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { imageName, setImageName } = useProjectSettingsContext()
|
||||||
|
|
||||||
const allowedImageNames = getMeta('ol-allowedImageNames') as
|
const allowedImageNames = getMeta('ol-allowedImageNames') as
|
||||||
| AllowedImageName[]
|
| AllowedImageName[]
|
||||||
| undefined
|
| undefined
|
||||||
|
@ -32,6 +29,8 @@ export default function SettingsImageName() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect
|
||||||
|
onChange={setImageName}
|
||||||
|
value={imageName}
|
||||||
options={options}
|
options={options}
|
||||||
label={t('tex_live_version')}
|
label={t('tex_live_version')}
|
||||||
name="imageName"
|
name="imageName"
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type { Keybindings } from '../../../../../../types/project-settings'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsKeybindings() {
|
export default function SettingsKeybindings() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { mode, setMode } = useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect<Keybindings>
|
||||||
|
onChange={setMode}
|
||||||
|
value={mode}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'default',
|
value: 'default',
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type { LineHeight } from '../../../../../../modules/source-editor/frontend/js/extensions/theme'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsLineHeight() {
|
export default function SettingsLineHeight() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { lineHeight, setLineHeight } = useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect<LineHeight>
|
||||||
|
onChange={setLineHeight}
|
||||||
|
value={lineHeight}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'compact',
|
value: 'compact',
|
||||||
|
|
|
@ -1,43 +1,68 @@
|
||||||
export type Option = {
|
import { ChangeEventHandler, useCallback } from 'react'
|
||||||
value: string
|
|
||||||
|
type PossibleValue = string | boolean
|
||||||
|
|
||||||
|
export type Option<T extends PossibleValue = string> = {
|
||||||
|
value: T
|
||||||
label: string
|
label: string
|
||||||
ariaHidden?: 'true' | 'false'
|
ariaHidden?: 'true' | 'false'
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Optgroup = {
|
export type Optgroup<T extends PossibleValue = string> = {
|
||||||
label: string
|
label: string
|
||||||
options: Array<Option>
|
options: Array<Option<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingsMenuSelectProps = {
|
type SettingsMenuSelectProps<T extends PossibleValue = string> = {
|
||||||
label: string
|
label: string
|
||||||
name: string
|
name: string
|
||||||
options: Array<Option>
|
options: Array<Option<T>>
|
||||||
optgroup?: Optgroup
|
optgroup?: Optgroup<T>
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
|
onChange: (val: T) => void
|
||||||
|
value?: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SettingsMenuSelect({
|
export default function SettingsMenuSelect<T extends PossibleValue = string>({
|
||||||
label,
|
label,
|
||||||
name,
|
name,
|
||||||
options,
|
options,
|
||||||
optgroup,
|
optgroup,
|
||||||
loading,
|
loading,
|
||||||
}: SettingsMenuSelectProps) {
|
onChange,
|
||||||
|
value,
|
||||||
|
}: SettingsMenuSelectProps<T>) {
|
||||||
|
const handleChange: ChangeEventHandler<HTMLSelectElement> = useCallback(
|
||||||
|
event => {
|
||||||
|
let value: PossibleValue = event.target.value
|
||||||
|
if (value === 'true' || value === 'false') {
|
||||||
|
value = value === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(value as T)
|
||||||
|
},
|
||||||
|
[onChange]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-group left-menu-setting">
|
<div className="form-group left-menu-setting">
|
||||||
<label htmlFor={name}>{label}</label>
|
<label htmlFor={`settings-menu-${name}`}>{label}</label>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p className="loading pull-right">
|
<p className="loading pull-right">
|
||||||
<i className="fa fa-fw fa-spin fa-refresh" />
|
<i className="fa fa-fw fa-spin fa-refresh" />
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<select name={name} className="form-control">
|
<select
|
||||||
|
id={`settings-menu-${name}`}
|
||||||
|
className="form-control"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={value?.toString()}
|
||||||
|
>
|
||||||
{options.map(option => (
|
{options.map(option => (
|
||||||
<option
|
<option
|
||||||
key={`${name}-${option.value}`}
|
key={`${name}-${option.value}`}
|
||||||
value={option.value}
|
value={option.value.toString()}
|
||||||
aria-hidden={option.ariaHidden}
|
aria-hidden={option.ariaHidden}
|
||||||
disabled={option.disabled}
|
disabled={option.disabled}
|
||||||
>
|
>
|
||||||
|
@ -47,7 +72,10 @@ export default function SettingsMenuSelect({
|
||||||
{optgroup ? (
|
{optgroup ? (
|
||||||
<optgroup label={optgroup.label}>
|
<optgroup label={optgroup.label}>
|
||||||
{optgroup.options.map(option => (
|
{optgroup.options.map(option => (
|
||||||
<option value={option.value} key={option.value}>
|
<option
|
||||||
|
value={option.value.toString()}
|
||||||
|
key={option.value.toString()}
|
||||||
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -2,25 +2,22 @@ import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useLayoutContext } from '../../../../shared/context/layout-context'
|
import { useLayoutContext } from '../../../../shared/context/layout-context'
|
||||||
import getMeta from '../../../../utils/meta'
|
import getMeta from '../../../../utils/meta'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect, { Option } from './settings-menu-select'
|
||||||
import type { Option } from './settings-menu-select'
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
|
import type { OverallThemeMeta } from '../../../../../../types/project-settings'
|
||||||
type OverallTheme = {
|
import type { OverallTheme } from '../../../../../../modules/source-editor/frontend/js/extensions/theme'
|
||||||
name: string
|
|
||||||
path: string
|
|
||||||
val: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SettingsOverallTheme() {
|
export default function SettingsOverallTheme() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const overallThemes = getMeta('ol-overallThemes') as
|
const overallThemes = getMeta('ol-overallThemes') as
|
||||||
| OverallTheme[]
|
| OverallThemeMeta[]
|
||||||
| undefined
|
| undefined
|
||||||
const { loadingStyleSheet } = useLayoutContext() as {
|
const { loadingStyleSheet } = useLayoutContext() as {
|
||||||
loadingStyleSheet: boolean
|
loadingStyleSheet: boolean
|
||||||
}
|
}
|
||||||
|
const { overallTheme, setOverallTheme } = useProjectSettingsContext()
|
||||||
|
|
||||||
const options: Array<Option> = useMemo(
|
const options: Array<Option<OverallTheme>> = useMemo(
|
||||||
() =>
|
() =>
|
||||||
overallThemes?.map(({ name, val }) => ({
|
overallThemes?.map(({ name, val }) => ({
|
||||||
value: val,
|
value: val,
|
||||||
|
@ -37,7 +34,9 @@ export default function SettingsOverallTheme() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect<OverallTheme>
|
||||||
|
onChange={setOverallTheme}
|
||||||
|
value={overallTheme}
|
||||||
options={options}
|
options={options}
|
||||||
loading={loadingStyleSheet}
|
loading={loadingStyleSheet}
|
||||||
label={t('overall_theme')}
|
label={t('overall_theme')}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type { PdfViewer } from '../../../../../../types/project-settings'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsPdfViewer() {
|
export default function SettingsPdfViewer() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { pdfViewer, setPdfViewer } = useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect<PdfViewer>
|
||||||
|
onChange={setPdfViewer}
|
||||||
|
value={pdfViewer}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'pdfjs',
|
value: 'pdfjs',
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import getMeta from '../../../../utils/meta'
|
import getMeta from '../../../../utils/meta'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
import type { Optgroup } from './settings-menu-select'
|
import type { Optgroup } from './settings-menu-select'
|
||||||
|
import type { SpellCheckLanguage } from '../../../../../../types/project-settings'
|
||||||
type Language = {
|
|
||||||
name: string
|
|
||||||
code: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SettingsSpellCheckLanguage() {
|
export default function SettingsSpellCheckLanguage() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const languages = getMeta('ol-languages') as Language[] | undefined
|
const languages = getMeta('ol-languages') as SpellCheckLanguage[] | undefined
|
||||||
|
|
||||||
|
const { spellCheckLanguage, setSpellCheckLanguage } =
|
||||||
|
useProjectSettingsContext()
|
||||||
|
|
||||||
const optgroup: Optgroup = useMemo(
|
const optgroup: Optgroup = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -27,6 +27,8 @@ export default function SettingsSpellCheckLanguage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect
|
||||||
|
onChange={setSpellCheckLanguage}
|
||||||
|
value={spellCheckLanguage}
|
||||||
options={[{ value: '', label: t('off') }]}
|
options={[{ value: '', label: t('off') }]}
|
||||||
optgroup={optgroup}
|
optgroup={optgroup}
|
||||||
label={t('spell_check')}
|
label={t('spell_check')}
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||||
import SettingsMenuSelect from './settings-menu-select'
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
export default function SettingsSyntaxValidation() {
|
export default function SettingsSyntaxValidation() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { syntaxValidation, setSyntaxValidation } = useProjectSettingsContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsMenuSelect
|
<SettingsMenuSelect<boolean>
|
||||||
|
onChange={setSyntaxValidation}
|
||||||
|
value={syntaxValidation}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'true',
|
value: true,
|
||||||
label: t('on'),
|
label: t('on'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'false',
|
value: false,
|
||||||
label: t('off'),
|
label: t('off'),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
import { createContext, useContext, useMemo } from 'react'
|
||||||
|
import type { PropsWithChildren } from 'react'
|
||||||
|
import type {
|
||||||
|
FontFamily,
|
||||||
|
LineHeight,
|
||||||
|
OverallTheme,
|
||||||
|
} from '../../../../../modules/source-editor/frontend/js/extensions/theme'
|
||||||
|
import type {
|
||||||
|
Keybindings,
|
||||||
|
PdfViewer,
|
||||||
|
ProjectCompiler,
|
||||||
|
} from '../../../../../types/project-settings'
|
||||||
|
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
||||||
|
|
||||||
|
type ProjectSettingsContextValue = {
|
||||||
|
compiler: ProjectCompiler
|
||||||
|
setCompiler: (compiler: ProjectCompiler) => void
|
||||||
|
imageName: string
|
||||||
|
setImageName: (imageName: string) => void
|
||||||
|
rootDocId: string
|
||||||
|
setRootDocId: (rootDocId: string) => void
|
||||||
|
spellCheckLanguage: string
|
||||||
|
setSpellCheckLanguage: (spellCheckLanguage: string) => void
|
||||||
|
autoComplete: boolean
|
||||||
|
setAutoComplete: (autoComplete: boolean) => void
|
||||||
|
autoPairDelimiters: boolean
|
||||||
|
setAutoPairDelimiters: (autoPairDelimiters: boolean) => void
|
||||||
|
syntaxValidation: boolean
|
||||||
|
setSyntaxValidation: (syntaxValidation: boolean) => void
|
||||||
|
mode: Keybindings
|
||||||
|
setMode: (mode: Keybindings) => void
|
||||||
|
editorTheme: string
|
||||||
|
setEditorTheme: (editorTheme: string) => void
|
||||||
|
overallTheme: OverallTheme
|
||||||
|
setOverallTheme: (overallTheme: OverallTheme) => void
|
||||||
|
fontSize: string
|
||||||
|
setFontSize: (fontSize: string) => void
|
||||||
|
fontFamily: FontFamily
|
||||||
|
setFontFamily: (fontFamily: FontFamily) => void
|
||||||
|
lineHeight: LineHeight
|
||||||
|
setLineHeight: (lineHeight: LineHeight) => void
|
||||||
|
pdfViewer: PdfViewer
|
||||||
|
setPdfViewer: (pdfViewer: PdfViewer) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProjectSettingsContext = createContext<
|
||||||
|
ProjectSettingsContextValue | undefined
|
||||||
|
>(undefined)
|
||||||
|
|
||||||
|
export function ProjectSettingsProvider({
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<Record<string, never>>) {
|
||||||
|
const [compiler, setCompiler] =
|
||||||
|
useScopeValue<ProjectCompiler>('project.compiler')
|
||||||
|
const [imageName, setImageName] = useScopeValue<string>('project.imageName')
|
||||||
|
const [rootDocId, setRootDocId] = useScopeValue<string>('project.rootDoc_id')
|
||||||
|
const [spellCheckLanguage, setSpellCheckLanguage] = useScopeValue<string>(
|
||||||
|
'project.spellCheckLanguage'
|
||||||
|
)
|
||||||
|
const [autoComplete, setAutoComplete] = useScopeValue<boolean>(
|
||||||
|
'settings.autoComplete'
|
||||||
|
)
|
||||||
|
const [autoPairDelimiters, setAutoPairDelimiters] = useScopeValue<boolean>(
|
||||||
|
'settings.autoPairDelimiters'
|
||||||
|
)
|
||||||
|
const [syntaxValidation, setSyntaxValidation] = useScopeValue<boolean>(
|
||||||
|
'settings.syntaxValidation'
|
||||||
|
)
|
||||||
|
const [editorTheme, setEditorTheme] = useScopeValue<string>(
|
||||||
|
'settings.editorTheme'
|
||||||
|
)
|
||||||
|
const [overallTheme, setOverallTheme] = useScopeValue<OverallTheme>(
|
||||||
|
'settings.overallTheme'
|
||||||
|
)
|
||||||
|
const [mode, setMode] = useScopeValue<Keybindings>('settings.mode')
|
||||||
|
const [fontSize, setFontSize] = useScopeValue<string>('settings.fontSize')
|
||||||
|
const [fontFamily, setFontFamily] = useScopeValue<FontFamily>(
|
||||||
|
'settings.fontFamily'
|
||||||
|
)
|
||||||
|
const [lineHeight, setLineHeight] = useScopeValue<LineHeight>(
|
||||||
|
'settings.lineHeight'
|
||||||
|
)
|
||||||
|
const [pdfViewer, setPdfViewer] =
|
||||||
|
useScopeValue<PdfViewer>('settings.pdfViewer')
|
||||||
|
|
||||||
|
const value: ProjectSettingsContextValue = useMemo(
|
||||||
|
() => ({
|
||||||
|
compiler,
|
||||||
|
setCompiler,
|
||||||
|
imageName,
|
||||||
|
setImageName,
|
||||||
|
rootDocId,
|
||||||
|
setRootDocId,
|
||||||
|
spellCheckLanguage,
|
||||||
|
setSpellCheckLanguage,
|
||||||
|
autoComplete,
|
||||||
|
setAutoComplete,
|
||||||
|
autoPairDelimiters,
|
||||||
|
setAutoPairDelimiters,
|
||||||
|
syntaxValidation,
|
||||||
|
setSyntaxValidation,
|
||||||
|
editorTheme,
|
||||||
|
setEditorTheme,
|
||||||
|
overallTheme,
|
||||||
|
setOverallTheme,
|
||||||
|
mode,
|
||||||
|
setMode,
|
||||||
|
fontSize,
|
||||||
|
setFontSize,
|
||||||
|
fontFamily,
|
||||||
|
setFontFamily,
|
||||||
|
lineHeight,
|
||||||
|
setLineHeight,
|
||||||
|
pdfViewer,
|
||||||
|
setPdfViewer,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
compiler,
|
||||||
|
setCompiler,
|
||||||
|
imageName,
|
||||||
|
setImageName,
|
||||||
|
rootDocId,
|
||||||
|
setRootDocId,
|
||||||
|
spellCheckLanguage,
|
||||||
|
setSpellCheckLanguage,
|
||||||
|
autoComplete,
|
||||||
|
setAutoComplete,
|
||||||
|
autoPairDelimiters,
|
||||||
|
setAutoPairDelimiters,
|
||||||
|
syntaxValidation,
|
||||||
|
setSyntaxValidation,
|
||||||
|
editorTheme,
|
||||||
|
setEditorTheme,
|
||||||
|
overallTheme,
|
||||||
|
setOverallTheme,
|
||||||
|
mode,
|
||||||
|
setMode,
|
||||||
|
fontSize,
|
||||||
|
setFontSize,
|
||||||
|
fontFamily,
|
||||||
|
setFontFamily,
|
||||||
|
lineHeight,
|
||||||
|
setLineHeight,
|
||||||
|
pdfViewer,
|
||||||
|
setPdfViewer,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProjectSettingsContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</ProjectSettingsContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useProjectSettingsContext() {
|
||||||
|
const context = useContext(ProjectSettingsContext)
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
'useProjectSettingsContext is only available inside ProjectSettingsProvider'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
|
function isValidTeXFile(filename) {
|
||||||
const validTeXFileRegExp = new RegExp(
|
const validTeXFileRegExp = new RegExp(
|
||||||
`\\.(${window.ExposedSettings.validRootDocExtensions.join('|')})$`,
|
`\\.(${window.ExposedSettings.validRootDocExtensions.join('|')})$`,
|
||||||
'i'
|
'i'
|
||||||
)
|
)
|
||||||
|
|
||||||
function isValidTeXFile(filename) {
|
|
||||||
return validTeXFileRegExp.test(filename)
|
return validTeXFileRegExp.test(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { ChatProvider } from '../../features/chat/context/chat-context'
|
||||||
import { ProjectProvider } from './project-context'
|
import { ProjectProvider } from './project-context'
|
||||||
import { SplitTestProvider } from './split-test-context'
|
import { SplitTestProvider } from './split-test-context'
|
||||||
import { FileTreeDataProvider } from './file-tree-data-context'
|
import { FileTreeDataProvider } from './file-tree-data-context'
|
||||||
|
import { ProjectSettingsProvider } from '../../features/editor-left-menu/context/project-settings-context'
|
||||||
|
|
||||||
export function ContextRoot({ children, ide, settings }) {
|
export function ContextRoot({ children, ide, settings }) {
|
||||||
return (
|
return (
|
||||||
|
@ -22,6 +23,7 @@ export function ContextRoot({ children, ide, settings }) {
|
||||||
<FileTreeDataProvider>
|
<FileTreeDataProvider>
|
||||||
<DetachProvider>
|
<DetachProvider>
|
||||||
<EditorProvider settings={settings}>
|
<EditorProvider settings={settings}>
|
||||||
|
<ProjectSettingsProvider>
|
||||||
<LayoutProvider>
|
<LayoutProvider>
|
||||||
<LocalCompileProvider>
|
<LocalCompileProvider>
|
||||||
<DetachCompileProvider>
|
<DetachCompileProvider>
|
||||||
|
@ -29,6 +31,7 @@ export function ContextRoot({ children, ide, settings }) {
|
||||||
</DetachCompileProvider>
|
</DetachCompileProvider>
|
||||||
</LocalCompileProvider>
|
</LocalCompileProvider>
|
||||||
</LayoutProvider>
|
</LayoutProvider>
|
||||||
|
</ProjectSettingsProvider>
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</DetachProvider>
|
</DetachProvider>
|
||||||
</FileTreeDataProvider>
|
</FileTreeDataProvider>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsAutoCloseBrackets from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-auto-close-brackets'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsAutoCloseBrackets />', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsAutoCloseBrackets />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Auto-close Brackets')
|
||||||
|
|
||||||
|
const optionOn = within(select).getByText('On')
|
||||||
|
expect(optionOn.getAttribute('value')).to.equal('true')
|
||||||
|
|
||||||
|
const optionOff = within(select).getByText('Off')
|
||||||
|
expect(optionOff.getAttribute('value')).to.equal('false')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsAutoComplete from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-auto-complete'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsAutoComplete />', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsAutoComplete />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Auto-complete')
|
||||||
|
|
||||||
|
const optionOn = within(select).getByText('On')
|
||||||
|
expect(optionOn.getAttribute('value')).to.equal('true')
|
||||||
|
|
||||||
|
const optionOff = within(select).getByText('Off')
|
||||||
|
expect(optionOff.getAttribute('value')).to.equal('false')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsDocument from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-document'
|
||||||
|
import * as isValidTeXFileModule from '../../../../../../frontend/js/main/is-valid-tex-file'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
import type { MainDocument } from '../../../../../../types/project-settings'
|
||||||
|
|
||||||
|
describe('<SettingsDocument />', function () {
|
||||||
|
let isValidTeXFileStub: sinon.SinonStub
|
||||||
|
const docs: MainDocument[] = [
|
||||||
|
{
|
||||||
|
path: 'main.tex',
|
||||||
|
doc: {
|
||||||
|
name: 'main.tex',
|
||||||
|
id: '123abc',
|
||||||
|
type: 'doc',
|
||||||
|
selected: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
isValidTeXFileStub = sinon
|
||||||
|
.stub(isValidTeXFileModule, 'default')
|
||||||
|
.returns(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
isValidTeXFileStub.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsDocument />, {
|
||||||
|
scope: {
|
||||||
|
docs,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Main document')
|
||||||
|
|
||||||
|
const optionOn = within(select).getByText('main.tex')
|
||||||
|
expect(optionOn.getAttribute('value')).to.equal('123abc')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsEditorTheme from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-editor-theme'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsEditorTheme />', function () {
|
||||||
|
const editorThemes = ['editortheme-1', 'editortheme-2', 'editortheme-3']
|
||||||
|
|
||||||
|
const legacyEditorThemes = ['legacytheme-1', 'legacytheme-2', 'legacytheme-3']
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
window.metaAttributesCache.set('ol-editorThemes', editorThemes)
|
||||||
|
window.metaAttributesCache.set('ol-legacyEditorThemes', legacyEditorThemes)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
window.metaAttributesCache = new Map()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsEditorTheme />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Editor theme')
|
||||||
|
|
||||||
|
for (const theme of editorThemes) {
|
||||||
|
const option = within(select).getByText(theme.replace(/_/g, ' '))
|
||||||
|
expect(option.getAttribute('value')).to.equal(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const theme of legacyEditorThemes) {
|
||||||
|
const option = within(select).getByText(theme.replace(/_/g, ' '))
|
||||||
|
expect(option.getAttribute('value')).to.equal(theme)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsFontFamily from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-font-family'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsFontFamily />', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsFontFamily />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Font Family')
|
||||||
|
|
||||||
|
const optionMonaco = within(select).getByText('Monaco / Menlo / Consolas')
|
||||||
|
expect(optionMonaco.getAttribute('value')).to.equal('monaco')
|
||||||
|
|
||||||
|
const optionLucida = within(select).getByText('Lucida / Source Code Pro')
|
||||||
|
expect(optionLucida.getAttribute('value')).to.equal('lucida')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsFontSize from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-font-size'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsFontSize />', function () {
|
||||||
|
const sizes = ['10', '11', '12', '13', '14', '16', '18', '20', '22', '24']
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsFontSize />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Font Size')
|
||||||
|
|
||||||
|
for (const size of sizes) {
|
||||||
|
const option = within(select).getByText(`${size}px`)
|
||||||
|
expect(option.getAttribute('value')).to.equal(size)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsImageName from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-image-name'
|
||||||
|
import type { AllowedImageName } from '../../../../../../types/project-settings'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsImageName />', function () {
|
||||||
|
const allowedImageNames: AllowedImageName[] = [
|
||||||
|
{
|
||||||
|
imageDesc: 'Image 1',
|
||||||
|
imageName: 'img-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imageDesc: 'Image 2',
|
||||||
|
imageName: 'img-2',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
window.metaAttributesCache.set('ol-allowedImageNames', allowedImageNames)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
window.metaAttributesCache = new Map()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsImageName />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('TeX Live version')
|
||||||
|
|
||||||
|
for (const { imageName, imageDesc } of allowedImageNames) {
|
||||||
|
const option = within(select).getByText(imageDesc)
|
||||||
|
expect(option.getAttribute('value')).to.equal(imageName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsKeybindings from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-keybindings'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsKeybindings />', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsKeybindings />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Keybindings')
|
||||||
|
|
||||||
|
const optionNone = within(select).getByText('None')
|
||||||
|
expect(optionNone.getAttribute('value')).to.equal('default')
|
||||||
|
|
||||||
|
const optionVim = within(select).getByText('Vim')
|
||||||
|
expect(optionVim.getAttribute('value')).to.equal('vim')
|
||||||
|
|
||||||
|
const optionEmacs = within(select).getByText('Emacs')
|
||||||
|
expect(optionEmacs.getAttribute('value')).to.equal('emacs')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsLineHeight from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-line-height'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsLineHeight />', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsLineHeight />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Line Height')
|
||||||
|
|
||||||
|
const optionCompact = within(select).getByText('Compact')
|
||||||
|
expect(optionCompact.getAttribute('value')).to.equal('compact')
|
||||||
|
|
||||||
|
const optionNormal = within(select).getByText('Normal')
|
||||||
|
expect(optionNormal.getAttribute('value')).to.equal('normal')
|
||||||
|
|
||||||
|
const optionWide = within(select).getByText('Wide')
|
||||||
|
expect(optionWide.getAttribute('value')).to.equal('wide')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsOverallTheme from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-overall-theme'
|
||||||
|
import type { OverallThemeMeta } from '../../../../../../types/project-settings'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsOverallTheme />', function () {
|
||||||
|
const overallThemes: OverallThemeMeta[] = [
|
||||||
|
{
|
||||||
|
name: 'Overall Theme 1',
|
||||||
|
val: '',
|
||||||
|
path: 'https://overleaf.com/overalltheme-1.css',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Overall Theme 2',
|
||||||
|
val: 'light-',
|
||||||
|
path: 'https://overleaf.com/overalltheme-2.css',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
window.metaAttributesCache.set('ol-overallThemes', overallThemes)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
window.metaAttributesCache = new Map()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsOverallTheme />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Overall theme')
|
||||||
|
|
||||||
|
for (const theme of overallThemes) {
|
||||||
|
const option = within(select).getByText(theme.name)
|
||||||
|
expect(option.getAttribute('value')).to.equal(theme.val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsPdfViewer from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-pdf-viewer'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsPdfViewer />', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsPdfViewer />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('PDF Viewer')
|
||||||
|
|
||||||
|
const optionOverleaf = within(select).getByText('Overleaf')
|
||||||
|
expect(optionOverleaf.getAttribute('value')).to.equal('pdfjs')
|
||||||
|
|
||||||
|
const optionBrowser = within(select).getByText('Browser')
|
||||||
|
expect(optionBrowser.getAttribute('value')).to.equal('native')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsSpellCheckLanguage from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-spell-check-language'
|
||||||
|
import type { SpellCheckLanguage } from '../../../../../../types/project-settings'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsSpellCheckLanguage />', function () {
|
||||||
|
const languages: SpellCheckLanguage[] = [
|
||||||
|
{
|
||||||
|
name: 'Lang 1',
|
||||||
|
code: 'lang-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Lang 2',
|
||||||
|
code: 'lang-2',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
window.metaAttributesCache.set('ol-languages', languages)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
window.metaAttributesCache = new Map()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsSpellCheckLanguage />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Spell check')
|
||||||
|
|
||||||
|
const optionEmpty = within(select).getByText('Off')
|
||||||
|
expect(optionEmpty.getAttribute('value')).to.equal('')
|
||||||
|
|
||||||
|
for (const language of languages) {
|
||||||
|
const option = within(select).getByText(language.name)
|
||||||
|
expect(option.getAttribute('value')).to.equal(language.code)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { screen, within } from '@testing-library/dom'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import SettingsSyntaxValidation from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-syntax-validation'
|
||||||
|
import { renderWithEditorContext } from '../../../../helpers/render-with-context'
|
||||||
|
|
||||||
|
describe('<SettingsSyntaxValidation />', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows correct menu', async function () {
|
||||||
|
renderWithEditorContext(<SettingsSyntaxValidation />)
|
||||||
|
|
||||||
|
const select = screen.getByLabelText('Code check')
|
||||||
|
|
||||||
|
const optionOn = within(select).getByText('On')
|
||||||
|
expect(optionOn.getAttribute('value')).to.equal('true')
|
||||||
|
|
||||||
|
const optionOff = within(select).getByText('Off')
|
||||||
|
expect(optionOff.getAttribute('value')).to.equal('false')
|
||||||
|
})
|
||||||
|
})
|
|
@ -12,6 +12,7 @@ import { DetachProvider } from '../../../frontend/js/shared/context/detach-conte
|
||||||
import { LayoutProvider } from '../../../frontend/js/shared/context/layout-context'
|
import { LayoutProvider } from '../../../frontend/js/shared/context/layout-context'
|
||||||
import { LocalCompileProvider } from '../../../frontend/js/shared/context/local-compile-context'
|
import { LocalCompileProvider } from '../../../frontend/js/shared/context/local-compile-context'
|
||||||
import { DetachCompileProvider } from '../../../frontend/js/shared/context/detach-compile-context'
|
import { DetachCompileProvider } from '../../../frontend/js/shared/context/detach-compile-context'
|
||||||
|
import { ProjectSettingsProvider } from '../../../frontend/js/features/editor-left-menu/context/project-settings-context'
|
||||||
|
|
||||||
// these constants can be imported in tests instead of
|
// these constants can be imported in tests instead of
|
||||||
// using magic strings
|
// using magic strings
|
||||||
|
@ -112,11 +113,15 @@ export function EditorProviders({
|
||||||
<FileTreeDataProvider>
|
<FileTreeDataProvider>
|
||||||
<DetachProvider>
|
<DetachProvider>
|
||||||
<EditorProvider settings={{}}>
|
<EditorProvider settings={{}}>
|
||||||
|
<ProjectSettingsProvider>
|
||||||
<LayoutProvider>
|
<LayoutProvider>
|
||||||
<LocalCompileProvider>
|
<LocalCompileProvider>
|
||||||
<DetachCompileProvider>{children}</DetachCompileProvider>
|
<DetachCompileProvider>
|
||||||
|
{children}
|
||||||
|
</DetachCompileProvider>
|
||||||
</LocalCompileProvider>
|
</LocalCompileProvider>
|
||||||
</LayoutProvider>
|
</LayoutProvider>
|
||||||
|
</ProjectSettingsProvider>
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</DetachProvider>
|
</DetachProvider>
|
||||||
</FileTreeDataProvider>
|
</FileTreeDataProvider>
|
||||||
|
|
33
services/web/types/project-settings.ts
Normal file
33
services/web/types/project-settings.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { OverallTheme } from '../modules/source-editor/frontend/js/extensions/theme'
|
||||||
|
|
||||||
|
export type AllowedImageName = {
|
||||||
|
imageDesc: string
|
||||||
|
imageName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MainDocument = {
|
||||||
|
doc: {
|
||||||
|
name: string
|
||||||
|
id: string
|
||||||
|
type: string
|
||||||
|
selected: boolean
|
||||||
|
}
|
||||||
|
path: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProjectCompiler = 'pdflatex' | 'latex' | 'xelatex' | 'lualatex'
|
||||||
|
|
||||||
|
export type Keybindings = 'default' | 'vim' | 'emacs'
|
||||||
|
|
||||||
|
export type OverallThemeMeta = {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
val: OverallTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PdfViewer = 'pdfjs' | 'native'
|
||||||
|
|
||||||
|
export type SpellCheckLanguage = {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
}
|
Loading…
Reference in a new issue