From aec360498c26cc369531ed92d180a9f6ba4dcbd2 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Wed, 28 Jul 2021 20:20:14 +0200 Subject: [PATCH] (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 --- app/client/ui/DocTour.ts | 43 ++++++++++++++++++++++++++++++++++-- app/client/ui/ExampleCard.ts | 6 ++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/app/client/ui/DocTour.ts b/app/client/ui/DocTour.ts index 683564a6..3ee5aaf5 100644 --- a/app/client/ui/DocTour.ts +++ b/app/client/ui/DocTour.ts @@ -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 { 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 { 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 { } 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 + })); +} diff --git a/app/client/ui/ExampleCard.ts b/app/client/ui/ExampleCard.ts index 9be8183a..3519ca3a 100644 --- a/app/client/ui/ExampleCard.ts +++ b/app/client/ui/ExampleCard.ts @@ -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; `);