feat: Workflow form collapsible section enhancements (#2381)

Resolves https://github.com/webrecorder/browsertrix/issues/2359

## Changes

- Track when a workflow form section is opened
- Hide workflow form section navigation on small screens

---------

Co-authored-by: Ilya Kreymer <ikreymer@users.noreply.github.com>
Co-authored-by: Emma Segal-Grossman <hi@emma.cafe>
Co-authored-by: Ilya Kreymer <ikreymer@gmail.com>
This commit is contained in:
sua yoo 2025-02-20 18:42:00 -08:00 committed by GitHub
parent 3ca68bf1d2
commit 8db80f5570
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 90 additions and 35 deletions

View File

@ -5,8 +5,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { TailwindElement } from "@/classes/TailwindElement"; import { TailwindElement } from "@/classes/TailwindElement";
const DEFAULT_PANEL_ID = "default-panel"; const DEFAULT_PANEL_ID = "default-panel";
// postcss-lit-disable-next-line
export const TWO_COL_SCREEN_MIN_CSS = css`64.5rem`;
const tabTagName = "btrix-tab-list-tab" as const; const tabTagName = "btrix-tab-list-tab" as const;
@ -58,12 +56,6 @@ export class TabList extends TailwindElement {
--track-width: 4px; --track-width: 4px;
} }
@media only screen and (min-width: ${TWO_COL_SCREEN_MIN_CSS}) {
.navWrapper {
overflow: initial;
}
}
.header { .header {
grid-area: header; grid-area: header;
font-size: var(--sl-font-size-large); font-size: var(--sl-font-size-large);
@ -94,12 +86,6 @@ export class TabList extends TailwindElement {
margin-left: var(--track-width); margin-left: var(--track-width);
} }
@media only screen and (min-width: ${TWO_COL_SCREEN_MIN_CSS}) {
.tablist {
flex-direction: column;
}
}
.track { .track {
display: none; display: none;
position: absolute; position: absolute;
@ -118,13 +104,6 @@ export class TabList extends TailwindElement {
border-radius: var(--track-width); border-radius: var(--track-width);
background-color: var(--sl-color-primary-500); background-color: var(--sl-color-primary-500);
} }
@media only screen and (min-width: ${TWO_COL_SCREEN_MIN_CSS}) {
.show-indicator .track,
.show-indicator .indicator {
display: block;
}
}
`; `;
// ID of active tab // ID of active tab
@ -178,7 +157,9 @@ export class TabList extends TailwindElement {
} }
render() { render() {
return html`<div class="navWrapper min-w-0">${this.renderNav()}</div>`; return html`<div class="min-w-0 lg:overflow-hidden">
${this.renderNav()}
</div>`;
} }
renderNav() { renderNav() {
@ -193,11 +174,14 @@ export class TabList extends TailwindElement {
? "hide-indicator" ? "hide-indicator"
: "show-indicator"} -mx-3 overflow-x-hidden px-3" : "show-indicator"} -mx-3 overflow-x-hidden px-3"
> >
<div class="track" role="presentation"> <div class="track lg:block" role="presentation">
<div class="indicator" role="presentation"></div> <div class="indicator lg:block" role="presentation"></div>
</div> </div>
<ul class="tablist -mx-3 overflow-x-auto px-3" role="tablist"> <ul
class="tablist -mx-3 overflow-x-auto px-3 lg:flex-col"
role="tablist"
>
<slot></slot> <slot></slot>
</ul> </ul>
</div> </div>

View File

@ -59,9 +59,11 @@ import { panel } from "@/layouts/panel";
import infoTextStrings from "@/strings/crawl-workflows/infoText"; import infoTextStrings from "@/strings/crawl-workflows/infoText";
import scopeTypeLabels from "@/strings/crawl-workflows/scopeType"; import scopeTypeLabels from "@/strings/crawl-workflows/scopeType";
import sectionStrings from "@/strings/crawl-workflows/section"; import sectionStrings from "@/strings/crawl-workflows/section";
import { AnalyticsTrackEvent } from "@/trackEvents";
import { ScopeType, type Seed, type WorkflowParams } from "@/types/crawler"; import { ScopeType, type Seed, type WorkflowParams } from "@/types/crawler";
import type { UnderlyingFunction } from "@/types/utils"; import type { UnderlyingFunction } from "@/types/utils";
import { NewWorkflowOnlyScopeType } from "@/types/workflow"; import { NewWorkflowOnlyScopeType } from "@/types/workflow";
import { track } from "@/utils/analytics";
import { isApiError } from "@/utils/api"; import { isApiError } from "@/utils/api";
import { DEPTH_SUPPORTED_SCOPES, isPageScopeType } from "@/utils/crawler"; import { DEPTH_SUPPORTED_SCOPES, isPageScopeType } from "@/utils/crawler";
import { import {
@ -69,6 +71,7 @@ import {
humanizeNextDate, humanizeNextDate,
humanizeSchedule, humanizeSchedule,
} from "@/utils/cron"; } from "@/utils/cron";
import { makeCurrentTargetHandler, stopProp } from "@/utils/events";
import { formValidator, maxLengthValidator } from "@/utils/form"; import { formValidator, maxLengthValidator } from "@/utils/form";
import localize from "@/utils/localize"; import localize from "@/utils/localize";
import { isArchivingDisabled } from "@/utils/orgs"; import { isArchivingDisabled } from "@/utils/orgs";
@ -234,6 +237,7 @@ export class WorkflowEditor extends BtrixElement {
threshold: 0.2, // stricter; default is 0.6 threshold: 0.2, // stricter; default is 0.6
}); });
private readonly handleCurrentTarget = makeCurrentTargetHandler(this);
private readonly checkFormValidity = formValidator(this); private readonly checkFormValidity = formValidator(this);
private readonly validateNameMax = maxLengthValidator(50); private readonly validateNameMax = maxLengthValidator(50);
private readonly validateDescriptionMax = maxLengthValidator(350); private readonly validateDescriptionMax = maxLengthValidator(350);
@ -405,7 +409,10 @@ export class WorkflowEditor extends BtrixElement {
}; };
return html` return html`
<btrix-tab-list tab=${ifDefined(this.progressState?.activeTab)}> <btrix-tab-list
class="hidden lg:block"
tab=${ifDefined(this.progressState?.activeTab)}
>
${STEPS.map(button)} ${STEPS.map(button)}
</btrix-tab-list> </btrix-tab-list>
`; `;
@ -436,13 +443,17 @@ export class WorkflowEditor extends BtrixElement {
activeTab: name, activeTab: name,
}); });
}} }}
@sl-show=${() => { @sl-show=${this.handleCurrentTarget(() => {
this.pauseObserve(); this.pauseObserve();
this.updateProgressState({ this.updateProgressState({
activeTab: name, activeTab: name,
}); });
}}
@sl-hide=${(e: SlHideEvent) => { track(AnalyticsTrackEvent.ExpandWorkflowFormSection, {
section: name,
});
})}
@sl-hide=${this.handleCurrentTarget((e: SlHideEvent) => {
const el = e.currentTarget as SlDetails; const el = e.currentTarget as SlDetails;
// Check if there's any invalid elements before hiding // Check if there's any invalid elements before hiding
@ -461,12 +472,19 @@ export class WorkflowEditor extends BtrixElement {
invalidEl.focus(); invalidEl.focus();
invalidEl.checkValidity(); invalidEl.checkValidity();
} }
}} })}
@sl-after-show=${this.resumeObserve} @sl-after-show=${this.handleCurrentTarget(this.resumeObserve)}
@sl-after-hide=${this.resumeObserve} @sl-after-hide=${this.handleCurrentTarget(this.resumeObserve)}
> >
<div slot="expand-icon" class="flex items-center"> <div slot="expand-icon" class="flex items-center">
<sl-tooltip content=${msg("Show section")} hoist> <sl-tooltip
content=${msg("Show section")}
hoist
@sl-show=${stopProp}
@sl-hide=${stopProp}
@sl-after-show=${stopProp}
@sl-after-hide=${stopProp}
>
<sl-icon name="chevron-down" class="size-5"></sl-icon> <sl-icon name="chevron-down" class="size-5"></sl-icon>
</sl-tooltip> </sl-tooltip>
</div> </div>
@ -477,6 +495,10 @@ export class WorkflowEditor extends BtrixElement {
<sl-tooltip <sl-tooltip
content=${msg("Please fix all errors in this section")} content=${msg("Please fix all errors in this section")}
hoist hoist
@sl-show=${stopProp}
@sl-hide=${stopProp}
@sl-after-show=${stopProp}
@sl-after-hide=${stopProp}
> >
<sl-icon <sl-icon
name="exclamation-lg" name="exclamation-lg"

View File

@ -3,7 +3,17 @@
*/ */
export enum AnalyticsTrackEvent { export enum AnalyticsTrackEvent {
/**
* Generic
*/
PageView = "pageview", PageView = "pageview",
/**
* Collections
*/
CopyShareCollectionLink = "Copy share collection link", CopyShareCollectionLink = "Copy share collection link",
DownloadPublicCollection = "Download public collection", DownloadPublicCollection = "Download public collection",
/**
* Workflows
*/
ExpandWorkflowFormSection = "Expand workflow form section",
} }

View File

@ -8,9 +8,10 @@
import { AnalyticsTrackEvent } from "../trackEvents"; import { AnalyticsTrackEvent } from "../trackEvents";
export type AnalyticsTrackProps = { export type AnalyticsTrackProps = {
org_slug: string | null; org_slug?: string | null;
collection_slug?: string | null;
logged_in?: boolean; logged_in?: boolean;
collection_slug?: string;
section?: string;
}; };
declare global { declare global {

View File

@ -0,0 +1,38 @@
/**
* Ignore events bubbling from children. Common use case would be
* ignoring @sl-hide events from tooltips within a dialog.
*
* @example Usage:
* ```
* <btrix-element
* @sl-hide=${makeCurrentTargetHandler(this)(() => console.log("Only current target!"))}
* >
* </btrix-element>
* ```
*/
export function makeCurrentTargetHandler(t: EventTarget) {
const currentTargetHandler: <T extends Event = CustomEvent>(
handler: (event: T) => void,
) => (event: T) => void = (handler) => (e) => {
if (e.target === e.currentTarget) {
handler.bind(t)(e);
}
};
return currentTargetHandler;
}
/**
* Stop propgation shorthand.
*
* @example Usage:
* ```
* <btrix-element
* @sl-show=${stopProp}
* >
* </btrix-element>
* ```
*/
export function stopProp(e: Event) {
e.stopPropagation();
}