(core) Allow adding a single link at the bottom of a document tour card

Summary:
Checks for new special columns in GristDocTour: Link_Text, Link_URL, and Link_Icon.

No link is generated if Link_Text is blank or Link_URL cannot be parsed as a URL.

No icon is shown if Link_Icon is not the name of an icon in IconList.ts

Test Plan: Expanded tests, but they now assert things about HTML which may be brittle

Reviewers: georgegevoian, dsagal

Reviewed By: georgegevoian, dsagal

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D2947
This commit is contained in:
Alex Hall 2021-07-28 20:20:14 +02:00
parent 37eed2d3c2
commit aec360498c
2 changed files with 44 additions and 5 deletions

View File

@ -4,11 +4,14 @@ import * as _ from "lodash";
import {Placement} from "@popperjs/core";
import {placements} from "@popperjs/core/lib/enums";
import {sameDocumentUrlState} from "../models/gristUrlState";
import {dom} from "grainjs";
import {IconList, IconName} from "../ui2018/IconList";
import {cssButtons, cssLinkBtn, cssLinkIcon} from "./ExampleCard";
export async function startDocTour(docData: DocData, onFinishCB: () => void) {
const docTour: IOnBoardingMsg[] = await makeDocTour(docData) || invalidDocTour;
(window as any)._gristDocTour = docTour; // for easy testing
exposeDocTour(docTour);
startOnBoarding(docTour, onFinishCB);
}
@ -32,7 +35,10 @@ async function makeDocTour(docData: DocData): Promise<IOnBoardingMsg[] | null> {
return String(tableData.getValue(rowId, colId) || "");
}
const title = getValue("Title");
const body = getValue("Body");
let body: HTMLElement | string = getValue("Body");
const linkText = getValue("Link_Text");
const linkUrl = getValue("Link_URL");
const linkIcon = getValue("Link_Icon") as IconName;
const locationValue = getValue("Location");
let placement = getValue("Placement");
@ -45,6 +51,27 @@ async function makeDocTour(docData: DocData): Promise<IOnBoardingMsg[] | null> {
placement = "auto";
}
let validLinkUrl = true;
try {
new URL(linkUrl);
} catch {
validLinkUrl = false;
}
if (validLinkUrl && linkText) {
body = dom(
'div',
dom('p', body),
dom('p',
cssButtons(cssLinkBtn(
IconList.includes(linkIcon) ? cssLinkIcon(linkIcon) : null,
linkText,
{href: linkUrl, target: '_blank'},
))
),
);
}
return {
title,
body,
@ -60,3 +87,15 @@ async function makeDocTour(docData: DocData): Promise<IOnBoardingMsg[] | null> {
}
return result;
}
// for easy testing
function exposeDocTour(docTour: IOnBoardingMsg[]) {
(window as any)._gristDocTour = () =>
docTour.map(msg => ({
...msg,
body: typeof msg.body === "string" ? msg.body
: (msg.body as HTMLElement)?.outerHTML
.replace(/_grain\d+_/g, "_grainXXX_"),
urlState: msg.urlState?.hash
}));
}

View File

@ -143,17 +143,17 @@ const cssInfo = styled('div', `
line-height: 1.6;
`);
const cssButtons = styled('div', `
export const cssButtons = styled('div', `
display: flex;
`);
const cssLinkBtn = styled(cssLink, `
export const cssLinkBtn = styled(cssLink, `
&:not(:last-child) {
margin-right: 32px;
}
`);
const cssLinkIcon = styled(icon, `
export const cssLinkIcon = styled(icon, `
margin-right: 8px;
margin-top: -2px;
`);