Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/static/js/pad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ const getParameters = [
$('#editbar').css('display', 'flex');
},
},
{
// showMenuRight accepts 'true' or 'false'. Explicit 'false' hides the
// right-side toolbar (import/export/timeslider/settings/share/users);
// explicit 'true' forces it visible, overriding the readonly
// auto-hide applied further down (issue #5182). Any other value is
// a no-op — the menu stays in its default state.
name: 'showMenuRight',
checkVal: null,
callback: (val) => {
if (val === 'false') {
$('#editbar .menu_right').hide();
} else if (val === 'true') {
$('#editbar .menu_right').show();
}
},
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
},
{
name: 'showChat',
checkVal: null,
Expand Down Expand Up @@ -678,6 +694,14 @@ const pad = {
$('#chaticon').hide();
$('#options-chatandusers').parent().hide();
$('#options-stickychat').parent().hide();
// Hide the right-side toolbar on readonly pads — import/export,
// timeslider, settings, share, users are all noise for viewers
// who can't interact with the pad. Callers who need those
// controls visible on a readonly pad can force them back via
// `?showMenuRight=true`, which runs in getParameters() above.
if (getUrlVars().get('showMenuRight') !== 'true') {
$('#editbar .menu_right').hide();
}
} else if (!settings.hideChat) { $('#chaticon').show(); }

$('body').addClass(window.clientVars.readonly ? 'readonly' : 'readwrite');
Expand Down
60 changes: 60 additions & 0 deletions src/tests/frontend-new/specs/hide_menu_right.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {expect, Page, test} from "@playwright/test";
import {appendQueryParams, goToNewPad} from "../helper/padHelper";

test.beforeEach(async ({page}) => {
// clearCookies on the page's own context — creating a separate
// BrowserContext and clearing cookies on it is a no-op for the page
// fixture (Qodo review feedback on #7553).
await page.context().clearCookies();
await goToNewPad(page);
});

test.describe('showMenuRight URL parameter', function () {
test('without the parameter, .menu_right is visible', async function ({page}) {
await expect(page.locator('#editbar .menu_right')).toBeVisible();
});

test('showMenuRight=false hides .menu_right', async function ({page}) {
await appendQueryParams(page, {showMenuRight: 'false'});
await expect(page.locator('#editbar .menu_right')).toBeHidden();
// The left menu stays visible so the pad remains navigable.
await expect(page.locator('#editbar .menu_left')).toBeVisible();
});

test('showMenuRight=true keeps .menu_right visible', async function ({page}) {
await appendQueryParams(page, {showMenuRight: 'true'});
await expect(page.locator('#editbar .menu_right')).toBeVisible();
});

// Helper: open the Share popup, flip it to read-only, read the r.* URL
// back out of #linkinput. The readonly toggle is a checkbox
// (`#readonlyinput`) that rewrites #linkinput's value live. The popup
// animates open, so the checkbox is briefly "not stable" — wait for
// the popup's show class before interacting, and use force:true so a
// trailing transform doesn't trip Playwright's stability check.
const getReadonlyUrl = async (page: Page) => {
await page.locator('.buttonicon-embed').click();
await page.locator('#embed.popup-show').waitFor({state: 'visible'});
await page.locator('#readonlyinput').check({force: true});
await page.waitForFunction(
() => (document.querySelector('#linkinput') as HTMLInputElement | null)
?.value.includes('/p/r.'));
const url = await page.locator('#linkinput').inputValue();
expect(url).toMatch(/\/p\/r\./);
return url;
};

test('readonly pad hides .menu_right by default', async function ({page}) {
const readonlyUrl = await getReadonlyUrl(page);
await page.goto(readonlyUrl);
await page.waitForSelector('#editorcontainer.initialized');
await expect(page.locator('#editbar .menu_right')).toBeHidden();
});

test('readonly pad with showMenuRight=true keeps the menu visible', async function ({page}) {
const readonlyUrl = await getReadonlyUrl(page);
await page.goto(`${readonlyUrl}?showMenuRight=true`);
await page.waitForSelector('#editorcontainer.initialized');
await expect(page.locator('#editbar .menu_right')).toBeVisible();
});
});
Loading