@sit-onyx/headless 0.4.0-dev-20251112131902 → 0.4.0-dev-20251118074438

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.
@@ -2,4 +2,5 @@ export * from './composables/comboBox/createComboBox.testing.js';
2
2
  export * from './composables/listbox/createListbox.testing.js';
3
3
  export * from './composables/menuButton/createMenuButton.testing.js';
4
4
  export * from './composables/navigationMenu/createMenu.testing.js';
5
+ export * from './composables/slider/createSlider.testing.js';
5
6
  export * from './composables/tabs/createTabs.testing.js';
@@ -265,6 +265,142 @@ const navigationTesting = async ({ nav, buttons }) => {
265
265
  await nav.press("ArrowLeft");
266
266
  await expect(buttons.nth(0)).toBeFocused();
267
267
  };
268
+ const getRailPosition = async (rail, percentage) => {
269
+ const box = await rail.boundingBox();
270
+ const x = box.x + box.width * percentage;
271
+ const y = box.y + box.height * 0.5;
272
+ return { x, y };
273
+ };
274
+ const singleSliderTesting = async ({ page, slider, rail }) => {
275
+ await test.step("Basic accessibility and initial state", async () => {
276
+ await expect(slider).toHaveAttribute("role", "slider");
277
+ await expect(slider).toHaveAttribute("aria-valuenow", "50");
278
+ await expect(slider).toHaveAttribute("aria-valuemin", "0");
279
+ await expect(slider).toHaveAttribute("aria-valuemax", "100");
280
+ await expect(slider).toHaveAttribute("aria-orientation", "horizontal");
281
+ await expect(slider).toHaveAttribute("type", "range");
282
+ await expect(slider).toHaveAttribute("step", "1");
283
+ await slider.focus();
284
+ await expect(slider).toBeFocused();
285
+ });
286
+ await test.step("Keyboard navigation - Arrow keys", async () => {
287
+ await slider.press("ArrowRight");
288
+ await expect(slider).toHaveValue("51");
289
+ await slider.press("ArrowLeft");
290
+ await expect(slider).toHaveValue("50");
291
+ await slider.press("ArrowUp");
292
+ await expect(slider).toHaveValue("51");
293
+ await slider.press("ArrowDown");
294
+ await expect(slider).toHaveValue("50");
295
+ });
296
+ await test.step("Keyboard navigation - Home and End keys", async () => {
297
+ await slider.press("End");
298
+ await expect(slider).toHaveValue("100");
299
+ await slider.press("Home");
300
+ await expect(slider).toHaveValue("0");
301
+ });
302
+ await test.step("Keyboard navigation - Page Up/Down keys", async () => {
303
+ await slider.press("PageUp");
304
+ await expect(slider).toHaveValue("1");
305
+ await slider.press("PageDown");
306
+ await expect(slider).toHaveValue("0");
307
+ });
308
+ await test.step("Shift key modifies step size", async () => {
309
+ await slider.press("Shift+ArrowRight");
310
+ await expect(slider).toHaveValue("10");
311
+ });
312
+ await test.step("Mouse interaction - Click to set value", async () => {
313
+ await expect(rail).toBeVisible();
314
+ const { x, y } = await getRailPosition(rail, 0.75);
315
+ await page.mouse.click(x, y);
316
+ await expect(slider).toHaveValue("75");
317
+ });
318
+ await test.step("Mouse drag interaction", async () => {
319
+ const startPosition = await getRailPosition(rail, 0.25);
320
+ const endPosition = await getRailPosition(rail, 0.75);
321
+ await page.mouse.move(startPosition.x, startPosition.y);
322
+ await page.mouse.down();
323
+ await expect(slider).toHaveValue("25");
324
+ await page.mouse.move(endPosition.x, endPosition.y);
325
+ await page.mouse.up();
326
+ await expect(slider).toHaveValue("75");
327
+ });
328
+ await test.step("Boundary value constraints", async () => {
329
+ await slider.press("Home");
330
+ await expect(slider).toHaveValue("0");
331
+ await slider.press("ArrowLeft");
332
+ await expect(slider).toHaveValue("0");
333
+ await slider.press("End");
334
+ await expect(slider).toHaveValue("100");
335
+ await slider.press("ArrowRight");
336
+ await expect(slider).toHaveValue("100");
337
+ });
338
+ };
339
+ const rangeSliderTesting = async ({ page, slider, rail }) => {
340
+ const firstThumb = slider.first();
341
+ const lastThumb = slider.last();
342
+ await test.step("Basic accessibility for range slider", async () => {
343
+ await expect(slider).toHaveCount(2);
344
+ for (let i = 0; i < 2; i++) {
345
+ const thumb = slider.nth(i);
346
+ await expect(thumb).toHaveAttribute("role", "slider");
347
+ await expect(thumb).toHaveAttribute("aria-valuenow", i === 0 ? "25" : "75");
348
+ await expect(thumb).toHaveAttribute("aria-valuemin", "0");
349
+ await expect(thumb).toHaveAttribute("aria-valuemax", "100");
350
+ await expect(thumb).toHaveAttribute("step", "1");
351
+ }
352
+ });
353
+ await test.step("Independent thumb navigation", async () => {
354
+ await firstThumb.focus();
355
+ await expect(firstThumb).toBeFocused();
356
+ await firstThumb.press("ArrowRight");
357
+ await expect(firstThumb).toHaveValue("26");
358
+ await firstThumb.press("ArrowLeft");
359
+ await expect(firstThumb).toHaveValue("25");
360
+ await lastThumb.focus();
361
+ await expect(lastThumb).toBeFocused();
362
+ await lastThumb.press("ArrowRight");
363
+ await expect(lastThumb).toHaveValue("76");
364
+ await lastThumb.press("ArrowLeft");
365
+ await expect(lastThumb).toHaveValue("75");
366
+ });
367
+ await test.step("Tab navigation between thumbs", async () => {
368
+ await firstThumb.focus();
369
+ await expect(firstThumb).toBeFocused();
370
+ await page.keyboard.press("Tab");
371
+ await expect(lastThumb).toBeFocused();
372
+ await page.keyboard.press("Shift+Tab");
373
+ await expect(firstThumb).toBeFocused();
374
+ });
375
+ await test.step("Click and drag interactions", async () => {
376
+ let position = await getRailPosition(rail, 0.1);
377
+ await page.mouse.click(position.x, position.y);
378
+ await expect(firstThumb).toHaveValue("10");
379
+ await expect(lastThumb).toHaveValue("75");
380
+ position = await getRailPosition(rail, 0.8);
381
+ await page.mouse.click(position.x, position.y);
382
+ await expect(firstThumb).toHaveValue("10");
383
+ await expect(lastThumb).toHaveValue("80");
384
+ position = await getRailPosition(rail, 0.1);
385
+ await page.mouse.move(position.x, position.y);
386
+ await page.mouse.down();
387
+ position = await getRailPosition(rail, 0.5);
388
+ await page.mouse.move(position.x, position.y);
389
+ await expect(firstThumb, "should change value by dragging thumb").toHaveValue("50");
390
+ await expect(lastThumb).toHaveValue("80");
391
+ position = await getRailPosition(rail, 0.9);
392
+ await page.mouse.move(position.x, position.y);
393
+ await expect(firstThumb, "should not exceed last thumb when dragging").toHaveValue("80");
394
+ await expect(lastThumb).toHaveValue("80");
395
+ await page.mouse.up();
396
+ });
397
+ await test.step("Home and End navigation", async () => {
398
+ await firstThumb.press("Home");
399
+ await expect(firstThumb).toHaveValue("0");
400
+ await lastThumb.press("End");
401
+ await expect(lastThumb).toHaveValue("100");
402
+ });
403
+ };
268
404
  const tabsTesting = async (options) => {
269
405
  await expect(options.tablist, 'tablist element must have role "tablist"').toHaveRole("tablist");
270
406
  await expect(options.tablist, "tablist must have an accessible label").toHaveAttribute(
@@ -365,5 +501,7 @@ export {
365
501
  listboxTesting,
366
502
  menuButtonTesting,
367
503
  navigationTesting,
504
+ rangeSliderTesting,
505
+ singleSliderTesting,
368
506
  tabsTesting
369
507
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sit-onyx/headless",
3
3
  "description": "Headless composables for Vue",
4
- "version": "0.4.0-dev-20251112131902",
4
+ "version": "0.4.0-dev-20251118074438",
5
5
  "type": "module",
6
6
  "author": "Schwarz IT KG",
7
7
  "license": "Apache-2.0",
@@ -1,92 +0,0 @@
1
- declare const _default: import('vue').DefineComponent<{}, {
2
- slider: import('../../index.js', { with: { "resolution-mode": "import" } }).HeadlessComposable<{
3
- root: import('vue').ComputedRef<{
4
- ref: import('../../index.js', { with: { "resolution-mode": "import" } }).HeadlessElRef<HTMLElement>;
5
- style: {
6
- touchAction: "pan-y";
7
- };
8
- onMousedown: (event: MouseEvent) => void;
9
- onTouchstart: (event: TouchEvent) => void;
10
- }>;
11
- thumbContainer: import('vue').ComputedRef<(data: {
12
- index: number;
13
- value: number;
14
- }) => {
15
- "data-index": number;
16
- style: {
17
- left: string;
18
- };
19
- }>;
20
- thumbInput: import('vue').ComputedRef<(data: {
21
- index: number;
22
- value: number;
23
- }) => {
24
- min: number;
25
- max: number;
26
- value: number;
27
- role: string;
28
- type: string;
29
- "aria-label": string;
30
- "aria-valuemin": number;
31
- "aria-valuemax": number;
32
- "aria-valuenow": number;
33
- "aria-orientation": "horizontal";
34
- "data-index": number;
35
- tabIndex: number;
36
- step: string | number;
37
- disabled: boolean;
38
- onChange: (event: Event) => void;
39
- onFocus: (event: FocusEvent) => void;
40
- onBlur: (event: FocusEvent) => void;
41
- onKeydown: (event: KeyboardEvent) => void;
42
- }>;
43
- mark: import('vue').ComputedRef<(data: {
44
- value: number;
45
- label?: string;
46
- positionOffset?: string;
47
- }) => {
48
- "data-value": number;
49
- "aria-hidden": true;
50
- style: {
51
- left: string;
52
- };
53
- }>;
54
- markLabel: import('vue').ComputedRef<(data: {
55
- value: number;
56
- }) => {
57
- "data-value": number;
58
- style: {
59
- left: string;
60
- };
61
- "aria-hidden": true;
62
- }>;
63
- track: import('vue').ComputedRef<{
64
- role: string;
65
- "aria-hidden": true;
66
- style: {
67
- left: string;
68
- width: string;
69
- };
70
- }>;
71
- rail: import('vue').ComputedRef<{
72
- role: string;
73
- "aria-hidden": true;
74
- }>;
75
- }, {
76
- isDragging: import('vue').Ref<boolean, boolean>;
77
- activeThumbIndex: import('vue').Ref<number, number>;
78
- isRange: import('vue').ComputedRef<boolean>;
79
- trackOffset: import('vue').ComputedRef<number>;
80
- trackLength: import('vue').ComputedRef<number>;
81
- marksList: import('vue').ComputedRef<{
82
- value: number;
83
- label?: string;
84
- }[]>;
85
- shiftStep: import('vue').ComputedRef<number>;
86
- normalizedValues: import('vue').ComputedRef<number[]>;
87
- }, {
88
- clampValue: import('vue').ComputedRef<(value: number) => number>;
89
- roundToStep: import('vue').ComputedRef<(value: number) => number | undefined>;
90
- }>;
91
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
92
- export default _default;
@@ -1,92 +0,0 @@
1
- declare const _default: import('vue').DefineComponent<{}, {
2
- slider: import('../../index.js', { with: { "resolution-mode": "import" } }).HeadlessComposable<{
3
- root: import('vue').ComputedRef<{
4
- ref: import('../../index.js', { with: { "resolution-mode": "import" } }).HeadlessElRef<HTMLElement>;
5
- style: {
6
- touchAction: "pan-y";
7
- };
8
- onMousedown: (event: MouseEvent) => void;
9
- onTouchstart: (event: TouchEvent) => void;
10
- }>;
11
- thumbContainer: import('vue').ComputedRef<(data: {
12
- index: number;
13
- value: number;
14
- }) => {
15
- "data-index": number;
16
- style: {
17
- left: string;
18
- };
19
- }>;
20
- thumbInput: import('vue').ComputedRef<(data: {
21
- index: number;
22
- value: number;
23
- }) => {
24
- min: number;
25
- max: number;
26
- value: number;
27
- role: string;
28
- type: string;
29
- "aria-label": string;
30
- "aria-valuemin": number;
31
- "aria-valuemax": number;
32
- "aria-valuenow": number;
33
- "aria-orientation": "horizontal";
34
- "data-index": number;
35
- tabIndex: number;
36
- step: string | number;
37
- disabled: boolean;
38
- onChange: (event: Event) => void;
39
- onFocus: (event: FocusEvent) => void;
40
- onBlur: (event: FocusEvent) => void;
41
- onKeydown: (event: KeyboardEvent) => void;
42
- }>;
43
- mark: import('vue').ComputedRef<(data: {
44
- value: number;
45
- label?: string;
46
- positionOffset?: string;
47
- }) => {
48
- "data-value": number;
49
- "aria-hidden": true;
50
- style: {
51
- left: string;
52
- };
53
- }>;
54
- markLabel: import('vue').ComputedRef<(data: {
55
- value: number;
56
- }) => {
57
- "data-value": number;
58
- style: {
59
- left: string;
60
- };
61
- "aria-hidden": true;
62
- }>;
63
- track: import('vue').ComputedRef<{
64
- role: string;
65
- "aria-hidden": true;
66
- style: {
67
- left: string;
68
- width: string;
69
- };
70
- }>;
71
- rail: import('vue').ComputedRef<{
72
- role: string;
73
- "aria-hidden": true;
74
- }>;
75
- }, {
76
- isDragging: import('vue').Ref<boolean, boolean>;
77
- activeThumbIndex: import('vue').Ref<number, number>;
78
- isRange: import('vue').ComputedRef<boolean>;
79
- trackOffset: import('vue').ComputedRef<number>;
80
- trackLength: import('vue').ComputedRef<number>;
81
- marksList: import('vue').ComputedRef<{
82
- value: number;
83
- label?: string;
84
- }[]>;
85
- shiftStep: import('vue').ComputedRef<number>;
86
- normalizedValues: import('vue').ComputedRef<number[]>;
87
- }, {
88
- clampValue: import('vue').ComputedRef<(value: number) => number>;
89
- roundToStep: import('vue').ComputedRef<(value: number) => number | undefined>;
90
- }>;
91
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
92
- export default _default;
@@ -1,92 +0,0 @@
1
- declare const _default: import('vue').DefineComponent<{}, {
2
- slider: import('../../index.js', { with: { "resolution-mode": "import" } }).HeadlessComposable<{
3
- root: import('vue').ComputedRef<{
4
- ref: import('../../index.js', { with: { "resolution-mode": "import" } }).HeadlessElRef<HTMLElement>;
5
- style: {
6
- touchAction: "pan-y";
7
- };
8
- onMousedown: (event: MouseEvent) => void;
9
- onTouchstart: (event: TouchEvent) => void;
10
- }>;
11
- thumbContainer: import('vue').ComputedRef<(data: {
12
- index: number;
13
- value: number;
14
- }) => {
15
- "data-index": number;
16
- style: {
17
- left: string;
18
- };
19
- }>;
20
- thumbInput: import('vue').ComputedRef<(data: {
21
- index: number;
22
- value: number;
23
- }) => {
24
- min: number;
25
- max: number;
26
- value: number;
27
- role: string;
28
- type: string;
29
- "aria-label": string;
30
- "aria-valuemin": number;
31
- "aria-valuemax": number;
32
- "aria-valuenow": number;
33
- "aria-orientation": "horizontal";
34
- "data-index": number;
35
- tabIndex: number;
36
- step: string | number;
37
- disabled: boolean;
38
- onChange: (event: Event) => void;
39
- onFocus: (event: FocusEvent) => void;
40
- onBlur: (event: FocusEvent) => void;
41
- onKeydown: (event: KeyboardEvent) => void;
42
- }>;
43
- mark: import('vue').ComputedRef<(data: {
44
- value: number;
45
- label?: string;
46
- positionOffset?: string;
47
- }) => {
48
- "data-value": number;
49
- "aria-hidden": true;
50
- style: {
51
- left: string;
52
- };
53
- }>;
54
- markLabel: import('vue').ComputedRef<(data: {
55
- value: number;
56
- }) => {
57
- "data-value": number;
58
- style: {
59
- left: string;
60
- };
61
- "aria-hidden": true;
62
- }>;
63
- track: import('vue').ComputedRef<{
64
- role: string;
65
- "aria-hidden": true;
66
- style: {
67
- left: string;
68
- width: string;
69
- };
70
- }>;
71
- rail: import('vue').ComputedRef<{
72
- role: string;
73
- "aria-hidden": true;
74
- }>;
75
- }, {
76
- isDragging: import('vue').Ref<boolean, boolean>;
77
- activeThumbIndex: import('vue').Ref<number, number>;
78
- isRange: import('vue').ComputedRef<boolean>;
79
- trackOffset: import('vue').ComputedRef<number>;
80
- trackLength: import('vue').ComputedRef<number>;
81
- marksList: import('vue').ComputedRef<{
82
- value: number;
83
- label?: string;
84
- }[]>;
85
- shiftStep: import('vue').ComputedRef<number>;
86
- normalizedValues: import('vue').ComputedRef<number[]>;
87
- }, {
88
- clampValue: import('vue').ComputedRef<(value: number) => number>;
89
- roundToStep: import('vue').ComputedRef<(value: number) => number | undefined>;
90
- }>;
91
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
92
- export default _default;