mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-18 00:57:55 +00:00
fix(history-mode): lay the history iframe in the editor's flex slot (#7903)
In-pad history mode positioned the timeslider iframe (#history-frame-mount)
as an absolute, inset:0 overlay over #editorcontainerbox. That filled the
editor area but took the iframe out of flow, so any in-flow side panel in
#editorcontainerbox — e.g. ep_webrtc's video column (#rtcbox) — ended up
hidden beneath the overlay. In live mode the same panel sits beside the
editor, so the two modes disagreed (ether/ep_webrtc rendered nothing in the
timeslider).
Make the iframe occupy the same in-flow flex slot the live editor uses
(flex: 1 1 auto in the row-flex #editorcontainerbox) so side panels lay out
identically in both modes.
This surfaced a latent bug: `body.history-mode #editorcontainer { display:
none }` (specificity 0-1-1) was being outranked by the two-id layout rule
`#editorcontainerbox #editorcontainer { display: flex }` (0-2-0), so the live
editor was never actually removed from flow — the absolute overlay merely
painted over it. With the iframe now in normal flow that no longer hides the
editor, so the hide rule is given matching specificity
(body.history-mode #editorcontainerbox #editorcontainer) to win the cascade.
Added a padmode.spec.ts regression test that injects a left-pinned in-flow
side panel, enters history mode, and asserts the live editor is display:none,
the iframe is not absolutely positioned, and the iframe lays out beside the
panel filling the editor's remaining width (no overlap).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
189ea4a29b
commit
e1476cf100
2 changed files with 71 additions and 8 deletions
|
|
@ -139,16 +139,22 @@ input {
|
|||
.history-banner-date { opacity: 0.85; }
|
||||
.history-banner #history-banner-return { margin-left: auto; }
|
||||
|
||||
/* The history iframe takes the place of the live ACE editor. We use the */
|
||||
/* same positioning model (absolute, fill the editor area) so the page */
|
||||
/* layout is identical between modes. */
|
||||
/* The history iframe takes the place of the live ACE editor by occupying the
|
||||
* same in-flow flex slot that #editorcontainer fills in live mode (flex: 1 1
|
||||
* auto inside the row-flex #editorcontainerbox). This keeps the layout
|
||||
* identical between modes — crucially including any in-flow side panels in
|
||||
* #editorcontainerbox, such as ep_webrtc's video column, which now sit beside
|
||||
* the history iframe exactly as they sit beside the live editor.
|
||||
*
|
||||
* This used to be an absolute, inset:0 overlay: it filled the editor area but
|
||||
* took the iframe out of flow, so an in-flow side panel ended up hidden
|
||||
* beneath it (see ether/ep_webrtc rendering nothing in the timeslider). */
|
||||
.history-frame-mount {
|
||||
display: none;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0; /* allow the iframe to shrink to make room for a side panel */
|
||||
border: 0;
|
||||
background: var(--bg-color, #f2f3f4);
|
||||
z-index: 4;
|
||||
}
|
||||
.history-frame-mount[hidden] { display: none; }
|
||||
.history-frame-mount > iframe {
|
||||
|
|
@ -164,8 +170,14 @@ input {
|
|||
/* editor, so we swap it for the history controls (slider + play/step */
|
||||
/* buttons) that drive the iframe. The right-side menu (Settings / Share / */
|
||||
/* Users / Chat / Home) stays fully interactive across modes. */
|
||||
body.history-mode #editorcontainer { display: none; }
|
||||
body.history-mode #editorcontainerbox { position: relative; }
|
||||
/* The two-id `#editorcontainerbox #editorcontainer { display: flex }` layout
|
||||
* rule (specificity 0-2-0) outranks a plain `body.history-mode #editorcontainer`
|
||||
* (0-1-1), so match the same container path here to actually win the cascade
|
||||
* and remove the live editor from flow. This was latent before: the old
|
||||
* absolute, inset:0 iframe overlay painted over the still-in-flow editor, so
|
||||
* nobody noticed it wasn't hidden. Now that the iframe sits in the normal
|
||||
* flex flow, the editor must genuinely be gone or the two would split the row. */
|
||||
body.history-mode #editorcontainerbox #editorcontainer { display: none; }
|
||||
body.history-mode .history-frame-mount { display: block; }
|
||||
body.history-mode #editbar .menu_left { display: none; }
|
||||
body.history-mode #editbar .show-more-icon-btn { display: none; }
|
||||
|
|
|
|||
|
|
@ -345,4 +345,55 @@ test.describe('in-pad history mode', () => {
|
|||
await expect(page.locator('body.history-mode')).toHaveCount(0);
|
||||
await expect(tbl.locator('.history-authors-row')).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('history iframe occupies the editor flex slot so side panels sit beside it', async ({page}) => {
|
||||
await goToNewPad(page);
|
||||
await clearPadContent(page);
|
||||
await writeToPad(page, 'one');
|
||||
await page.waitForTimeout(300);
|
||||
await writeToPad(page, ' two');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Simulate a plugin side panel mounted in #editorcontainerbox (e.g.
|
||||
// ep_webrtc's #rtcbox video column): an in-flow flex item pinned left.
|
||||
await page.evaluate(() => {
|
||||
const box = document.getElementById('editorcontainerbox')!;
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'test-side-panel';
|
||||
panel.style.cssText = 'display:flex;order:-1;flex:0 1 auto;width:120px';
|
||||
panel.innerHTML = '<div style="width:120px;height:120px"></div>';
|
||||
box.appendChild(panel);
|
||||
});
|
||||
const editorRight = await page.locator('#editorcontainer').evaluate(
|
||||
(el) => Math.round(el.getBoundingClientRect().right));
|
||||
|
||||
await page.locator('.buttonicon-history').click();
|
||||
await expect(page.locator('body.history-mode')).toBeVisible();
|
||||
await page.locator('#history-frame-mount iframe').waitFor({state: 'attached'});
|
||||
|
||||
// 1) The live editor is genuinely removed from flow — not merely painted
|
||||
// over. The two-id `#editorcontainerbox #editorcontainer { display:flex }`
|
||||
// layout rule outranks a plain `body.history-mode #editorcontainer`, so
|
||||
// the hide rule must match the same container path to win the cascade.
|
||||
expect(await page.locator('#editorcontainer').evaluate(
|
||||
(el) => getComputedStyle(el).display)).toBe('none');
|
||||
|
||||
// 2) The history iframe is in normal flow (not an absolute overlay)...
|
||||
expect(await page.locator('#history-frame-mount').evaluate(
|
||||
(el) => getComputedStyle(el).position)).not.toBe('absolute');
|
||||
|
||||
// 3) ...so it lays out beside the side panel instead of on top of it, and
|
||||
// still fills the rest of the editor's old width.
|
||||
const r = (sel: string) => page.locator(sel).evaluate((el) => {
|
||||
const b = el.getBoundingClientRect();
|
||||
return {left: b.left, right: b.right, top: b.top, bottom: b.bottom};
|
||||
});
|
||||
const panel = await r('#test-side-panel');
|
||||
const frame = await r('#history-frame-mount');
|
||||
const overlaps = !(frame.right <= panel.left || panel.right <= frame.left ||
|
||||
frame.bottom <= panel.top || panel.bottom <= frame.top);
|
||||
expect(overlaps).toBe(false);
|
||||
expect(Math.round(frame.left)).toBeGreaterThanOrEqual(Math.round(panel.right) - 2);
|
||||
expect(Math.round(frame.right)).toBeGreaterThanOrEqual(editorRight - 3);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue