Merge pull request #36575 from overleaf/mj-generic-modals-themes

[web] Add dark mode to ide modals

GitOrigin-RevId: cd8e95e2286df46510a377ff9a69c4ab646ceabd
This commit is contained in:
Mathias Jakobsen
2026-09-17 11:59:00 +00:00
committed by Copybot
parent 8139638d9d
commit 1f9873e391
9 changed files with 47 additions and 18 deletions
@@ -7,6 +7,7 @@ import {
OLModalHeader,
OLModalTitle,
} from '@/shared/components/ol/ol-modal'
import { useFeatureFlag } from '@/shared/context/split-test-context'
// show modal when editor is forcefully disconnected
function ForceDisconnected() {
@@ -14,6 +15,7 @@ function ForceDisconnected() {
const { t } = useTranslation()
const [secondsUntilRefresh, setSecondsUntilRefresh] = useState(0)
const [show, setShow] = useState(false)
const themed = useFeatureFlag('themed-modals')
useEffect(() => {
if (
@@ -55,6 +57,7 @@ function ForceDisconnected() {
className="lock-editor-modal"
backdrop={false}
keyboard={false}
themed={themed}
>
<OLModalHeader closeButton={false}>
<OLModalTitle>{t('please_wait')}</OLModalTitle>
@@ -9,6 +9,7 @@ import {
} from '@/shared/components/ol/ol-modal'
import OLButton from '@/shared/components/ol/ol-button'
import { ButtonProps } from '@/shared/components/types/button-props'
import { useFeatureFlag } from '@/shared/context/split-test-context'
export type GenericConfirmModalOwnProps = {
title: string
@@ -30,9 +31,10 @@ function GenericConfirmModal({
...modalProps
}: GenericConfirmModalProps) {
const { t } = useTranslation()
const themed = useFeatureFlag('themed-modals')
return (
<OLModal {...modalProps}>
<OLModal {...modalProps} themed={themed}>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>
@@ -8,6 +8,7 @@ import {
OLModalTitle,
} from '@/shared/components/ol/ol-modal'
import OLButton from '@/shared/components/ol/ol-button'
import { useFeatureFlag } from '@/shared/context/split-test-context'
export type GenericMessageModalOwnProps = {
title: string
@@ -23,9 +24,10 @@ function GenericMessageModal({
...modalProps
}: GenericMessageModalProps) {
const { t } = useTranslation()
const themed = useFeatureFlag('themed-modals')
return (
<OLModal {...modalProps}>
<OLModal {...modalProps} themed={themed}>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>
@@ -10,6 +10,7 @@ import {
OLModalHeader,
OLModalTitle,
} from '@/shared/components/ol/ol-modal'
import { useFeatureFlag } from '@/shared/context/split-test-context'
export type OutOfSyncModalProps = {
editorContent: string
@@ -21,6 +22,7 @@ function OutOfSyncModal({ editorContent, show, onHide }: OutOfSyncModalProps) {
const { t } = useTranslation()
const location = useLocation()
const [editorContentShown, setEditorContentShown] = useState(false)
const themed = useFeatureFlag('themed-modals')
const editorContentRows = (editorContent.match(/\n/g)?.length || 0) + 1
useEffect(() => {
@@ -43,6 +45,7 @@ function OutOfSyncModal({ editorContent, show, onHide }: OutOfSyncModalProps) {
className="out-of-sync-modal"
backdrop={false}
keyboard={false}
themed={themed}
>
<OLModalHeader>
<OLModalTitle>{t('out_of_sync')}</OLModalTitle>
@@ -8,6 +8,7 @@ import {
} from '@/shared/components/ol/ol-modal'
import OLButton from '@/shared/components/ol/ol-button'
import { useEffect, useState } from 'react'
import { useFeatureFlag } from '@/shared/context/split-test-context'
function ProjectConvertedFromDocumentModal() {
const [convertedFrom, setConvertedFrom] = useState<string | null>(null)
@@ -46,6 +47,7 @@ function ProjectConvertedFromImportModalContent({
onHide: () => void
}) {
const { t } = useTranslation()
const themed = useFeatureFlag('themed-modals')
return (
<OLModal
@@ -54,6 +56,7 @@ function ProjectConvertedFromImportModalContent({
onHide={onHide}
id="converted-from-document-modal"
backdrop="static"
themed={themed}
>
<OLModalHeader>
<OLModalTitle as="h3">{t('document_ready_for_editing')}</OLModalTitle>
@@ -8,6 +8,10 @@ $editor-toggler-bg-dark-color: color.adjust(
:root {
--editor-toggler-bg-color: #{$editor-toggler-bg-dark-color};
--editor-resizer-bg-color: var(--bg-dark-secondary);
// Deliberately more transparent than $modal-backdrop-opacity so the project
// stays readable and selectable behind the modal
--editor-lock-modal-backdrop-color: #{rgba($modal-backdrop-bg, 0.3)};
}
@include theme('light') {
@@ -19,6 +23,12 @@ $editor-toggler-bg-dark-color: color.adjust(
}
}
@include theme('default') {
.modal-themed {
--editor-lock-modal-backdrop-color: #{rgba($neutral-70, 0.3)};
}
}
#ide-root {
@include full-height-stacked-page;
@@ -153,7 +163,7 @@ $editor-toggler-bg-dark-color: color.adjust(
.modal.lock-editor-modal {
display: flex !important;
background-color: rgba($bg-dark-primary, 0.3);
background-color: var(--editor-lock-modal-backdrop-color);
overflow-y: hidden;
pointer-events: none;
@@ -163,6 +173,8 @@ $editor-toggler-bg-dark-color: color.adjust(
}
.out-of-sync-modal {
@include themed-links;
.text-preview {
margin-top: var(--spacing-05);
@@ -171,7 +183,8 @@ $editor-toggler-bg-dark-color: color.adjust(
max-height: 360px;
width: 100%;
background-color: var(--bg-light-primary);
color: var(--content-primary-themed);
background-color: var(--bg-primary-themed);
overflow: auto;
border: 1px solid var(--border-primary-dark);
padding: var(--spacing-04) var(--spacing-05);
@@ -28,13 +28,13 @@ describe('<MessageGroup />', function () {
return (
<UserProvider>
<ModalsContextProvider>
<SplitTestProvider>
<SplitTestProvider>
<ModalsContextProvider>
<ChatContext.Provider value={mockContextValue as any}>
{children}
</ChatContext.Provider>
</SplitTestProvider>
</ModalsContextProvider>
</ModalsContextProvider>
</SplitTestProvider>
</UserProvider>
)
}
@@ -18,13 +18,13 @@ describe('<MessageList />', function () {
return (
<UserProvider>
<ModalsContextProvider>
<SplitTestProvider>
<SplitTestProvider>
<ModalsContextProvider>
<ChatContext.Provider value={mockContextValue as any}>
{children}
</ChatContext.Provider>
</SplitTestProvider>
</ModalsContextProvider>
</ModalsContextProvider>
</SplitTestProvider>
</UserProvider>
)
}
@@ -4,6 +4,7 @@ import { fireEvent, render, screen } from '@testing-library/react'
import OutOfSyncModal from '@/features/ide-react/components/modals/out-of-sync-modal'
import * as eventTracking from '@/infrastructure/event-tracking'
import { location } from '@/shared/components/location'
import { SplitTestProvider } from '@/shared/context/split-test-context'
describe('<OutOfSyncModal />', function () {
let sendMBSpy: sinon.SinonSpy
@@ -12,12 +13,14 @@ describe('<OutOfSyncModal />', function () {
const renderModal = (props: { show?: boolean } = {}) =>
render(
<OutOfSyncModal
editorContent="offline content"
show
onHide={onHide}
{...props}
/>
<SplitTestProvider>
<OutOfSyncModal
editorContent="offline content"
show
onHide={onHide}
{...props}
/>
</SplitTestProvider>
)
beforeEach(function () {