Skip to content

Commit

Permalink
feat(pat-stacks): Add configurable scroll support.
Browse files Browse the repository at this point in the history
Add "scroll-selector" and "scroll-offset" arguments to pat-stacks,
likewise as in pat-collapsible. "scroll-selector" accepts a CSS selector
string or the special values "self" to scroll to the element itself and
"none" to block any scrolling behavior from parent pat-stacks configuration
options.

With these configuration options you can control the scrolling behavior
for pat-stacks on a fine-grained level.
  • Loading branch information
thet committed Aug 30, 2023
1 parent 92e1e6b commit a8aae63
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/pat/stacks/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ The Stacks pattern may be configured through a `data-pat-stacks` attribute. The
| `transition` | `none` | Transition effect to use. Must be one of `none`, `css`, `fade` or `slide`. |
| `effect-duration` | `fast` | Duration of transition. This is ignored if the transition is `none` or `css`. |
| `effect-easing` | `swing` | Easing to use for the transition. This must be a known jQuery easing method. jQuery includes `swing` and `linear`, but more can be included via jQuery UI. |
| `scroll-selector` | | CSS selector, `self` or `none`. Defines which element will be scrolled into view. `self` if it is the stacks content element itself. `none` to disable scrolling if a scrolling selector is inherited from a parent pat-stacks element. |
| `scroll-offset` | | `offset` in pixels to stop scrolling before the target position defined by `scroll-selector`. Can also be a negative number. |
33 changes: 31 additions & 2 deletions src/pat/stacks/stacks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import $ from "jquery";
import { BasePattern } from "../../core/basepattern";
import Parser from "../../core/parser";
import events from "../../core/events";
import logging from "../../core/logging";
import Parser from "../../core/parser";
import registry from "../../core/registry";
import utils from "../../core/utils";

Expand All @@ -12,15 +13,39 @@ parser.addArgument("selector", "> *[id]");
parser.addArgument("transition", "none", ["none", "css", "fade", "slide"]);
parser.addArgument("effect-duration", "fast");
parser.addArgument("effect-easing", "swing");
// pat-scroll support
parser.addArgument("scroll-selector");
parser.addArgument("scroll-offset", 0);

const debounce_scroll_timer = { timer: null };

class Pattern extends BasePattern {
static name = "stacks";
static trigger = ".pat-stacks";
static parser = parser;
document = document;

init() {
async init() {
this.$el = $(this.el);

// pat-scroll support
if (this.options.scroll?.selector && this.options.scroll.selector !== "none") {
const Scroll = (await import("../scroll/scroll")).default;
this.scroll = new Scroll(this.el, {
trigger: "manual",
selector: this.options.scroll.selector,
offset: this.options.scroll?.offset,
});
await events.await_pattern_init(this.scroll);

// scroll debouncer for later use.
this.debounce_scroll = utils.debounce(
this.scroll.scrollTo.bind(this.scroll),
10,
debounce_scroll_timer
);
}

this._setupStack();
$(this.document).on("click", "a", this._onClick.bind(this));
}
Expand Down Expand Up @@ -78,6 +103,10 @@ class Pattern extends BasePattern {
e.preventDefault();
this._updateAnchors(href_parts[1]);
this._switch(href_parts[1]);

this.debounce_scroll?.(); // debounce scroll, if available.

// Notify other patterns
$(e.target).trigger("pat-update", {
pattern: "stacks",
action: "attribute-changed",
Expand Down
62 changes: 62 additions & 0 deletions src/pat/stacks/stacks.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import $ from "jquery";
import events from "../../core/events";
import Stacks from "./stacks";
import utils from "../../core/utils";
import { jest } from "@jest/globals";

describe("pat-stacks", function () {
Expand Down Expand Up @@ -164,4 +165,65 @@ describe("pat-stacks", function () {
expect($("#l2").hasClass("current")).toBe(true);
});
});

describe("5 - Scrolling support.", function () {
beforeEach(function () {
document.body.innerHTML = "";
this.spy_scrollTo = jest
.spyOn(window, "scrollTo")
.mockImplementation(() => null);
});

afterEach(function () {
this.spy_scrollTo.mockRestore();
});

it("5.1 - Scrolls to self.", async function () {
document.body.innerHTML = `
<a href='#s51'>1</a>
<div class="pat-stacks" data-pat-stacks="scroll-selector: self">
<section id="s51">
</section>
</div>
`;
const el = document.querySelector(".pat-stacks");

const instance = new Stacks(el);
await events.await_pattern_init(instance);

const s51 = document.querySelector("[href='#s51']");
$(s51).click();
await utils.timeout(10);

expect(this.spy_scrollTo).toHaveBeenCalled();
});

it("5.2 - Does clear scroll setting from parent config.", async function () {
// NOTE: We give the stack section a different id.
// The event handler which is registered on the document in the
// previous test is still attached. Two event handlers are run when
// clicking here and if the anchor-targets would have the same id
// the scrolling would happen as it was set up in the previous
// test.
document.body.innerHTML = `
<div data-pat-stacks="scroll-selector: self">
<a href='#s52'>1</a>
<div class="pat-stacks" data-pat-stacks="scroll-selector: none">
<section id="s52">
</section>
</div>
</div>
`;
const el = document.querySelector(".pat-stacks");

const instance = new Stacks(el);
await events.await_pattern_init(instance);

const s52 = document.querySelector("[href='#s52']");
$(s52).click();
await utils.timeout(10);

expect(this.spy_scrollTo).not.toHaveBeenCalled();
});
});
});

0 comments on commit a8aae63

Please sign in to comment.