fix: Open and highlight correct workflow form section on tab click (#2463)
Fixes https://github.com/webrecorder/browsertrix/issues/2461 ## Changes Opens workflow form section when clicking on section navigation link, fixing issue with scroll position impacting unopened panels.
This commit is contained in:
parent
03fa00df45
commit
fa05d68292
@ -419,6 +419,8 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderFormSections() {
|
private renderFormSections() {
|
||||||
|
const activeTab = this.progressState?.activeTab;
|
||||||
|
|
||||||
const panelBody = ({
|
const panelBody = ({
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
@ -439,21 +441,27 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
)}
|
)}
|
||||||
?open=${required || hasError || tabProgress?.completed}
|
?open=${required || hasError || tabProgress?.completed}
|
||||||
@sl-focus=${() => {
|
@sl-focus=${() => {
|
||||||
this.updateProgressState({
|
if (activeTab !== name) {
|
||||||
activeTab: name,
|
this.updateProgressState({
|
||||||
});
|
activeTab: name,
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
@sl-show=${this.handleCurrentTarget(() => {
|
@sl-show=${this.handleCurrentTarget(() => {
|
||||||
this.pauseObserve();
|
if (activeTab !== name) {
|
||||||
this.updateProgressState({
|
this.pauseHandlePanelIntersect(name);
|
||||||
activeTab: name,
|
this.updateProgressState({
|
||||||
});
|
activeTab: name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
track(AnalyticsTrackEvent.ExpandWorkflowFormSection, {
|
track(AnalyticsTrackEvent.ExpandWorkflowFormSection, {
|
||||||
section: name,
|
section: name,
|
||||||
});
|
});
|
||||||
})}
|
})}
|
||||||
@sl-hide=${this.handleCurrentTarget((e: SlHideEvent) => {
|
@sl-hide=${this.handleCurrentTarget((e: SlHideEvent) => {
|
||||||
|
this.pauseHandlePanelIntersect(name);
|
||||||
|
|
||||||
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
|
||||||
@ -473,8 +481,12 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
invalidEl.checkValidity();
|
invalidEl.checkValidity();
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
@sl-after-show=${this.handleCurrentTarget(this.resumeObserve)}
|
@sl-after-show=${this.handleCurrentTarget(
|
||||||
@sl-after-hide=${this.handleCurrentTarget(this.resumeObserve)}
|
this.resumeHandlePanelIntersect,
|
||||||
|
)}
|
||||||
|
@sl-after-hide=${this.handleCurrentTarget(
|
||||||
|
this.resumeHandlePanelIntersect,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div slot="expand-icon" class="flex items-center">
|
<div slot="expand-icon" class="flex items-center">
|
||||||
<sl-tooltip
|
<sl-tooltip
|
||||||
@ -1652,25 +1664,22 @@ https://archiveweb.page/images/${"logo.svg"}`}
|
|||||||
this.updateFormState(formState);
|
this.updateFormState(formState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use to skip updates on intersect changes, like when scrolling
|
// Store the panel to focus or scroll to temporarily
|
||||||
// an element into view on click
|
// so that the intersection observer doesn't update
|
||||||
private skipIntersectUpdate = false;
|
// the active tab on scroll
|
||||||
|
private scrollTargetTab: StepName | null = null;
|
||||||
|
|
||||||
private pauseObserve() {
|
private pauseHandlePanelIntersect(targetActiveTab: StepName) {
|
||||||
this.onPanelIntersect.flush();
|
this.onPanelIntersect.flush();
|
||||||
this.skipIntersectUpdate = true;
|
this.scrollTargetTab = targetActiveTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
private resumeObserve() {
|
private resumeHandlePanelIntersect() {
|
||||||
this.skipIntersectUpdate = false;
|
// Reset scroll target tab to indicate that scroll handling should continue
|
||||||
|
this.scrollTargetTab = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly onPanelIntersect = throttle(10)((e: Event) => {
|
private readonly onPanelIntersect = throttle(10)((e: Event) => {
|
||||||
if (this.skipIntersectUpdate) {
|
|
||||||
this.resumeObserve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { entries } = (e as IntersectEvent).detail;
|
const { entries } = (e as IntersectEvent).detail;
|
||||||
|
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
@ -1681,17 +1690,27 @@ https://archiveweb.page/images/${"logo.svg"}`}
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const panels = [...(this.panels ?? [])];
|
if (!this.scrollTargetTab) {
|
||||||
const activeTab = panels
|
// Make first visible tab active
|
||||||
.find((panel) => this.visiblePanels.has(panel.id))
|
const panels = [...(this.panels ?? [])];
|
||||||
?.id.split(panelSuffix)[0] as StepName | undefined;
|
const targetActiveTab = panels
|
||||||
|
.find((panel) => this.visiblePanels.has(panel.id))
|
||||||
|
?.id.split(panelSuffix)[0] as StepName | undefined;
|
||||||
|
|
||||||
if (!STEPS.includes(activeTab!)) {
|
if (!targetActiveTab || !STEPS.includes(targetActiveTab)) {
|
||||||
console.debug("tab not in steps:", activeTab, this.visiblePanels);
|
if (targetActiveTab) {
|
||||||
return;
|
console.debug(
|
||||||
|
"tab not in steps:",
|
||||||
|
targetActiveTab,
|
||||||
|
this.visiblePanels,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateProgressState({ activeTab: targetActiveTab });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateProgressState({ activeTab });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
private hasRequiredFields(): boolean {
|
private hasRequiredFields(): boolean {
|
||||||
@ -1709,15 +1728,22 @@ https://archiveweb.page/images/${"logo.svg"}`}
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pauseObserve();
|
if (this.progressState?.activeTab) {
|
||||||
|
this.pauseHandlePanelIntersect(this.progressState.activeTab);
|
||||||
|
}
|
||||||
|
|
||||||
// Focus on focusable element, if found, to highlight the section
|
// Focus on focusable element, if found, to highlight the section
|
||||||
const summary = activeTabPanel
|
const details = activeTabPanel.querySelector("sl-details")!;
|
||||||
.querySelector("sl-details")
|
const summary = details.shadowRoot?.querySelector<HTMLElement>(
|
||||||
?.shadowRoot?.querySelector<HTMLElement>("summary[aria-controls]");
|
"summary[aria-controls]",
|
||||||
|
);
|
||||||
|
|
||||||
|
activeTabPanel.scrollIntoView({ block: "start" });
|
||||||
|
|
||||||
if (summary) {
|
if (summary) {
|
||||||
summary.focus({
|
summary.focus({
|
||||||
|
// Handle scrolling into view separately
|
||||||
|
preventScroll: true,
|
||||||
// Prevent firefox from applying own focus styles
|
// Prevent firefox from applying own focus styles
|
||||||
focusVisible: false,
|
focusVisible: false,
|
||||||
} as FocusOptions & {
|
} as FocusOptions & {
|
||||||
@ -1726,7 +1752,12 @@ https://archiveweb.page/images/${"logo.svg"}`}
|
|||||||
} else {
|
} else {
|
||||||
console.debug("summary not found in sl-details");
|
console.debug("summary not found in sl-details");
|
||||||
}
|
}
|
||||||
activeTabPanel.scrollIntoView({ block: "start" });
|
|
||||||
|
if (details.open) {
|
||||||
|
this.resumeHandlePanelIntersect();
|
||||||
|
} else {
|
||||||
|
void details.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleRemoveRegex(e: CustomEvent) {
|
private async handleRemoveRegex(e: CustomEvent) {
|
||||||
|
Loading…
Reference in New Issue
Block a user