diff --git a/apps/readest-app/src/__tests__/components/settings/AnnotationToolbarCustomizerEink.test.tsx b/apps/readest-app/src/__tests__/components/settings/AnnotationToolbarCustomizerEink.test.tsx new file mode 100644 index 00000000..9c8a5eaf --- /dev/null +++ b/apps/readest-app/src/__tests__/components/settings/AnnotationToolbarCustomizerEink.test.tsx @@ -0,0 +1,58 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { render, cleanup } from '@testing-library/react'; + +/** + * Regression guard for issue #4839 (display error under e-ink mode). + * + * The Customize Toolbar sub-page renders a content-width *preview* of the live + * selection popup. The preview surface uses `bg-gray-600 text-white` to mirror + * the real popup — but unlike the real popup (which earns its e-ink treatment + * from `.popup-container` in globals.css), the preview Zone is a plain div. With + * no e-ink override, the dark fill survives under `[data-eink='true']` and the + * whole row paints as an unreadable solid black bar. + * + * Invariant: the toolbar preview must carry `eink-bordered`, so e-ink swaps the + * dark fill for a `base-100` surface with a 1px `base-content` border — matching + * how the annotation toolbar renders in the reader. + */ + +vi.mock('@/context/EnvContext', () => ({ + useEnv: () => ({ envConfig: {}, appService: {} }), +})); + +vi.mock('@/hooks/useTranslation', () => ({ + useTranslation: () => (s: string) => s, +})); + +vi.mock('@/store/readerStore', () => ({ + useReaderStore: () => ({ getViewSettings: () => undefined }), +})); + +vi.mock('@/store/settingsStore', () => ({ + useSettingsStore: () => ({ + settings: { globalViewSettings: { annotationToolbarItems: undefined } }, + }), +})); + +vi.mock('@/helpers/settings', () => ({ + saveViewSettings: vi.fn(), +})); + +vi.mock('@/utils/share', () => ({ + canShareText: () => true, +})); + +import AnnotationToolbarCustomizer from '@/components/settings/AnnotationToolbarCustomizer'; + +afterEach(() => { + cleanup(); +}); + +describe('AnnotationToolbarCustomizer e-ink toolbar preview', () => { + it('marks the toolbar preview eink-bordered so e-ink swaps the dark fill for a bordered base-100 surface', () => { + const { container } = render( {}} />); + const toolbarPreview = container.querySelector('.selection-popup') as HTMLElement; + expect(toolbarPreview).not.toBeNull(); + expect(toolbarPreview.classList.contains('eink-bordered')).toBe(true); + }); +}); diff --git a/apps/readest-app/src/components/settings/AnnotationToolbarCustomizer.tsx b/apps/readest-app/src/components/settings/AnnotationToolbarCustomizer.tsx index 812c6c5f..ca32ab49 100644 --- a/apps/readest-app/src/components/settings/AnnotationToolbarCustomizer.tsx +++ b/apps/readest-app/src/components/settings/AnnotationToolbarCustomizer.tsx @@ -113,14 +113,23 @@ const Zone: React.FC<{ 'flex min-h-12 flex-wrap items-center gap-2 rounded-lg p-2', isToolbar ? // A faithful, content-width preview of the real popup, start-aligned - // with the Available row below it. - 'selection-popup bg-gray-600 text-white w-fit max-w-full' + // with the Available row below it. Off e-ink it mirrors the popup's + // dark fill; in e-ink the dark fill is dropped entirely and + // `eink-bordered` renders it as the popup's e-ink chrome instead + // (.popup-container): a base-100 surface with a 1px base-content + // border, so it doesn't paint as a solid black bar (#4839). + 'selection-popup eink-bordered w-fit max-w-full not-eink:bg-gray-600 not-eink:text-white' : 'bg-base-200/60', )} > {items.length === 0 ? ( {emptyHint}