Skip to content

Commit a8aae63

Browse files
committed
feat(pat-stacks): Add configurable scroll support.
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.
1 parent 92e1e6b commit a8aae63

File tree

3 files changed

+95
-2
lines changed

3 files changed

+95
-2
lines changed

src/pat/stacks/documentation.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,5 @@ The Stacks pattern may be configured through a `data-pat-stacks` attribute. The
4545
| `transition` | `none` | Transition effect to use. Must be one of `none`, `css`, `fade` or `slide`. |
4646
| `effect-duration` | `fast` | Duration of transition. This is ignored if the transition is `none` or `css`. |
4747
| `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. |
48+
| `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. |
49+
| `scroll-offset` | | `offset` in pixels to stop scrolling before the target position defined by `scroll-selector`. Can also be a negative number. |

src/pat/stacks/stacks.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import $ from "jquery";
22
import { BasePattern } from "../../core/basepattern";
3-
import Parser from "../../core/parser";
3+
import events from "../../core/events";
44
import logging from "../../core/logging";
5+
import Parser from "../../core/parser";
56
import registry from "../../core/registry";
67
import utils from "../../core/utils";
78

@@ -12,15 +13,39 @@ parser.addArgument("selector", "> *[id]");
1213
parser.addArgument("transition", "none", ["none", "css", "fade", "slide"]);
1314
parser.addArgument("effect-duration", "fast");
1415
parser.addArgument("effect-easing", "swing");
16+
// pat-scroll support
17+
parser.addArgument("scroll-selector");
18+
parser.addArgument("scroll-offset", 0);
19+
20+
const debounce_scroll_timer = { timer: null };
1521

1622
class Pattern extends BasePattern {
1723
static name = "stacks";
1824
static trigger = ".pat-stacks";
1925
static parser = parser;
2026
document = document;
2127

22-
init() {
28+
async init() {
2329
this.$el = $(this.el);
30+
31+
// pat-scroll support
32+
if (this.options.scroll?.selector && this.options.scroll.selector !== "none") {
33+
const Scroll = (await import("../scroll/scroll")).default;
34+
this.scroll = new Scroll(this.el, {
35+
trigger: "manual",
36+
selector: this.options.scroll.selector,
37+
offset: this.options.scroll?.offset,
38+
});
39+
await events.await_pattern_init(this.scroll);
40+
41+
// scroll debouncer for later use.
42+
this.debounce_scroll = utils.debounce(
43+
this.scroll.scrollTo.bind(this.scroll),
44+
10,
45+
debounce_scroll_timer
46+
);
47+
}
48+
2449
this._setupStack();
2550
$(this.document).on("click", "a", this._onClick.bind(this));
2651
}
@@ -78,6 +103,10 @@ class Pattern extends BasePattern {
78103
e.preventDefault();
79104
this._updateAnchors(href_parts[1]);
80105
this._switch(href_parts[1]);
106+
107+
this.debounce_scroll?.(); // debounce scroll, if available.
108+
109+
// Notify other patterns
81110
$(e.target).trigger("pat-update", {
82111
pattern: "stacks",
83112
action: "attribute-changed",

src/pat/stacks/stacks.test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import $ from "jquery";
22
import events from "../../core/events";
33
import Stacks from "./stacks";
4+
import utils from "../../core/utils";
45
import { jest } from "@jest/globals";
56

67
describe("pat-stacks", function () {
@@ -164,4 +165,65 @@ describe("pat-stacks", function () {
164165
expect($("#l2").hasClass("current")).toBe(true);
165166
});
166167
});
168+
169+
describe("5 - Scrolling support.", function () {
170+
beforeEach(function () {
171+
document.body.innerHTML = "";
172+
this.spy_scrollTo = jest
173+
.spyOn(window, "scrollTo")
174+
.mockImplementation(() => null);
175+
});
176+
177+
afterEach(function () {
178+
this.spy_scrollTo.mockRestore();
179+
});
180+
181+
it("5.1 - Scrolls to self.", async function () {
182+
document.body.innerHTML = `
183+
<a href='#s51'>1</a>
184+
<div class="pat-stacks" data-pat-stacks="scroll-selector: self">
185+
<section id="s51">
186+
</section>
187+
</div>
188+
`;
189+
const el = document.querySelector(".pat-stacks");
190+
191+
const instance = new Stacks(el);
192+
await events.await_pattern_init(instance);
193+
194+
const s51 = document.querySelector("[href='#s51']");
195+
$(s51).click();
196+
await utils.timeout(10);
197+
198+
expect(this.spy_scrollTo).toHaveBeenCalled();
199+
});
200+
201+
it("5.2 - Does clear scroll setting from parent config.", async function () {
202+
// NOTE: We give the stack section a different id.
203+
// The event handler which is registered on the document in the
204+
// previous test is still attached. Two event handlers are run when
205+
// clicking here and if the anchor-targets would have the same id
206+
// the scrolling would happen as it was set up in the previous
207+
// test.
208+
document.body.innerHTML = `
209+
<div data-pat-stacks="scroll-selector: self">
210+
<a href='#s52'>1</a>
211+
<div class="pat-stacks" data-pat-stacks="scroll-selector: none">
212+
<section id="s52">
213+
</section>
214+
</div>
215+
</div>
216+
`;
217+
const el = document.querySelector(".pat-stacks");
218+
219+
const instance = new Stacks(el);
220+
await events.await_pattern_init(instance);
221+
222+
const s52 = document.querySelector("[href='#s52']");
223+
$(s52).click();
224+
await utils.timeout(10);
225+
226+
expect(this.spy_scrollTo).not.toHaveBeenCalled();
227+
});
228+
});
167229
});

0 commit comments

Comments
 (0)