Skip to content

Commit 6b510fe

Browse files
SuaYooemma-sg
andauthored
fix: Sync user guide to correct workflow section (#2592)
Resolves #2560 ## Changes - Syncs workflow current form section with user guide section. - Stickies "User Guide" button to top of viewport so that user guide can be opened. - Makes content behind user guide clickable (fixes issues with stickied elements shifting when user guide is not contained to the parent element.) - Decreases size of user guide text when embedded in an iframe. - Refactors overflow scrim to reuse CSS variables. --------- Co-authored-by: Emma Segal-Grossman <[email protected]>
1 parent 652e8a6 commit 6b510fe

File tree

10 files changed

+138
-51
lines changed

10 files changed

+138
-51
lines changed

frontend/docs/docs/js/embed.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
if (window.self !== window.top) {
2+
// Within iframe--assume this is an iframe embedded in the Browsertrix app.
3+
const style = document.createElement("style");
4+
5+
// Decrease text size without decreasing element size and overall spacing
6+
style.innerText = `.md-typeset { font-size: 0.7rem; }`;
7+
8+
window.document.body.appendChild(style);
9+
}

frontend/docs/mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extra_css:
66
- stylesheets/extra.css
77
extra_javascript:
88
- js/insertversion.js
9+
- js/embed.js
910
theme:
1011
name: material
1112
custom_dir: docs/overrides

frontend/src/features/crawl-workflows/workflow-editor.ts

+23
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import type {
6464
ExclusionChangeEvent,
6565
QueueExclusionTable,
6666
} from "@/features/crawl-workflows/queue-exclusion-table";
67+
import type { UserGuideEventMap } from "@/index";
6768
import { infoCol, inputCol } from "@/layouts/columns";
6869
import { pageSectionsWithNav } from "@/layouts/pageSectionsWithNav";
6970
import { panel } from "@/layouts/panel";
@@ -105,7 +106,9 @@ import {
105106
getDefaultFormState,
106107
getInitialFormState,
107108
getServerDefaults,
109+
makeUserGuideEvent,
108110
SECTIONS,
111+
workflowTabToGuideHash,
109112
type FormState,
110113
type WorkflowDefaults,
111114
} from "@/utils/workflow";
@@ -420,6 +423,7 @@ export class WorkflowEditor extends BtrixElement {
420423
nav: this.renderNav(),
421424
main: this.renderFormSections(),
422425
sticky: true,
426+
stickyTopClassname: tw`lg:top-16`,
423427
})}
424428
${this.renderFooter()}
425429
</form>
@@ -485,6 +489,10 @@ export class WorkflowEditor extends BtrixElement {
485489
this.updateProgressState({
486490
activeTab: name,
487491
});
492+
493+
if (this.appState.userGuideOpen) {
494+
this.dispatchEvent(makeUserGuideEvent(name));
495+
}
488496
}
489497
490498
track(AnalyticsTrackEvent.ExpandWorkflowFormSection, {
@@ -2160,6 +2168,21 @@ https://archiveweb.page/images/${"logo.svg"}`}
21602168
await this.updateComplete;
21612169

21622170
void this.scrollToActivePanel();
2171+
2172+
if (this.appState.userGuideOpen) {
2173+
this.dispatchEvent(
2174+
new CustomEvent<UserGuideEventMap["btrix-user-guide-show"]["detail"]>(
2175+
"btrix-user-guide-show",
2176+
{
2177+
detail: {
2178+
path: `user-guide/workflow-setup/#${workflowTabToGuideHash[step]}`,
2179+
},
2180+
bubbles: true,
2181+
composed: true,
2182+
},
2183+
),
2184+
);
2185+
}
21632186
};
21642187

21652188
private onKeyDown(event: KeyboardEvent) {

frontend/src/index.ts

+21-4
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,9 @@ export class App extends BtrixElement {
339339
<div class="min-w-screen flex min-h-screen flex-col">
340340
${this.renderSuperadminBanner()} ${this.renderNavBar()}
341341
${this.renderAlertBanner()}
342-
<main class="relative flex flex-auto md:min-h-[calc(100vh-3.125rem)]">
342+
<main
343+
class="relative flex flex-auto transition-[padding] md:min-h-[calc(100vh-3.125rem)]"
344+
>
343345
${this.renderPage()}
344346
</main>
345347
<div class="border-t border-neutral-100">${this.renderFooter()}</div>
@@ -356,14 +358,27 @@ export class App extends BtrixElement {
356358
<sl-drawer
357359
id="userGuideDrawer"
358360
label=${msg("User Guide")}
359-
style="--body-spacing: 0; --footer-spacing: var(--sl-spacing-2x-small);"
361+
class="[--body-spacing:0] [--footer-spacing:var(--sl-spacing-2x-small)] [--size:31rem] part-[base]:fixed part-[base]:z-50 part-[panel]:[border-left:1px_solid_var(--sl-panel-border-color)]"
362+
?open=${this.appState.userGuideOpen}
363+
contained
364+
@sl-hide=${() => AppStateService.updateUserGuideOpen(false)}
365+
@sl-after-hide=${() => {
366+
// FIXME There might be a way to handle this in Mkdocs, but updating
367+
// only the hash doesn't seem to update the docs view
368+
const iframe = this.userGuideDrawer.querySelector("iframe");
369+
370+
if (!iframe) return;
371+
372+
const src = iframe.src;
373+
iframe.src = src.slice(0, src.indexOf("#"));
374+
}}
360375
>
361376
<span slot="label" class="flex items-center gap-3">
362377
<sl-icon name="book" class=""></sl-icon>
363378
<span>${msg("User Guide")}</span>
364379
</span>
365380
<iframe
366-
class="size-full transition-opacity duration-slow"
381+
class="size-full text-xs transition-opacity duration-slow"
367382
src="${this.docsUrl}user-guide/workflow-setup/"
368383
></iframe>
369384
<sl-button
@@ -952,7 +967,9 @@ export class App extends BtrixElement {
952967
iframe.src = this.fullDocsUrl;
953968
}
954969

955-
void this.userGuideDrawer.show();
970+
if (!this.appState.userGuideOpen) {
971+
AppStateService.updateUserGuideOpen(true);
972+
}
956973
} else {
957974
console.debug("user guide iframe not found");
958975
}

frontend/src/layouts/pageSectionsWithNav.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ export function pageSectionsWithNav({
88
main,
99
placement = "start",
1010
sticky = false,
11+
stickyTopClassname,
1112
}: {
1213
nav: TemplateResult;
1314
main: TemplateResult;
1415
placement?: "start" | "top";
1516
sticky?: boolean;
17+
stickyTopClassname?: string; // e.g. `lg:top-0`
1618
}) {
1719
return html`
1820
<div
@@ -24,7 +26,8 @@ export function pageSectionsWithNav({
2426
<div
2527
class=${clsx(
2628
tw`flex flex-1 flex-col gap-2`,
27-
sticky && tw`lg:sticky lg:top-2 lg:self-start`,
29+
sticky &&
30+
[tw`lg:sticky lg:self-start`, stickyTopClassname || tw`lg:top-2`],
2831
placement === "start" ? tw`lg:max-w-[16.5rem]` : tw`lg:flex-row`,
2932
)}
3033
part="tabs"

frontend/src/pages/org/workflow-detail.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,9 @@ export class WorkflowDetail extends BtrixElement {
592592
private readonly renderEditor = () => html`
593593
<div class="col-span-1">${this.renderBreadcrumbs()}</div>
594594
595-
<header class="col-span-1 mb-3 flex flex-wrap gap-2">
595+
<header
596+
class="scrim scrim-to-b z-10 col-span-1 mb-3 flex flex-wrap gap-2 before:-top-3 lg:sticky lg:top-3"
597+
>
596598
<btrix-detail-page-title .item=${this.workflow}></btrix-detail-page-title>
597599
</header>
598600

frontend/src/pages/org/workflows-new.ts

+13-45
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { localized, msg } from "@lit/localize";
2+
import clsx from "clsx";
23
import { mergeDeep } from "immutable";
34
import { customElement, property } from "lit/decorators.js";
45
import { ifDefined } from "lit/directives/if-defined.js";
@@ -7,33 +8,18 @@ import type { PartialDeep } from "type-fest";
78

89
import { ScopeType, type Seed, type WorkflowParams } from "./types";
910

10-
import type { UserGuideEventMap } from "@/index";
1111
import { pageNav, type Breadcrumb } from "@/layouts/pageHeader";
1212
import { WorkflowScopeType } from "@/types/workflow";
1313
import LiteElement, { html } from "@/utils/LiteElement";
14+
import { tw } from "@/utils/tailwind";
1415
import {
1516
DEFAULT_AUTOCLICK_SELECTOR,
1617
DEFAULT_SELECT_LINKS,
18+
makeUserGuideEvent,
19+
type SectionsEnum,
1720
type FormState as WorkflowFormState,
1821
} from "@/utils/workflow";
1922

20-
type GuideHash =
21-
| "scope"
22-
| "limits"
23-
| "browser-settings"
24-
| "scheduling"
25-
| "metadata"
26-
| "review-settings";
27-
28-
const workflowTabToGuideHash: Record<string, GuideHash> = {
29-
crawlSetup: "scope",
30-
crawlLimits: "limits",
31-
browserSettings: "browser-settings",
32-
crawlScheduling: "scheduling",
33-
crawlMetadata: "metadata",
34-
confirmSettings: "review-settings",
35-
};
36-
3723
/**
3824
* Usage:
3925
* ```ts
@@ -55,23 +41,6 @@ export class WorkflowsNew extends LiteElement {
5541
@property({ type: Object })
5642
initialWorkflow?: WorkflowParams;
5743

58-
private userGuideHashLink: GuideHash = "scope";
59-
60-
connectedCallback(): void {
61-
super.connectedCallback();
62-
63-
this.userGuideHashLink =
64-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
65-
workflowTabToGuideHash[window.location.hash.slice(1) as GuideHash] ||
66-
"scope";
67-
68-
window.addEventListener("hashchange", () => {
69-
const hashValue = window.location.hash.slice(1) as GuideHash;
70-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
71-
this.userGuideHashLink = workflowTabToGuideHash[hashValue] || "scope";
72-
});
73-
}
74-
7544
private get defaultNewWorkflow(): WorkflowParams {
7645
return {
7746
name: "",
@@ -125,21 +94,20 @@ export class WorkflowsNew extends LiteElement {
12594

12695
return html`
12796
<div class="mb-5">${this.renderBreadcrumbs()}</div>
128-
<header class="flex items-center justify-between">
97+
<header
98+
class="scrim scrim-to-b z-10 flex flex-wrap items-start justify-between gap-2 to-white before:-top-3 lg:sticky lg:top-3"
99+
>
129100
<h2 class="mb-6 text-xl font-semibold">${msg("New Crawl Workflow")}</h2>
130101
<sl-button
131102
size="small"
103+
class=${clsx(
104+
tw`transition-opacity`,
105+
this.appState.userGuideOpen && tw`pointer-events-none opacity-0`,
106+
)}
107+
?disabled=${this.appState.userGuideOpen}
132108
@click=${() => {
133109
this.dispatchEvent(
134-
new CustomEvent<
135-
UserGuideEventMap["btrix-user-guide-show"]["detail"]
136-
>("btrix-user-guide-show", {
137-
detail: {
138-
path: `user-guide/workflow-setup/#${this.userGuideHashLink}`,
139-
},
140-
bubbles: true,
141-
composed: true,
142-
}),
110+
makeUserGuideEvent(window.location.hash.slice(1) as SectionsEnum),
143111
);
144112
}}
145113
>

frontend/src/theme.stylesheet.css

+19
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@
9696

9797
/* Transition */
9898
--sl-transition-x-fast: 100ms;
99+
100+
/*
101+
*
102+
* Browsertrix theme tokens
103+
*
104+
*/
105+
/* Overflow scrim */
106+
--btrix-overflow-scroll-scrim-color: var(--sl-panel-background-color);
107+
--btrix-overflow-scrim-width: 3rem;
99108
}
100109

101110
body {
@@ -446,6 +455,16 @@
446455
sl-button.button-card::part(label) {
447456
@apply flex flex-1 flex-col justify-center gap-2 text-left;
448457
}
458+
459+
.scrim:before {
460+
@apply pointer-events-none absolute -z-10;
461+
}
462+
463+
.scrim-to-b:before {
464+
@apply w-full bg-gradient-to-b from-white;
465+
height: var(--btrix-overflow-scrim-width);
466+
--tw-gradient-from: var(--btrix-overflow-scroll-scrim-color, white);
467+
}
449468
}
450469

451470
/* Following styles won't work with layers */

frontend/src/utils/state.ts

+7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export function makeAppStateService() {
4747
// Org details
4848
org: OrgData | null | undefined = undefined;
4949

50+
userGuideOpen = false;
51+
5052
// Since org slug is used to ID an org, use `userOrg`
5153
// to retrieve the basic org info like name and ID
5254
// before other org details are available
@@ -159,6 +161,11 @@ export function makeAppStateService() {
159161
}
160162
}
161163

164+
@unlock()
165+
updateUserGuideOpen(open: boolean) {
166+
appState.userGuideOpen = open;
167+
}
168+
162169
@transaction()
163170
@unlock()
164171
resetAll() {

frontend/src/utils/workflow.ts

+38
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { z } from "zod";
44
import { getAppSettings } from "./app";
55

66
import type { Tags } from "@/components/ui/tag-input";
7+
import type { UserGuideEventMap } from "@/index";
78
import {
89
Behavior,
910
ScopeType,
@@ -37,6 +38,43 @@ export const SECTIONS = [
3738
export const sectionsEnum = z.enum(SECTIONS);
3839
export type SectionsEnum = z.infer<typeof sectionsEnum>;
3940

41+
export enum GuideHash {
42+
Scope = "scope",
43+
Limits = "crawl-limits",
44+
Behaviors = "page-behavior",
45+
BrowserSettings = "browser-settings",
46+
Scheduling = "scheduling",
47+
Metadata = "metadata",
48+
}
49+
50+
export const workflowTabToGuideHash: Record<SectionsEnum, GuideHash> = {
51+
scope: GuideHash.Scope,
52+
limits: GuideHash.Limits,
53+
behaviors: GuideHash.Behaviors,
54+
browserSettings: GuideHash.BrowserSettings,
55+
scheduling: GuideHash.Scheduling,
56+
metadata: GuideHash.Metadata,
57+
};
58+
59+
export function makeUserGuideEvent(
60+
section: SectionsEnum,
61+
): UserGuideEventMap["btrix-user-guide-show"] {
62+
const userGuideHash =
63+
(workflowTabToGuideHash[section] as GuideHash | undefined) ||
64+
GuideHash.Scope;
65+
66+
return new CustomEvent<UserGuideEventMap["btrix-user-guide-show"]["detail"]>(
67+
"btrix-user-guide-show",
68+
{
69+
detail: {
70+
path: `user-guide/workflow-setup/#${userGuideHash}`,
71+
},
72+
bubbles: true,
73+
composed: true,
74+
},
75+
);
76+
}
77+
4078
export function defaultLabel(value: unknown): string {
4179
if (value === Infinity) {
4280
return msg("Default: Unlimited");

0 commit comments

Comments
 (0)