From 6a800028be1e7f53f825f40315bac96f1c8e95b9 Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Thu, 13 Aug 2026 09:15:42 +0100 Subject: [PATCH] Merge pull request #36474 from overleaf/mj-themed-file-tree-modals [web] Add dark mode file tree modals GitOrigin-RevId: 8bfd548eedd16fd6848a9d6460cdd7f1be50d759 --- .../modes/file-tree-upload-doc.tsx | 4 ++ .../modals/file-tree-modal-create-file.tsx | 4 +- .../modals/file-tree-modal-create-folder.tsx | 9 +++- .../modals/file-tree-modal-delete.tsx | 5 ++- .../modals/file-tree-modal-error.tsx | 4 +- .../stylesheets/pages/editor/file-tree.scss | 44 ++++++++++++++----- 6 files changed, 55 insertions(+), 15 deletions(-) diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.tsx index 812c481b26..bedb846ec0 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.tsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.tsx @@ -23,6 +23,7 @@ import { FolderUploadConflicts, } from '@/features/file-tree/components/file-tree-create/file-tree-upload-conflicts' import getMeta from '@/utils/meta' +import { useActiveOverallTheme } from '@/shared/hooks/use-active-overall-theme' export default function FileTreeUploadDoc() { const { parentFolderId, cancel, droppedFiles, setDroppedFiles } = @@ -215,6 +216,8 @@ export default function FileTreeUploadDoc() { ) }) + const uppyTheme = useActiveOverallTheme('themed-modals') + useEffect(() => { if (uppy && droppedFiles) { uppy.setOptions({ @@ -312,6 +315,7 @@ export default function FileTreeUploadDoc() { proudlyDisplayPoweredByUppy={false} // allow files or folders to be selected fileManagerSelectionType="both" + theme={uppyTheme} locale={{ strings: { // Text to show on the droppable area. diff --git a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-file.tsx b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-file.tsx index 78c3e64986..9ddd5e5e53 100644 --- a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-file.tsx +++ b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-file.tsx @@ -10,9 +10,11 @@ import { OLModalHeader, OLModalTitle, } from '@/shared/components/ol/ol-modal' +import { useFeatureFlag } from '@/shared/context/split-test-context' export default function FileTreeModalCreateFile() { const { t } = useTranslation() + const themed = useFeatureFlag('themed-modals') const { isCreatingFile, cancel } = useFileTreeActionable() @@ -22,7 +24,7 @@ export default function FileTreeModalCreateFile() { return ( - + {t('add_files')} diff --git a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-folder.tsx b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-folder.tsx index 776c69b0df..d1c1696764 100644 --- a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-folder.tsx +++ b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-folder.tsx @@ -15,11 +15,13 @@ import OLButton from '@/shared/components/ol/ol-button' import OLFormControl from '@/shared/components/ol/ol-form-control' import OLFormLabel from '@/shared/components/ol/ol-form-label' import OLFormGroup from '@/shared/components/ol/ol-form-group' +import { useFeatureFlag } from '@/shared/context/split-test-context' function FileTreeModalCreateFolder() { const { t } = useTranslation() const [name, setName] = useState('') const [validName, setValidName] = useState(true) + const themed = useFeatureFlag('themed-modals') const { isCreatingFolder, inFlight, finishCreatingFolder, cancel, error } = useFileTreeActionable() @@ -51,7 +53,12 @@ function FileTreeModalCreateFolder() { } return ( - + {t('new_folder')} diff --git a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-delete.tsx b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-delete.tsx index 4239db7660..6f284d6545 100644 --- a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-delete.tsx +++ b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-delete.tsx @@ -11,6 +11,7 @@ import { import OLButton from '@/shared/components/ol/ol-button' import Notification from '@/shared/components/notification' import { useFileTreeSelectable } from '../../contexts/file-tree-selectable' +import { useFeatureFlag } from '@/shared/context/split-test-context' function FileTreeModalDelete() { const { t } = useTranslation() @@ -26,6 +27,8 @@ function FileTreeModalDelete() { const { select } = useFileTreeSelectable() + const themed = useFeatureFlag('themed-modals') + if (!isDeleting) return null // the modal will not be rendered; return early function handleHide() { @@ -38,7 +41,7 @@ function FileTreeModalDelete() { } return ( - + {t('delete')} diff --git a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-error.tsx b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-error.tsx index 1d4db8bcc7..3c29d6654f 100644 --- a/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-error.tsx +++ b/services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-error.tsx @@ -16,11 +16,13 @@ import { OLModalTitle, } from '@/shared/components/ol/ol-modal' import OLButton from '@/shared/components/ol/ol-button' +import { useFeatureFlag } from '@/shared/context/split-test-context' function FileTreeModalError() { const { t } = useTranslation() const { isRenaming, isMoving, cancel, error } = useFileTreeActionable() + const themed = useFeatureFlag('themed-modals') // the modal will not be rendered; return early if (!error) return null @@ -67,7 +69,7 @@ function FileTreeModalError() { } return ( - + {errorTitle()} diff --git a/services/web/frontend/stylesheets/pages/editor/file-tree.scss b/services/web/frontend/stylesheets/pages/editor/file-tree.scss index c50321931f..cc9ab65951 100644 --- a/services/web/frontend/stylesheets/pages/editor/file-tree.scss +++ b/services/web/frontend/stylesheets/pages/editor/file-tree.scss @@ -13,6 +13,10 @@ --file-tree-line-height: 2.05; --file-tree-icon-colour: var(--content-primary-dark); --file-tree-expand-button-color: var(--content-primary-dark); + + // Modals follow the page theme only when opted in, so this defaults to the + // light value and is overridden for .modal-themed below. + --modal-new-file-conflict-bg-color: var(--bg-danger-03); } @include theme('light') { @@ -595,9 +599,23 @@ } } +@include theme('default') { + .modal-themed { + --modal-new-file-conflict-bg-color: var(--red-70); + } +} + .modal-new-file { padding: 0; + .form-label { + color: var(--content-secondary-themed); + + &:has(+ .form-control[disabled], + .form-control-wrapper-disabled) { + color: var(--content-disabled-themed); + } + } + table { width: 100%; table-layout: fixed; @@ -626,14 +644,14 @@ } .modal-new-file-list { - background-color: var(--bg-light-secondary); + background-color: var(--bg-secondary-themed); width: 220px; ul { li { /* old modal (a) */ a { - color: var(--content-secondary); + color: var(--content-secondary-themed); padding: var(--spacing-03); display: block; text-decoration: none; @@ -641,7 +659,7 @@ /* new modal (button) */ .btn { - color: var(--content-secondary); + color: var(--content-secondary-themed); padding: var(--spacing-03); } @@ -652,27 +670,27 @@ .btn:focus { outline: none; text-decoration: none; - background-color: white; + background-color: var(--bg-primary-themed); } } li.active { - background-color: white; + background-color: var(--bg-primary-themed); /* old modal (a) */ a { - color: var(--link-ui); + color: var(--link-ui-themed); } /* new modal (button) */ .btn { - color: var(--link-ui); + color: var(--link-ui-themed); text-decoration: none; } } li:hover { - background-color: white; + background-color: var(--bg-primary-themed); } } } @@ -683,6 +701,10 @@ padding: 20px; } +.file-tree-modal-create-folder .form-label { + color: var(--content-secondary-themed); +} + .file-tree-modal-alert { margin-top: var(--spacing-06); } @@ -714,11 +736,11 @@ } .modal-new-file-body-conflict { - background-color: var(--bg-danger-03); - border: 1px dashed var(--border-danger); + background-color: var(--modal-new-file-conflict-bg-color); + border: 1px dashed var(--border-danger-themed); min-height: 400px; border-radius: 3px; - color: var(--content-primary); + color: var(--content-primary-themed); display: flex; flex-direction: column; justify-content: center;