fix: Sync user guide to correct workflow section (#2592)
Resolves https://github.com/webrecorder/browsertrix/issues/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 <hi@emma.cafe>
This commit is contained in:
parent
652e8a6085
commit
6b510fe89c
9
frontend/docs/docs/js/embed.js
Normal file
9
frontend/docs/docs/js/embed.js
Normal file
@ -0,0 +1,9 @@
|
||||
if (window.self !== window.top) {
|
||||
// Within iframe--assume this is an iframe embedded in the Browsertrix app.
|
||||
const style = document.createElement("style");
|
||||
|
||||
// Decrease text size without decreasing element size and overall spacing
|
||||
style.innerText = `.md-typeset { font-size: 0.7rem; }`;
|
||||
|
||||
window.document.body.appendChild(style);
|
||||
}
|
@ -6,6 +6,7 @@ extra_css:
|
||||
- stylesheets/extra.css
|
||||
extra_javascript:
|
||||
- js/insertversion.js
|
||||
- js/embed.js
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: docs/overrides
|
||||
|
@ -64,6 +64,7 @@ import type {
|
||||
ExclusionChangeEvent,
|
||||
QueueExclusionTable,
|
||||
} from "@/features/crawl-workflows/queue-exclusion-table";
|
||||
import type { UserGuideEventMap } from "@/index";
|
||||
import { infoCol, inputCol } from "@/layouts/columns";
|
||||
import { pageSectionsWithNav } from "@/layouts/pageSectionsWithNav";
|
||||
import { panel } from "@/layouts/panel";
|
||||
@ -105,7 +106,9 @@ import {
|
||||
getDefaultFormState,
|
||||
getInitialFormState,
|
||||
getServerDefaults,
|
||||
makeUserGuideEvent,
|
||||
SECTIONS,
|
||||
workflowTabToGuideHash,
|
||||
type FormState,
|
||||
type WorkflowDefaults,
|
||||
} from "@/utils/workflow";
|
||||
@ -420,6 +423,7 @@ export class WorkflowEditor extends BtrixElement {
|
||||
nav: this.renderNav(),
|
||||
main: this.renderFormSections(),
|
||||
sticky: true,
|
||||
stickyTopClassname: tw`lg:top-16`,
|
||||
})}
|
||||
${this.renderFooter()}
|
||||
</form>
|
||||
@ -485,6 +489,10 @@ export class WorkflowEditor extends BtrixElement {
|
||||
this.updateProgressState({
|
||||
activeTab: name,
|
||||
});
|
||||
|
||||
if (this.appState.userGuideOpen) {
|
||||
this.dispatchEvent(makeUserGuideEvent(name));
|
||||
}
|
||||
}
|
||||
|
||||
track(AnalyticsTrackEvent.ExpandWorkflowFormSection, {
|
||||
@ -2160,6 +2168,21 @@ https://archiveweb.page/images/${"logo.svg"}`}
|
||||
await this.updateComplete;
|
||||
|
||||
void this.scrollToActivePanel();
|
||||
|
||||
if (this.appState.userGuideOpen) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent<UserGuideEventMap["btrix-user-guide-show"]["detail"]>(
|
||||
"btrix-user-guide-show",
|
||||
{
|
||||
detail: {
|
||||
path: `user-guide/workflow-setup/#${workflowTabToGuideHash[step]}`,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private onKeyDown(event: KeyboardEvent) {
|
||||
|
@ -339,7 +339,9 @@ export class App extends BtrixElement {
|
||||
<div class="min-w-screen flex min-h-screen flex-col">
|
||||
${this.renderSuperadminBanner()} ${this.renderNavBar()}
|
||||
${this.renderAlertBanner()}
|
||||
<main class="relative flex flex-auto md:min-h-[calc(100vh-3.125rem)]">
|
||||
<main
|
||||
class="relative flex flex-auto transition-[padding] md:min-h-[calc(100vh-3.125rem)]"
|
||||
>
|
||||
${this.renderPage()}
|
||||
</main>
|
||||
<div class="border-t border-neutral-100">${this.renderFooter()}</div>
|
||||
@ -356,14 +358,27 @@ export class App extends BtrixElement {
|
||||
<sl-drawer
|
||||
id="userGuideDrawer"
|
||||
label=${msg("User Guide")}
|
||||
style="--body-spacing: 0; --footer-spacing: var(--sl-spacing-2x-small);"
|
||||
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)]"
|
||||
?open=${this.appState.userGuideOpen}
|
||||
contained
|
||||
@sl-hide=${() => AppStateService.updateUserGuideOpen(false)}
|
||||
@sl-after-hide=${() => {
|
||||
// FIXME There might be a way to handle this in Mkdocs, but updating
|
||||
// only the hash doesn't seem to update the docs view
|
||||
const iframe = this.userGuideDrawer.querySelector("iframe");
|
||||
|
||||
if (!iframe) return;
|
||||
|
||||
const src = iframe.src;
|
||||
iframe.src = src.slice(0, src.indexOf("#"));
|
||||
}}
|
||||
>
|
||||
<span slot="label" class="flex items-center gap-3">
|
||||
<sl-icon name="book" class=""></sl-icon>
|
||||
<span>${msg("User Guide")}</span>
|
||||
</span>
|
||||
<iframe
|
||||
class="size-full transition-opacity duration-slow"
|
||||
class="size-full text-xs transition-opacity duration-slow"
|
||||
src="${this.docsUrl}user-guide/workflow-setup/"
|
||||
></iframe>
|
||||
<sl-button
|
||||
@ -952,7 +967,9 @@ export class App extends BtrixElement {
|
||||
iframe.src = this.fullDocsUrl;
|
||||
}
|
||||
|
||||
void this.userGuideDrawer.show();
|
||||
if (!this.appState.userGuideOpen) {
|
||||
AppStateService.updateUserGuideOpen(true);
|
||||
}
|
||||
} else {
|
||||
console.debug("user guide iframe not found");
|
||||
}
|
||||
|
@ -8,11 +8,13 @@ export function pageSectionsWithNav({
|
||||
main,
|
||||
placement = "start",
|
||||
sticky = false,
|
||||
stickyTopClassname,
|
||||
}: {
|
||||
nav: TemplateResult;
|
||||
main: TemplateResult;
|
||||
placement?: "start" | "top";
|
||||
sticky?: boolean;
|
||||
stickyTopClassname?: string; // e.g. `lg:top-0`
|
||||
}) {
|
||||
return html`
|
||||
<div
|
||||
@ -24,7 +26,8 @@ export function pageSectionsWithNav({
|
||||
<div
|
||||
class=${clsx(
|
||||
tw`flex flex-1 flex-col gap-2`,
|
||||
sticky && tw`lg:sticky lg:top-2 lg:self-start`,
|
||||
sticky &&
|
||||
[tw`lg:sticky lg:self-start`, stickyTopClassname || tw`lg:top-2`],
|
||||
placement === "start" ? tw`lg:max-w-[16.5rem]` : tw`lg:flex-row`,
|
||||
)}
|
||||
part="tabs"
|
||||
|
@ -592,7 +592,9 @@ export class WorkflowDetail extends BtrixElement {
|
||||
private readonly renderEditor = () => html`
|
||||
<div class="col-span-1">${this.renderBreadcrumbs()}</div>
|
||||
|
||||
<header class="col-span-1 mb-3 flex flex-wrap gap-2">
|
||||
<header
|
||||
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"
|
||||
>
|
||||
<btrix-detail-page-title .item=${this.workflow}></btrix-detail-page-title>
|
||||
</header>
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { localized, msg } from "@lit/localize";
|
||||
import clsx from "clsx";
|
||||
import { mergeDeep } from "immutable";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@ -7,33 +8,18 @@ import type { PartialDeep } from "type-fest";
|
||||
|
||||
import { ScopeType, type Seed, type WorkflowParams } from "./types";
|
||||
|
||||
import type { UserGuideEventMap } from "@/index";
|
||||
import { pageNav, type Breadcrumb } from "@/layouts/pageHeader";
|
||||
import { WorkflowScopeType } from "@/types/workflow";
|
||||
import LiteElement, { html } from "@/utils/LiteElement";
|
||||
import { tw } from "@/utils/tailwind";
|
||||
import {
|
||||
DEFAULT_AUTOCLICK_SELECTOR,
|
||||
DEFAULT_SELECT_LINKS,
|
||||
makeUserGuideEvent,
|
||||
type SectionsEnum,
|
||||
type FormState as WorkflowFormState,
|
||||
} from "@/utils/workflow";
|
||||
|
||||
type GuideHash =
|
||||
| "scope"
|
||||
| "limits"
|
||||
| "browser-settings"
|
||||
| "scheduling"
|
||||
| "metadata"
|
||||
| "review-settings";
|
||||
|
||||
const workflowTabToGuideHash: Record<string, GuideHash> = {
|
||||
crawlSetup: "scope",
|
||||
crawlLimits: "limits",
|
||||
browserSettings: "browser-settings",
|
||||
crawlScheduling: "scheduling",
|
||||
crawlMetadata: "metadata",
|
||||
confirmSettings: "review-settings",
|
||||
};
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* ```ts
|
||||
@ -55,23 +41,6 @@ export class WorkflowsNew extends LiteElement {
|
||||
@property({ type: Object })
|
||||
initialWorkflow?: WorkflowParams;
|
||||
|
||||
private userGuideHashLink: GuideHash = "scope";
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
this.userGuideHashLink =
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
workflowTabToGuideHash[window.location.hash.slice(1) as GuideHash] ||
|
||||
"scope";
|
||||
|
||||
window.addEventListener("hashchange", () => {
|
||||
const hashValue = window.location.hash.slice(1) as GuideHash;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
this.userGuideHashLink = workflowTabToGuideHash[hashValue] || "scope";
|
||||
});
|
||||
}
|
||||
|
||||
private get defaultNewWorkflow(): WorkflowParams {
|
||||
return {
|
||||
name: "",
|
||||
@ -125,21 +94,20 @@ export class WorkflowsNew extends LiteElement {
|
||||
|
||||
return html`
|
||||
<div class="mb-5">${this.renderBreadcrumbs()}</div>
|
||||
<header class="flex items-center justify-between">
|
||||
<header
|
||||
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"
|
||||
>
|
||||
<h2 class="mb-6 text-xl font-semibold">${msg("New Crawl Workflow")}</h2>
|
||||
<sl-button
|
||||
size="small"
|
||||
class=${clsx(
|
||||
tw`transition-opacity`,
|
||||
this.appState.userGuideOpen && tw`pointer-events-none opacity-0`,
|
||||
)}
|
||||
?disabled=${this.appState.userGuideOpen}
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent<
|
||||
UserGuideEventMap["btrix-user-guide-show"]["detail"]
|
||||
>("btrix-user-guide-show", {
|
||||
detail: {
|
||||
path: `user-guide/workflow-setup/#${this.userGuideHashLink}`,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
makeUserGuideEvent(window.location.hash.slice(1) as SectionsEnum),
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
@ -96,6 +96,15 @@
|
||||
|
||||
/* Transition */
|
||||
--sl-transition-x-fast: 100ms;
|
||||
|
||||
/*
|
||||
*
|
||||
* Browsertrix theme tokens
|
||||
*
|
||||
*/
|
||||
/* Overflow scrim */
|
||||
--btrix-overflow-scroll-scrim-color: var(--sl-panel-background-color);
|
||||
--btrix-overflow-scrim-width: 3rem;
|
||||
}
|
||||
|
||||
body {
|
||||
@ -446,6 +455,16 @@
|
||||
sl-button.button-card::part(label) {
|
||||
@apply flex flex-1 flex-col justify-center gap-2 text-left;
|
||||
}
|
||||
|
||||
.scrim:before {
|
||||
@apply pointer-events-none absolute -z-10;
|
||||
}
|
||||
|
||||
.scrim-to-b:before {
|
||||
@apply w-full bg-gradient-to-b from-white;
|
||||
height: var(--btrix-overflow-scrim-width);
|
||||
--tw-gradient-from: var(--btrix-overflow-scroll-scrim-color, white);
|
||||
}
|
||||
}
|
||||
|
||||
/* Following styles won't work with layers */
|
||||
|
@ -47,6 +47,8 @@ export function makeAppStateService() {
|
||||
// Org details
|
||||
org: OrgData | null | undefined = undefined;
|
||||
|
||||
userGuideOpen = false;
|
||||
|
||||
// Since org slug is used to ID an org, use `userOrg`
|
||||
// to retrieve the basic org info like name and ID
|
||||
// before other org details are available
|
||||
@ -159,6 +161,11 @@ export function makeAppStateService() {
|
||||
}
|
||||
}
|
||||
|
||||
@unlock()
|
||||
updateUserGuideOpen(open: boolean) {
|
||||
appState.userGuideOpen = open;
|
||||
}
|
||||
|
||||
@transaction()
|
||||
@unlock()
|
||||
resetAll() {
|
||||
|
@ -4,6 +4,7 @@ import { z } from "zod";
|
||||
import { getAppSettings } from "./app";
|
||||
|
||||
import type { Tags } from "@/components/ui/tag-input";
|
||||
import type { UserGuideEventMap } from "@/index";
|
||||
import {
|
||||
Behavior,
|
||||
ScopeType,
|
||||
@ -37,6 +38,43 @@ export const SECTIONS = [
|
||||
export const sectionsEnum = z.enum(SECTIONS);
|
||||
export type SectionsEnum = z.infer<typeof sectionsEnum>;
|
||||
|
||||
export enum GuideHash {
|
||||
Scope = "scope",
|
||||
Limits = "crawl-limits",
|
||||
Behaviors = "page-behavior",
|
||||
BrowserSettings = "browser-settings",
|
||||
Scheduling = "scheduling",
|
||||
Metadata = "metadata",
|
||||
}
|
||||
|
||||
export const workflowTabToGuideHash: Record<SectionsEnum, GuideHash> = {
|
||||
scope: GuideHash.Scope,
|
||||
limits: GuideHash.Limits,
|
||||
behaviors: GuideHash.Behaviors,
|
||||
browserSettings: GuideHash.BrowserSettings,
|
||||
scheduling: GuideHash.Scheduling,
|
||||
metadata: GuideHash.Metadata,
|
||||
};
|
||||
|
||||
export function makeUserGuideEvent(
|
||||
section: SectionsEnum,
|
||||
): UserGuideEventMap["btrix-user-guide-show"] {
|
||||
const userGuideHash =
|
||||
(workflowTabToGuideHash[section] as GuideHash | undefined) ||
|
||||
GuideHash.Scope;
|
||||
|
||||
return new CustomEvent<UserGuideEventMap["btrix-user-guide-show"]["detail"]>(
|
||||
"btrix-user-guide-show",
|
||||
{
|
||||
detail: {
|
||||
path: `user-guide/workflow-setup/#${userGuideHash}`,
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function defaultLabel(value: unknown): string {
|
||||
if (value === Infinity) {
|
||||
return msg("Default: Unlimited");
|
||||
|
Loading…
Reference in New Issue
Block a user