@orangesk/orange-design-system 2.0.0-beta.40 → 2.0.0-beta.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangesk/orange-design-system",
3
- "version": "2.0.0-beta.40",
3
+ "version": "2.0.0-beta.41",
4
4
  "private": false,
5
5
  "engines": {
6
6
  "node": ">=20.x"
@@ -16,6 +16,7 @@ export default class AnchorNavigation {
16
16
  private scrollEndHandler: () => void;
17
17
  private scrollEndFallbackHandler: () => void;
18
18
  private resizeHandler: () => void;
19
+ private mouseDownHandler: (event: MouseEvent) => void;
19
20
  private dragStartHandler: (event: MouseEvent) => void;
20
21
  private dragMoveHandler: (event: MouseEvent) => void;
21
22
  private dragEndHandler: () => void;
@@ -50,6 +51,7 @@ export default class AnchorNavigation {
50
51
  this.scrollEndHandler = this.handleScrollEnd.bind(this);
51
52
  this.scrollEndFallbackHandler = this.handleScrollEndFallback.bind(this);
52
53
  this.resizeHandler = this.handleResize.bind(this);
54
+ this.mouseDownHandler = this.handleMouseDown.bind(this);
53
55
  this.dragStartHandler = this.handleDragStart.bind(this);
54
56
  this.dragMoveHandler = this.handleDragMove.bind(this);
55
57
  this.dragEndHandler = this.handleDragEnd.bind(this);
@@ -116,6 +118,7 @@ export default class AnchorNavigation {
116
118
  this.teardownScrollSpyListeners();
117
119
 
118
120
  this.element.addEventListener("click", this.anchorClickHandler);
121
+ this.element.addEventListener("mousedown", this.mouseDownHandler);
119
122
  window.addEventListener("scroll", this.scrollSpyHandler, { passive: true });
120
123
 
121
124
  if (this.supportsScrollEnd) {
@@ -133,6 +136,7 @@ export default class AnchorNavigation {
133
136
 
134
137
  private teardownScrollSpyListeners(): void {
135
138
  this.element.removeEventListener("click", this.anchorClickHandler);
139
+ this.element.removeEventListener("mousedown", this.mouseDownHandler);
136
140
  window.removeEventListener("scroll", this.scrollSpyHandler);
137
141
  window.removeEventListener("resize", this.resizeHandler);
138
142
 
@@ -148,20 +152,35 @@ export default class AnchorNavigation {
148
152
  }
149
153
  }
150
154
 
151
- private handleAnchorClick(event: Event): void {
155
+ private handleMouseDown(event: MouseEvent): void {
156
+ if (event.button !== 0) return;
157
+
152
158
  const target = event.target as HTMLElement | null;
153
- const anchor = target?.closest(
154
- ".anchor-navigation__item",
155
- ) as HTMLAnchorElement | null;
159
+ if (!target || !this.element.contains(target)) return;
156
160
 
157
- if (!anchor || !this.element.contains(anchor)) return;
161
+ const interactiveSelector =
162
+ "a, button, input, select, textarea, label, [role='button'], [contenteditable='true']";
163
+ if (target.closest(interactiveSelector)) return;
164
+
165
+ // Prevent native text-selection drag from triggering page autoscroll.
166
+ event.preventDefault();
167
+ }
158
168
 
169
+ private handleAnchorClick(event: Event): void {
159
170
  if (this.suppressClick) {
160
171
  event.preventDefault();
172
+ event.stopPropagation();
161
173
  this.suppressClick = false;
162
174
  return;
163
175
  }
164
176
 
177
+ const target = event.target as HTMLElement | null;
178
+ const anchor = target?.closest(
179
+ ".anchor-navigation__item",
180
+ ) as HTMLAnchorElement | null;
181
+
182
+ if (!anchor || !this.element.contains(anchor)) return;
183
+
165
184
  event.preventDefault();
166
185
 
167
186
  const href = anchor.getAttribute("href");
@@ -300,18 +319,47 @@ export default class AnchorNavigation {
300
319
  activeLink: HTMLElement,
301
320
  forceCenter: boolean = false,
302
321
  ): void {
322
+ const maxScrollLeft = Math.max(
323
+ 0,
324
+ contentLeft.scrollWidth - contentLeft.clientWidth,
325
+ );
326
+ const navLinks = this.navLinks;
327
+ let firstNavLink: HTMLAnchorElement | null = null;
328
+ let lastNavLink: HTMLAnchorElement | null = null;
329
+
330
+ if (navLinks && navLinks.length > 0) {
331
+ firstNavLink = navLinks[0];
332
+ lastNavLink = navLinks[navLinks.length - 1];
333
+ }
334
+
335
+ const isFirstItem = activeLink === firstNavLink;
336
+ const isLastItem = activeLink === lastNavLink;
337
+
338
+ let targetScrollLeft: number | null = null;
339
+ if (isFirstItem) {
340
+ targetScrollLeft = 0;
341
+ } else if (isLastItem) {
342
+ targetScrollLeft = maxScrollLeft;
343
+ }
344
+
303
345
  const itemLeft = activeLink.offsetLeft;
304
346
  const itemRight = itemLeft + activeLink.clientWidth;
305
347
  const viewportLeft = contentLeft.scrollLeft;
306
348
  const viewportRight = viewportLeft + contentLeft.clientWidth;
307
349
  const isVisible = itemLeft >= viewportLeft && itemRight <= viewportRight;
308
350
 
309
- if (!forceCenter && isVisible) return;
351
+ if (targetScrollLeft === null && !forceCenter && isVisible) return;
352
+
353
+ if (targetScrollLeft === null) {
354
+ targetScrollLeft =
355
+ itemLeft - contentLeft.clientWidth / 2 + activeLink.clientWidth / 2;
356
+ }
310
357
 
311
- const targetScrollLeft =
312
- itemLeft - contentLeft.clientWidth / 2 + activeLink.clientWidth / 2;
313
358
  const behavior = window.innerWidth < 768 ? "auto" : "smooth";
314
- const nextScrollLeft = Math.max(0, targetScrollLeft);
359
+ const nextScrollLeft = Math.min(
360
+ maxScrollLeft,
361
+ Math.max(0, targetScrollLeft),
362
+ );
315
363
 
316
364
  if (typeof contentLeft.scrollTo === "function") {
317
365
  contentLeft.scrollTo({
@@ -44,7 +44,7 @@
44
44
  }
45
45
 
46
46
  @include breakpoint.get("sm", "down") {
47
- margin-right: space.get("medium") !important;
47
+ margin-right: convert.to-rem(10px) !important;
48
48
  padding: convert.to-rem(15px) 0 !important;
49
49
  }
50
50
 
@@ -134,6 +134,7 @@
134
134
  display: flex;
135
135
  flex: 1 1 auto;
136
136
  min-width: 0;
137
+ scroll-snap-type: none !important;
137
138
 
138
139
  &.is-draggable {
139
140
  user-select: none;
@@ -274,9 +274,63 @@ describe("rendering AnchorNavigation", () => {
274
274
  section.remove();
275
275
  });
276
276
 
277
+ it("prevents mousedown default on non-interactive content-right area", () => {
278
+ const { container } = render(
279
+ <AnchorNavigation items={basicItems}>
280
+ <div className="align-lg-right mb-none">
281
+ <span>16 €</span>
282
+ <span className="text-secondary">S viazanostou 24 mesiacov</span>
283
+ </div>
284
+ <a href="/senior-pausal/chcem-senior">Kupit Senior pausal</a>
285
+ </AnchorNavigation>,
286
+ );
287
+ const anchorNavigationElement = initializeAnchorNavigation(container);
288
+ const anchorNavigation = AnchorNavigationStatic.getInstance(
289
+ anchorNavigationElement,
290
+ );
291
+ const infoBlock = container.querySelector(".align-lg-right");
292
+
293
+ const downEvent = new MouseEvent("mousedown", {
294
+ bubbles: true,
295
+ cancelable: true,
296
+ button: 0,
297
+ });
298
+ infoBlock?.dispatchEvent(downEvent);
299
+
300
+ expect(downEvent.defaultPrevented).toBe(true);
301
+
302
+ anchorNavigation?.destroy();
303
+ });
304
+
305
+ it("does not prevent mousedown default on interactive content-right link", () => {
306
+ const { container } = render(
307
+ <AnchorNavigation items={basicItems}>
308
+ <a href="/senior-pausal/chcem-senior">Kupit Senior pausal</a>
309
+ </AnchorNavigation>,
310
+ );
311
+ const anchorNavigationElement = initializeAnchorNavigation(container);
312
+ const anchorNavigation = AnchorNavigationStatic.getInstance(
313
+ anchorNavigationElement,
314
+ );
315
+ const ctaLink = container.querySelector(
316
+ 'a[href="/senior-pausal/chcem-senior"]',
317
+ );
318
+
319
+ const downEvent = new MouseEvent("mousedown", {
320
+ bubbles: true,
321
+ cancelable: true,
322
+ button: 0,
323
+ });
324
+ ctaLink?.dispatchEvent(downEvent);
325
+
326
+ expect(downEvent.defaultPrevented).toBe(false);
327
+
328
+ anchorNavigation?.destroy();
329
+ });
330
+
277
331
  it("scrolls active item using the left content viewport width", () => {
278
332
  const section = document.createElement("section");
279
- section.id = "contact";
333
+ section.id = "pricing";
280
334
  document.body.appendChild(section);
281
335
 
282
336
  const { container } = render(
@@ -292,13 +346,17 @@ describe("rendering AnchorNavigation", () => {
292
346
  const contentLeft = container.querySelector(
293
347
  ".anchor-navigation__content-left",
294
348
  );
295
- const activeLink = container.querySelector('a[href="#contact"]');
349
+ const activeLink = container.querySelector('a[href="#pricing"]');
296
350
  const scrollToSpy = vi.fn();
297
351
 
298
352
  Object.defineProperty(contentLeft, "clientWidth", {
299
353
  configurable: true,
300
354
  value: 180,
301
355
  });
356
+ Object.defineProperty(contentLeft, "scrollWidth", {
357
+ configurable: true,
358
+ value: 600,
359
+ });
302
360
  Object.defineProperty(contentLeft, "scrollLeft", {
303
361
  configurable: true,
304
362
  value: 0,
@@ -314,7 +372,7 @@ describe("rendering AnchorNavigation", () => {
314
372
  });
315
373
  contentLeft.scrollTo = scrollToSpy;
316
374
 
317
- anchorNavigation.initScrollSpy("contact");
375
+ anchorNavigation.initScrollSpy("pricing");
318
376
 
319
377
  expect(scrollToSpy).toHaveBeenCalledWith({
320
378
  left: 170,
@@ -327,7 +385,7 @@ describe("rendering AnchorNavigation", () => {
327
385
 
328
386
  it("centers clicked active item even when it is already visible", () => {
329
387
  const section = document.createElement("section");
330
- section.id = "contact";
388
+ section.id = "pricing";
331
389
  document.body.appendChild(section);
332
390
 
333
391
  const { container } = render(<AnchorNavigation items={basicItems} />);
@@ -338,13 +396,17 @@ describe("rendering AnchorNavigation", () => {
338
396
  const contentLeft = container.querySelector(
339
397
  ".anchor-navigation__content-left",
340
398
  );
341
- const activeLink = container.querySelector('a[href="#contact"]');
399
+ const activeLink = container.querySelector('a[href="#pricing"]');
342
400
  const scrollToSpy = vi.fn();
343
401
 
344
402
  Object.defineProperty(contentLeft, "clientWidth", {
345
403
  configurable: true,
346
404
  value: 200,
347
405
  });
406
+ Object.defineProperty(contentLeft, "scrollWidth", {
407
+ configurable: true,
408
+ value: 600,
409
+ });
348
410
  Object.defineProperty(contentLeft, "scrollLeft", {
349
411
  configurable: true,
350
412
  value: 180,
@@ -360,7 +422,7 @@ describe("rendering AnchorNavigation", () => {
360
422
  });
361
423
  contentLeft.scrollTo = scrollToSpy;
362
424
 
363
- anchorNavigation.initScrollSpy("contact");
425
+ anchorNavigation.initScrollSpy("pricing");
364
426
 
365
427
  expect(scrollToSpy).toHaveBeenCalledWith({
366
428
  left: 160,