(core) Allow creating custom document tours with a special table

Summary:
Like the welcome tour, a special URL hash triggers startDocTour which uses data from a table GristDocTour to construct the appropriate popups.

This is the basic version described in https://grist.quip.com/sN2RAHI2dchm/Document-tours

Test Plan:
Added a new nbrowser test which tests the data produced by makeDocTour. The general behaviour of the UI and popups has hardly changed so existing tests cover that well enough.

The new test uses a new fixture document which you can open to easily experience the tour.

Error cases where there's no valid document tour are not tested because that behaviour is likely to change significantly and this feature is still quite 'private'.

Reviewers: georgegevoian

Reviewed By: georgegevoian

Subscribers: jarek, dsagal

Differential Revision: https://phab.getgrist.com/D2938
This commit is contained in:
Alex Hall
2021-07-23 18:24:17 +02:00
parent 04e5d90f86
commit 15f1ef96fa
6 changed files with 145 additions and 40 deletions

View File

@@ -27,6 +27,7 @@ import {UrlState} from 'app/client/lib/UrlState';
import {decodeUrl, encodeUrl, getSlugIfNeeded, GristLoadConfig, IGristUrlState, useNewUI} from 'app/common/gristUrls';
import {Document} from 'app/common/UserAPI';
import isEmpty = require('lodash/isEmpty');
import {CellValue} from "app/plugin/GristData";
/**
* Returns a singleton UrlState object, initializing it on first use.
@@ -164,3 +165,49 @@ export class UrlStateImpl {
}
}
}
/**
* Given value like `foo bar baz`, constructs URL by checking if `baz` is a valid URL and,
* if not, prepending `http://`.
*/
export function constructUrl(value: CellValue): string {
if (typeof value !== 'string') {
return '';
}
const url = value.slice(value.lastIndexOf(' ') + 1);
try {
// Try to construct a valid URL
return (new URL(url)).toString();
} catch (e) {
// Not a valid URL, so try to prefix it with http
return 'http://' + url;
}
}
/**
* If urlValue contains a URL to the current document that can be navigated to without a page reload,
* returns a parsed IGristUrlState that can be passed to urlState().pushState() to do that navigation.
* Otherwise, returns null.
*/
export function sameDocumentUrlState(urlValue: CellValue): IGristUrlState | null {
const urlString = constructUrl(urlValue);
let url: URL;
try {
url = new URL(urlString);
} catch {
return null;
}
const oldOrigin = window.location.origin;
const newOrigin = url.origin;
if (oldOrigin !== newOrigin) {
return null;
}
const urlStateImpl = new UrlStateImpl(window as any);
const result = urlStateImpl.decodeUrl(url);
if (urlStateImpl.needPageLoad(urlState().state.get(), result)) {
return null;
} else {
return result;
}
}