(core) Fix browser history bug with tutorials

Summary:
History wasn't being replaced in some cases, which was
causing a bug where trying to leave a tutorial fork via the
browser's back button would navigate back to the trunk, and
trigger forking again. This effectively made it impossible to
leave a tutorial.

Also adds support for specifying custom CSS classes for
tutorial Markdown images.

Test Plan: Browser test.

Reviewers: JakubSerafin

Reviewed By: JakubSerafin

Differential Revision: https://phab.getgrist.com/D3866
This commit is contained in:
George Gevoian 2023-04-17 22:23:12 -04:00
parent 69fea132de
commit b15ae98349
4 changed files with 40 additions and 9 deletions

View File

@ -240,9 +240,9 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
public updateUrlNoReload( public updateUrlNoReload(
urlId: string, urlId: string,
urlOpenMode: OpenDocMode, urlOpenMode: OpenDocMode,
options: {removeSlug?: boolean, replaceUrl?: boolean} = {removeSlug: false, replaceUrl: true} options: {removeSlug?: boolean, replaceUrl?: boolean} = {}
) { ) {
const {removeSlug, replaceUrl} = options; const {removeSlug = false, replaceUrl = true} = options;
const state = urlState().state.get(); const state = urlState().state.get();
const nextState = { const nextState = {
...state, ...state,

View File

@ -69,6 +69,12 @@
cursor: pointer; cursor: pointer;
} }
.doc-tutorial-popup-thumbnail-half-screenshot {
margin-left: auto;
margin-right: auto;
width: 50%;
}
.doc-tutorial-popup-thumbnail img { .doc-tutorial-popup-thumbnail img {
width: 100%; width: 100%;
border: 1px solid var(--grist-theme-tutorials-popup-border, #D9D9D9); border: 1px solid var(--grist-theme-tutorials-popup-border, #D9D9D9);

View File

@ -2,15 +2,21 @@ import {marked} from 'marked';
export const renderer = new marked.Renderer(); export const renderer = new marked.Renderer();
renderer.image = (href: string, text: string) => { renderer.image = (href: string | null, title: string | null, _text: string) => {
return `<div class="doc-tutorial-popup-thumbnail"> let classes = 'doc-tutorial-popup-thumbnail';
<img src="${href}" title="${text ?? ''}" /> const hash = href?.split('#')?.[1];
if (hash) {
const extraClass = `doc-tutorial-popup-thumbnail-${hash}`;
classes += ` ${extraClass}`;
}
return `<div class="${classes}">
<img src="${href}" title="${title ?? ''}" />
<div class="doc-tutorial-popup-thumbnail-icon-wrapper"> <div class="doc-tutorial-popup-thumbnail-icon-wrapper">
<div class="doc-tutorial-popup-thumbnail-icon"></div> <div class="doc-tutorial-popup-thumbnail-icon"></div>
</div> </div>
</div>`; </div>`;
}; };
renderer.link = (href: string, _title: string, text: string) => { renderer.link = (href: string | null, _title: string | null, text: string) => {
return `<a href="${href}" target="_blank">${text}</a>`; return `<a href="${href}" target="_blank">${text}</a>`;
}; };

View File

@ -115,6 +115,25 @@ describe('DocTutorial', function () {
} }
}); });
it('does not break navigation via browser history', async function() {
// Navigating via browser history was partially broken at one point; if you
// started a tutorial, and wanted to leave via the browser's back button, you
// couldn't.
for (const page of ['code', 'data', 'acl']) {
await driver.navigate().back();
const currentUrl = await driver.getCurrentUrl();
assert.match(currentUrl, new RegExp(`/p/${page}$`));
}
await driver.navigate().back();
await driver.navigate().back();
await driver.findWait('.test-dm-doclist', 2000);
await driver.navigate().forward();
await gu.waitForDocToLoad();
assert.isTrue(await driver.findWait('.test-doc-tutorial-popup', 2000).isDisplayed());
});
it('does not show the GristDocTutorial page or table', async function() { it('does not show the GristDocTutorial page or table', async function() {
assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2']); assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2']);
await driver.find('.test-tools-raw').click(); await driver.find('.test-tools-raw').click();
@ -290,7 +309,7 @@ describe('DocTutorial', function () {
await api.updateDoc(doc.id, {name: 'DocTutorial V2'}); await api.updateDoc(doc.id, {name: 'DocTutorial V2'});
await api.applyUserActions(doc.id, [['AddTable', 'NewTable', [{id: 'A'}]]]); await api.applyUserActions(doc.id, [['AddTable', 'NewTable', [{id: 'A'}]]]);
// Load the current fork of the tutorial. // Load the fork of the tutorial.
await driver.navigate().to(forkUrl); await driver.navigate().to(forkUrl);
await gu.waitForDocToLoad(); await gu.waitForDocToLoad();
await driver.findWait('.test-doc-tutorial-popup', 2000); await driver.findWait('.test-doc-tutorial-popup', 2000);
@ -299,7 +318,7 @@ describe('DocTutorial', function () {
assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2']); assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2']);
assert.deepEqual(await gu.getVisibleGridCells({cols: [0], rowNums: [1]}), ['Redacted']); assert.deepEqual(await gu.getVisibleGridCells({cols: [0], rowNums: [1]}), ['Redacted']);
// Restart the tutorial and wait for a new fork to be created. // Restart the tutorial.
await driver.find('.test-doc-tutorial-popup-restart').click(); await driver.find('.test-doc-tutorial-popup-restart').click();
await driver.find('.test-modal-confirm').click(); await driver.find('.test-modal-confirm').click();
await gu.waitForServer(); await gu.waitForServer();
@ -319,7 +338,7 @@ describe('DocTutorial', function () {
// Check that edits were reset. // Check that edits were reset.
assert.deepEqual(await gu.getVisibleGridCells({cols: [0], rowNums: [1]}), ['Zane Rails']); assert.deepEqual(await gu.getVisibleGridCells({cols: [0], rowNums: [1]}), ['Zane Rails']);
// Check that changes made to the tutorial since the last fork are included. // Check that changes made to the tutorial since it was last started are included.
assert.equal(await driver.find('.test-doc-tutorial-popup-header').getText(), assert.equal(await driver.find('.test-doc-tutorial-popup-header').getText(),
'DocTutorial V2'); 'DocTutorial V2');
assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2', 'NewTable']); assert.deepEqual(await gu.getPageNames(), ['Page 1', 'Page 2', 'NewTable']);