@react-aria/test-utils 1.0.0-nightly.5042 → 1.0.0-rc.0
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/README.md +70 -0
- package/dist/import.mjs +6 -4
- package/dist/main.js +15 -23
- package/dist/main.js.map +1 -1
- package/dist/module.js +6 -4
- package/dist/module.js.map +1 -1
- package/dist/private/act.cjs +33 -0
- package/dist/private/act.cjs.map +1 -0
- package/dist/private/act.js +28 -0
- package/dist/private/act.js.map +1 -0
- package/dist/private/checkboxgroup.cjs +107 -0
- package/dist/private/checkboxgroup.cjs.map +1 -0
- package/dist/private/checkboxgroup.js +102 -0
- package/dist/private/checkboxgroup.js.map +1 -0
- package/dist/private/combobox.cjs +199 -0
- package/dist/private/combobox.cjs.map +1 -0
- package/dist/private/combobox.js +194 -0
- package/dist/private/combobox.js.map +1 -0
- package/dist/private/dialog.cjs +110 -0
- package/dist/private/dialog.cjs.map +1 -0
- package/dist/private/dialog.js +105 -0
- package/dist/private/dialog.js.map +1 -0
- package/dist/private/gridlist.cjs +173 -0
- package/dist/private/gridlist.cjs.map +1 -0
- package/dist/private/gridlist.js +168 -0
- package/dist/private/gridlist.js.map +1 -0
- package/dist/private/listbox.cjs +163 -0
- package/dist/private/listbox.cjs.map +1 -0
- package/dist/private/listbox.js +158 -0
- package/dist/private/listbox.js.map +1 -0
- package/dist/private/menu.cjs +265 -0
- package/dist/private/menu.cjs.map +1 -0
- package/dist/private/menu.js +260 -0
- package/dist/private/menu.js.map +1 -0
- package/dist/private/radiogroup.cjs +122 -0
- package/dist/private/radiogroup.cjs.map +1 -0
- package/dist/private/radiogroup.js +117 -0
- package/dist/private/radiogroup.js.map +1 -0
- package/dist/private/select.cjs +169 -0
- package/dist/private/select.cjs.map +1 -0
- package/dist/private/select.js +164 -0
- package/dist/private/select.js.map +1 -0
- package/dist/private/table.cjs +346 -0
- package/dist/private/table.cjs.map +1 -0
- package/dist/private/table.js +341 -0
- package/dist/private/table.js.map +1 -0
- package/dist/private/tabs.cjs +131 -0
- package/dist/private/tabs.cjs.map +1 -0
- package/dist/private/tabs.js +126 -0
- package/dist/private/tabs.js.map +1 -0
- package/dist/private/testSetup.cjs +87 -0
- package/dist/private/testSetup.cjs.map +1 -0
- package/dist/private/testSetup.js +81 -0
- package/dist/private/testSetup.js.map +1 -0
- package/dist/private/tree.cjs +181 -0
- package/dist/private/tree.cjs.map +1 -0
- package/dist/private/tree.js +176 -0
- package/dist/private/tree.js.map +1 -0
- package/dist/private/user.cjs +85 -0
- package/dist/private/user.cjs.map +1 -0
- package/dist/private/user.js +76 -0
- package/dist/private/user.js.map +1 -0
- package/dist/{userEventMaps.main.js → private/userEventMaps.cjs} +3 -3
- package/dist/private/userEventMaps.cjs.map +1 -0
- package/dist/{userEventMaps.mjs → private/userEventMaps.js} +3 -3
- package/dist/private/userEventMaps.js.map +1 -0
- package/dist/private/utils.cjs +136 -0
- package/dist/private/utils.cjs.map +1 -0
- package/dist/private/utils.js +127 -0
- package/dist/private/utils.js.map +1 -0
- package/dist/types/src/act.d.ts +4 -0
- package/dist/types/src/checkboxgroup.d.ts +47 -0
- package/dist/types/src/combobox.d.ts +87 -0
- package/dist/types/src/dialog.d.ts +37 -0
- package/dist/types/src/events.d.ts +25 -0
- package/dist/types/src/gridlist.d.ts +56 -0
- package/dist/types/src/index.d.ts +16 -0
- package/dist/types/src/listbox.d.ts +91 -0
- package/dist/types/src/menu.d.ts +112 -0
- package/dist/types/src/radiogroup.d.ts +47 -0
- package/dist/types/src/select.d.ts +74 -0
- package/dist/types/src/table.d.ts +120 -0
- package/dist/types/src/tabs.d.ts +59 -0
- package/dist/types/src/testSetup.d.ts +6 -0
- package/dist/types/src/tree.d.ts +62 -0
- package/dist/types/src/types.d.ts +143 -0
- package/dist/types/src/user.d.ts +49 -0
- package/dist/types/src/userEventMaps.d.ts +2 -0
- package/dist/types/src/utils.d.ts +29 -0
- package/package.json +26 -18
- package/src/act.ts +35 -0
- package/src/checkboxgroup.ts +165 -0
- package/src/combobox.ts +307 -0
- package/src/dialog.ts +155 -0
- package/src/gridlist.ts +278 -0
- package/src/index.ts +17 -3
- package/src/listbox.ts +300 -0
- package/src/menu.ts +479 -0
- package/src/radiogroup.ts +179 -0
- package/src/select.ts +273 -0
- package/src/table.ts +589 -0
- package/src/tabs.ts +204 -0
- package/src/testSetup.ts +41 -36
- package/src/tree.ts +290 -0
- package/src/types.ts +171 -0
- package/src/user.ts +153 -0
- package/src/userEventMaps.ts +1 -1
- package/src/utils.ts +155 -0
- package/dist/events.main.js +0 -37
- package/dist/events.main.js.map +0 -1
- package/dist/events.mjs +0 -31
- package/dist/events.module.js +0 -31
- package/dist/events.module.js.map +0 -1
- package/dist/testSetup.main.js +0 -82
- package/dist/testSetup.main.js.map +0 -1
- package/dist/testSetup.mjs +0 -76
- package/dist/testSetup.module.js +0 -76
- package/dist/testSetup.module.js.map +0 -1
- package/dist/types.d.ts +0 -16
- package/dist/types.d.ts.map +0 -1
- package/dist/userEventMaps.main.js.map +0 -1
- package/dist/userEventMaps.module.js +0 -38
- package/dist/userEventMaps.module.js.map +0 -1
- package/src/events.ts +0 -28
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {act as $1350703ef3ac1acc$export$3ba232387fd5d6dd} from "./act.js";
|
|
2
|
+
import {within as $Hdzb6$within, waitFor as $Hdzb6$waitFor} from "@testing-library/dom";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
6
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
8
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
11
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
12
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
13
|
+
* governing permissions and limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
class $1bad24930969e309$export$e79328f5295cc2a1 {
|
|
17
|
+
constructor(opts){
|
|
18
|
+
let { root: root, user: user, interactionType: interactionType, overlayType: overlayType } = opts;
|
|
19
|
+
this.user = user;
|
|
20
|
+
this._interactionType = interactionType || 'mouse';
|
|
21
|
+
this._overlayType = overlayType || 'modal';
|
|
22
|
+
// Handle case where element provided is a wrapper of the trigger button.
|
|
23
|
+
let buttons = (0, $Hdzb6$within)(root).queryAllByRole('button');
|
|
24
|
+
let triggerButton;
|
|
25
|
+
if (buttons.length === 0) triggerButton = root;
|
|
26
|
+
else if (buttons.length === 1) triggerButton = buttons[0];
|
|
27
|
+
else triggerButton = buttons.find((button)=>button.hasAttribute('aria-haspopup'));
|
|
28
|
+
this._trigger = triggerButton ?? root;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Set the interaction type used by the dialog tester.
|
|
32
|
+
*/ setInteractionType(type) {
|
|
33
|
+
this._interactionType = type;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Opens the dialog. Defaults to using the interaction type set on the dialog tester.
|
|
37
|
+
*/ async open(opts = {}) {
|
|
38
|
+
let { interactionType: interactionType = this._interactionType } = opts;
|
|
39
|
+
let trigger = this.getTrigger();
|
|
40
|
+
if (!trigger.hasAttribute('disabled')) {
|
|
41
|
+
if (interactionType === 'mouse') await this.user.click(trigger);
|
|
42
|
+
else if (interactionType === 'touch') await this.user.pointer({
|
|
43
|
+
target: trigger,
|
|
44
|
+
keys: '[TouchA]'
|
|
45
|
+
});
|
|
46
|
+
else if (interactionType === 'keyboard') {
|
|
47
|
+
(0, $1350703ef3ac1acc$export$3ba232387fd5d6dd)(()=>trigger.focus());
|
|
48
|
+
await this.user.keyboard('[Enter]');
|
|
49
|
+
}
|
|
50
|
+
if (this._overlayType === 'popover') {
|
|
51
|
+
await (0, $Hdzb6$waitFor)(()=>{
|
|
52
|
+
if (trigger.getAttribute('aria-controls') == null) throw new Error('No aria-controls found on dialog trigger element.');
|
|
53
|
+
else return true;
|
|
54
|
+
});
|
|
55
|
+
let dialogId = trigger.getAttribute('aria-controls');
|
|
56
|
+
await (0, $Hdzb6$waitFor)(()=>{
|
|
57
|
+
if (!dialogId || document.getElementById(dialogId) == null) throw new Error(`Dialog with id of ${dialogId} not found in document.`);
|
|
58
|
+
else {
|
|
59
|
+
this._dialog = document.getElementById(dialogId);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
let dialog;
|
|
65
|
+
await (0, $Hdzb6$waitFor)(()=>{
|
|
66
|
+
dialog = document.querySelector('[role=dialog], [role=alertdialog]');
|
|
67
|
+
if (dialog == null) throw new Error('No dialog of type role="dialog" or role="alertdialog" found after pressing the trigger.');
|
|
68
|
+
else return true;
|
|
69
|
+
});
|
|
70
|
+
if (dialog && document.activeElement !== this._trigger && dialog.contains(document.activeElement)) this._dialog = dialog;
|
|
71
|
+
else throw new Error('New modal dialog doesnt contain the active element OR the active element is still the trigger. Uncertain if the proper modal dialog was found');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Closes the dialog via the Escape key.
|
|
77
|
+
*/ async close() {
|
|
78
|
+
let dialog = this._dialog;
|
|
79
|
+
if (dialog) {
|
|
80
|
+
await this.user.keyboard('[Escape]');
|
|
81
|
+
await (0, $Hdzb6$waitFor)(()=>{
|
|
82
|
+
if (document.contains(dialog)) throw new Error('Expected the dialog to not be in the document after closing it.');
|
|
83
|
+
else {
|
|
84
|
+
this._dialog = undefined;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Returns the dialog's trigger.
|
|
92
|
+
*/ getTrigger() {
|
|
93
|
+
if (!this._trigger) throw new Error('No trigger element found for dialog.');
|
|
94
|
+
return this._trigger;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns the dialog if present.
|
|
98
|
+
*/ getDialog() {
|
|
99
|
+
return this._dialog && document.contains(this._dialog) ? this._dialog : null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
export {$1bad24930969e309$export$e79328f5295cc2a1 as DialogTester};
|
|
105
|
+
//# sourceMappingURL=dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/test-utils/src/dialog.ts"],"sourcesContent":["/*\n * Copyright 2025 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {act} from './act';\nimport {DialogTesterOpts, UserOpts} from './types';\nimport {waitFor, within} from '@testing-library/dom';\n\ninterface DialogOpenOpts {\n /**\n * What interaction type to use when opening the dialog. Defaults to the interaction type set on\n * the tester.\n */\n interactionType?: UserOpts['interactionType'];\n}\n\nexport class DialogTester {\n private user;\n private _interactionType: UserOpts['interactionType'];\n private _trigger: HTMLElement | undefined;\n private _dialog: HTMLElement | undefined;\n private _overlayType: DialogTesterOpts['overlayType'];\n\n constructor(opts: DialogTesterOpts) {\n let {root, user, interactionType, overlayType} = opts;\n this.user = user;\n this._interactionType = interactionType || 'mouse';\n this._overlayType = overlayType || 'modal';\n\n // Handle case where element provided is a wrapper of the trigger button.\n let buttons = within(root).queryAllByRole('button');\n let triggerButton: HTMLElement | undefined;\n if (buttons.length === 0) {\n triggerButton = root;\n } else if (buttons.length === 1) {\n triggerButton = buttons[0];\n } else {\n triggerButton = buttons.find(button => button.hasAttribute('aria-haspopup'));\n }\n this._trigger = triggerButton ?? root;\n }\n\n /**\n * Set the interaction type used by the dialog tester.\n */\n setInteractionType(type: UserOpts['interactionType']): void {\n this._interactionType = type;\n }\n\n /**\n * Opens the dialog. Defaults to using the interaction type set on the dialog tester.\n */\n async open(opts: DialogOpenOpts = {}): Promise<void> {\n let {interactionType = this._interactionType} = opts;\n let trigger = this.getTrigger();\n if (!trigger.hasAttribute('disabled')) {\n if (interactionType === 'mouse') {\n await this.user.click(trigger);\n } else if (interactionType === 'touch') {\n await this.user.pointer({target: trigger, keys: '[TouchA]'});\n } else if (interactionType === 'keyboard') {\n act(() => trigger.focus());\n await this.user.keyboard('[Enter]');\n }\n\n if (this._overlayType === 'popover') {\n await waitFor(() => {\n if (trigger.getAttribute('aria-controls') == null) {\n throw new Error('No aria-controls found on dialog trigger element.');\n } else {\n return true;\n }\n });\n\n let dialogId = trigger.getAttribute('aria-controls');\n await waitFor(() => {\n if (!dialogId || document.getElementById(dialogId) == null) {\n throw new Error(`Dialog with id of ${dialogId} not found in document.`);\n } else {\n this._dialog = document.getElementById(dialogId)!;\n return true;\n }\n });\n } else {\n let dialog;\n await waitFor(() => {\n dialog = document.querySelector('[role=dialog], [role=alertdialog]');\n if (dialog == null) {\n throw new Error(\n 'No dialog of type role=\"dialog\" or role=\"alertdialog\" found after pressing the trigger.'\n );\n } else {\n return true;\n }\n });\n\n if (\n dialog &&\n document.activeElement !== this._trigger &&\n dialog.contains(document.activeElement)\n ) {\n this._dialog = dialog;\n } else {\n throw new Error(\n 'New modal dialog doesnt contain the active element OR the active element is still the trigger. Uncertain if the proper modal dialog was found'\n );\n }\n }\n }\n }\n\n /**\n * Closes the dialog via the Escape key.\n */\n async close(): Promise<void> {\n let dialog = this._dialog;\n if (dialog) {\n await this.user.keyboard('[Escape]');\n await waitFor(() => {\n if (document.contains(dialog)) {\n throw new Error('Expected the dialog to not be in the document after closing it.');\n } else {\n this._dialog = undefined;\n return true;\n }\n });\n }\n }\n\n /**\n * Returns the dialog's trigger.\n */\n getTrigger(): HTMLElement {\n if (!this._trigger) {\n throw new Error('No trigger element found for dialog.');\n }\n\n return this._trigger;\n }\n\n /**\n * Returns the dialog if present.\n */\n getDialog(): HTMLElement | null {\n return this._dialog && document.contains(this._dialog) ? this._dialog : null;\n }\n}\n"],"names":[],"version":3,"file":"dialog.js.map"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
var $a84d573878cc1e5e$exports = require("./act.cjs");
|
|
2
|
+
var $022fcf8e360befed$exports = require("./utils.cjs");
|
|
3
|
+
var $aoGu7$testinglibrarydom = require("@testing-library/dom");
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
function $parcel$export(e, n, v, s) {
|
|
7
|
+
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
$parcel$export(module.exports, "GridListTester", function () { return $31bb3cd1e8c56ca2$export$93a85aaed9fabd83; });
|
|
11
|
+
/*
|
|
12
|
+
* Copyright 2024 Adobe. All rights reserved.
|
|
13
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
15
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
18
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
19
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
20
|
+
* governing permissions and limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class $31bb3cd1e8c56ca2$export$93a85aaed9fabd83 {
|
|
25
|
+
constructor(opts){
|
|
26
|
+
let { root: root, user: user, interactionType: interactionType, advanceTimer: advanceTimer, direction: direction, layout: layout } = opts;
|
|
27
|
+
this.user = user;
|
|
28
|
+
this._interactionType = interactionType || 'mouse';
|
|
29
|
+
this._advanceTimer = advanceTimer;
|
|
30
|
+
this._direction = direction || 'ltr';
|
|
31
|
+
this._layout = layout || 'stack';
|
|
32
|
+
this._gridlist = root;
|
|
33
|
+
if (root.getAttribute('role') !== 'grid') {
|
|
34
|
+
let gridlist = (0, $aoGu7$testinglibrarydom.within)(root).queryByRole('grid');
|
|
35
|
+
if (gridlist) this._gridlist = gridlist;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Set the interaction type used by the gridlist tester.
|
|
40
|
+
*/ setInteractionType(type) {
|
|
41
|
+
this._interactionType = type;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Returns a row matching the specified index or text content.
|
|
45
|
+
*/ findRow(opts) {
|
|
46
|
+
let { indexOrText: indexOrText } = opts;
|
|
47
|
+
let row;
|
|
48
|
+
if (typeof indexOrText === 'number') row = this.getRows()[indexOrText];
|
|
49
|
+
else if (typeof indexOrText === 'string') row = (0, $aoGu7$testinglibrarydom.within)(this.getGridlist()).getByText(indexOrText).closest('[role=row]');
|
|
50
|
+
return row;
|
|
51
|
+
}
|
|
52
|
+
async keyboardNavigateToRow(opts) {
|
|
53
|
+
let { row: row, selectionOnNav: selectionOnNav = 'default' } = opts;
|
|
54
|
+
let altKey = (0, $022fcf8e360befed$exports.getAltKey)();
|
|
55
|
+
let rows = this.getRows();
|
|
56
|
+
let targetIndex = rows.indexOf(row);
|
|
57
|
+
if (targetIndex === -1) throw new Error('Row provided is not in the gridlist');
|
|
58
|
+
if (document.activeElement !== this._gridlist && !this._gridlist.contains(document.activeElement)) (0, $a84d573878cc1e5e$exports.act)(()=>this._gridlist.focus());
|
|
59
|
+
let focusPrevKey = this._direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
|
|
60
|
+
if (document.activeElement === this._gridlist) await this.user.keyboard(`${selectionOnNav === 'none' ? `[${altKey}>]` : ''}[ArrowDown]${selectionOnNav === 'none' ? `[/${altKey}]` : ''}`);
|
|
61
|
+
else if (this._gridlist.contains(document.activeElement) && document.activeElement.getAttribute('role') !== 'row') do await this.user.keyboard(`[${focusPrevKey}]`);
|
|
62
|
+
while (document.activeElement.getAttribute('role') !== 'row');
|
|
63
|
+
let currIndex = rows.indexOf(document.activeElement);
|
|
64
|
+
if (currIndex === -1) throw new Error('ActiveElement is not in the gridlist');
|
|
65
|
+
if (selectionOnNav === 'none') await this.user.keyboard(`[${altKey}>]`);
|
|
66
|
+
if (this._layout === 'grid') while(document.activeElement !== row){
|
|
67
|
+
let curr = document.activeElement.getBoundingClientRect();
|
|
68
|
+
let target = row.getBoundingClientRect();
|
|
69
|
+
let key;
|
|
70
|
+
// basically compare current position with desired position to determine if we need to go up/down/left/right
|
|
71
|
+
// use 1 in the comparison here for subpixels since getBoundingClientRect returns subpixels precision
|
|
72
|
+
if (Math.abs(curr.top - target.top) > 1) key = curr.top < target.top ? 'ArrowDown' : 'ArrowUp';
|
|
73
|
+
else if (Math.abs(curr.left - target.left) > 1) key = curr.left < target.left ? 'ArrowRight' : 'ArrowLeft';
|
|
74
|
+
else // if the diff in current vs desired is < 1 but it is claiming we arent focused on the target
|
|
75
|
+
// then we might be in a case where getBoundingClientRect isnt mocked
|
|
76
|
+
throw new Error('Could not navigate to target row in grid layout. Did the test mock getBoundingClientRect?');
|
|
77
|
+
await this.user.keyboard(`[${key}]`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
let direction = targetIndex > currIndex ? 'down' : 'up';
|
|
81
|
+
for(let i = 0; i < Math.abs(targetIndex - currIndex); i++)await this.user.keyboard(`[${direction === 'down' ? 'ArrowDown' : 'ArrowUp'}]`);
|
|
82
|
+
}
|
|
83
|
+
if (selectionOnNav === 'none') await this.user.keyboard(`[/${altKey}]`);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Toggles the selection for the specified gridlist row. Defaults to using the interaction type
|
|
87
|
+
* set on the gridlist tester. Note that this will endevor to always add/remove JUST the provided
|
|
88
|
+
* row to the set of selected rows.
|
|
89
|
+
*/ async toggleRowSelection(opts) {
|
|
90
|
+
let { row: row, needsLongPress: needsLongPress, checkboxSelection: checkboxSelection = true, interactionType: interactionType = this._interactionType, selectionBehavior: selectionBehavior = 'toggle' } = opts;
|
|
91
|
+
let altKey = (0, $022fcf8e360befed$exports.getAltKey)();
|
|
92
|
+
let metaKey = (0, $022fcf8e360befed$exports.getMetaKey)();
|
|
93
|
+
if (typeof row === 'string' || typeof row === 'number') row = this.findRow({
|
|
94
|
+
indexOrText: row
|
|
95
|
+
});
|
|
96
|
+
if (!row) throw new Error(`Target row "${(0, $022fcf8e360befed$exports.formatTargetNode)(opts.row)}" not found in the gridlist.`);
|
|
97
|
+
let rowCheckbox = (0, $aoGu7$testinglibrarydom.within)(row).queryByRole('checkbox');
|
|
98
|
+
if (rowCheckbox?.getAttribute('disabled') === '' || row?.getAttribute('aria-disabled') === 'true') throw new Error(`Cannot toggle selection on disabled row "${(0, $022fcf8e360befed$exports.formatTargetNode)(opts.row)}".`);
|
|
99
|
+
// this would be better than the check to do nothing in events.ts
|
|
100
|
+
// also, it'd be good to be able to trigger selection on the row instead of having to go to the checkbox directly
|
|
101
|
+
if (interactionType === 'keyboard' && (!checkboxSelection || !rowCheckbox)) {
|
|
102
|
+
await this.keyboardNavigateToRow({
|
|
103
|
+
row: row,
|
|
104
|
+
selectionOnNav: selectionBehavior === 'replace' ? 'none' : 'default'
|
|
105
|
+
});
|
|
106
|
+
if (selectionBehavior === 'replace') await this.user.keyboard(`[${altKey}>]`);
|
|
107
|
+
await this.user.keyboard('[Space]');
|
|
108
|
+
if (selectionBehavior === 'replace') await this.user.keyboard(`[/${altKey}]`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (rowCheckbox && checkboxSelection) await (0, $022fcf8e360befed$exports.pressElement)(this.user, rowCheckbox, interactionType);
|
|
112
|
+
else {
|
|
113
|
+
let cell = (0, $aoGu7$testinglibrarydom.within)(row).getAllByRole('gridcell')[0];
|
|
114
|
+
if (needsLongPress && interactionType === 'touch') // Note that long press interactions with rows is strictly touch only for grid rows
|
|
115
|
+
await (0, $022fcf8e360befed$exports.triggerLongPress)({
|
|
116
|
+
element: cell,
|
|
117
|
+
advanceTimer: this._advanceTimer,
|
|
118
|
+
pointerOpts: {
|
|
119
|
+
pointerType: 'touch'
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
else {
|
|
123
|
+
if (selectionBehavior === 'replace' && interactionType !== 'touch') await this.user.keyboard(`[${metaKey}>]`);
|
|
124
|
+
await (0, $022fcf8e360befed$exports.pressElement)(this.user, row, interactionType);
|
|
125
|
+
if (selectionBehavior === 'replace' && interactionType !== 'touch') await this.user.keyboard(`[/${metaKey}]`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Triggers the action for the specified gridlist row. Defaults to using the interaction type set
|
|
131
|
+
* on the gridlist tester.
|
|
132
|
+
*/ async triggerRowAction(opts) {
|
|
133
|
+
let { row: row, needsDoubleClick: needsDoubleClick, interactionType: interactionType = this._interactionType } = opts;
|
|
134
|
+
if (typeof row === 'string' || typeof row === 'number') row = this.findRow({
|
|
135
|
+
indexOrText: row
|
|
136
|
+
});
|
|
137
|
+
if (!row) throw new Error(`Target row "${(0, $022fcf8e360befed$exports.formatTargetNode)(opts.row)}" not found in the gridlist.`);
|
|
138
|
+
if (row.getAttribute('aria-disabled') === 'true') throw new Error(`Cannot trigger row action on disabled row "${(0, $022fcf8e360befed$exports.formatTargetNode)(opts.row)}".`);
|
|
139
|
+
if (needsDoubleClick) await this.user.dblClick(row);
|
|
140
|
+
else if (interactionType === 'keyboard') {
|
|
141
|
+
await this.keyboardNavigateToRow({
|
|
142
|
+
row: row,
|
|
143
|
+
selectionOnNav: 'none'
|
|
144
|
+
});
|
|
145
|
+
await this.user.keyboard('[Enter]');
|
|
146
|
+
} else await (0, $022fcf8e360befed$exports.pressElement)(this.user, row, interactionType);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Returns the gridlist.
|
|
150
|
+
*/ getGridlist() {
|
|
151
|
+
return this._gridlist;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Returns the gridlist's rows if any.
|
|
155
|
+
*/ getRows() {
|
|
156
|
+
return (0, $aoGu7$testinglibrarydom.within)(this.getGridlist()).queryAllByRole('row');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Returns the gridlist's selected rows if any.
|
|
160
|
+
*/ getSelectedRows() {
|
|
161
|
+
return this.getRows().filter((row)=>row.getAttribute('aria-selected') === 'true');
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Returns the gridlist's cells if any. Can be filtered against a specific row if provided via
|
|
165
|
+
* `element`.
|
|
166
|
+
*/ getCells(opts = {}) {
|
|
167
|
+
let { element: element = this.getGridlist() } = opts;
|
|
168
|
+
return (0, $aoGu7$testinglibrarydom.within)(element).queryAllByRole('gridcell');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
//# sourceMappingURL=gridlist.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/test-utils/src/gridlist.ts"],"sourcesContent":["/*\n * Copyright 2024 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {act} from './act';\nimport {\n Direction,\n GridListTesterOpts,\n GridRowActionOpts,\n ToggleGridRowOpts,\n UserOpts\n} from './types';\nimport {formatTargetNode, getAltKey, getMetaKey, pressElement, triggerLongPress} from './utils';\nimport {within} from '@testing-library/dom';\n\ninterface GridListToggleRowOpts extends ToggleGridRowOpts {}\ninterface GridListRowActionOpts extends GridRowActionOpts {}\n\nexport class GridListTester {\n private user;\n private _interactionType: UserOpts['interactionType'];\n private _advanceTimer: UserOpts['advanceTimer'];\n private _direction: Direction;\n private _gridlist: HTMLElement;\n private _layout: GridListTesterOpts['layout'];\n\n constructor(opts: GridListTesterOpts) {\n let {root, user, interactionType, advanceTimer, direction, layout} = opts;\n this.user = user;\n this._interactionType = interactionType || 'mouse';\n this._advanceTimer = advanceTimer;\n this._direction = direction || 'ltr';\n this._layout = layout || 'stack';\n this._gridlist = root;\n if (root.getAttribute('role') !== 'grid') {\n let gridlist = within(root).queryByRole('grid');\n if (gridlist) {\n this._gridlist = gridlist;\n }\n }\n }\n\n /**\n * Set the interaction type used by the gridlist tester.\n */\n setInteractionType(type: UserOpts['interactionType']): void {\n this._interactionType = type;\n }\n\n /**\n * Returns a row matching the specified index or text content.\n */\n findRow(opts: {indexOrText: number | string}): HTMLElement {\n let {indexOrText} = opts;\n\n let row;\n if (typeof indexOrText === 'number') {\n row = this.getRows()[indexOrText];\n } else if (typeof indexOrText === 'string') {\n row = within(this.getGridlist()!)\n .getByText(indexOrText)\n .closest('[role=row]')! as HTMLElement;\n }\n\n return row;\n }\n\n private async keyboardNavigateToRow(opts: {\n row: HTMLElement;\n selectionOnNav?: 'default' | 'none';\n }) {\n let {row, selectionOnNav = 'default'} = opts;\n let altKey = getAltKey();\n let rows = this.getRows();\n let targetIndex = rows.indexOf(row);\n if (targetIndex === -1) {\n throw new Error('Row provided is not in the gridlist');\n }\n\n if (\n document.activeElement !== this._gridlist &&\n !this._gridlist.contains(document.activeElement)\n ) {\n act(() => this._gridlist.focus());\n }\n\n let focusPrevKey = this._direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';\n if (document.activeElement === this._gridlist) {\n await this.user.keyboard(\n `${selectionOnNav === 'none' ? `[${altKey}>]` : ''}[ArrowDown]${selectionOnNav === 'none' ? `[/${altKey}]` : ''}`\n );\n } else if (\n this._gridlist.contains(document.activeElement) &&\n document.activeElement!.getAttribute('role') !== 'row'\n ) {\n do {\n await this.user.keyboard(`[${focusPrevKey}]`);\n } while (document.activeElement!.getAttribute('role') !== 'row');\n }\n let currIndex = rows.indexOf(document.activeElement as HTMLElement);\n if (currIndex === -1) {\n throw new Error('ActiveElement is not in the gridlist');\n }\n\n if (selectionOnNav === 'none') {\n await this.user.keyboard(`[${altKey}>]`);\n }\n if (this._layout === 'grid') {\n while (document.activeElement !== row) {\n let curr = (document.activeElement as HTMLElement).getBoundingClientRect();\n let target = row.getBoundingClientRect();\n let key: string;\n // basically compare current position with desired position to determine if we need to go up/down/left/right\n // use 1 in the comparison here for subpixels since getBoundingClientRect returns subpixels precision\n if (Math.abs(curr.top - target.top) > 1) {\n key = curr.top < target.top ? 'ArrowDown' : 'ArrowUp';\n } else if (Math.abs(curr.left - target.left) > 1) {\n key = curr.left < target.left ? 'ArrowRight' : 'ArrowLeft';\n } else {\n // if the diff in current vs desired is < 1 but it is claiming we arent focused on the target\n // then we might be in a case where getBoundingClientRect isnt mocked\n throw new Error(\n 'Could not navigate to target row in grid layout. Did the test mock getBoundingClientRect?'\n );\n }\n await this.user.keyboard(`[${key}]`);\n }\n } else {\n let direction = targetIndex > currIndex ? 'down' : 'up';\n for (let i = 0; i < Math.abs(targetIndex - currIndex); i++) {\n await this.user.keyboard(`[${direction === 'down' ? 'ArrowDown' : 'ArrowUp'}]`);\n }\n }\n if (selectionOnNav === 'none') {\n await this.user.keyboard(`[/${altKey}]`);\n }\n }\n\n /**\n * Toggles the selection for the specified gridlist row. Defaults to using the interaction type\n * set on the gridlist tester. Note that this will endevor to always add/remove JUST the provided\n * row to the set of selected rows.\n */\n async toggleRowSelection(opts: GridListToggleRowOpts): Promise<void> {\n let {\n row,\n needsLongPress,\n checkboxSelection = true,\n interactionType = this._interactionType,\n selectionBehavior = 'toggle'\n } = opts;\n\n let altKey = getAltKey();\n let metaKey = getMetaKey();\n\n if (typeof row === 'string' || typeof row === 'number') {\n row = this.findRow({indexOrText: row});\n }\n\n if (!row) {\n throw new Error(`Target row \"${formatTargetNode(opts.row)}\" not found in the gridlist.`);\n }\n\n let rowCheckbox = within(row).queryByRole('checkbox');\n\n if (\n rowCheckbox?.getAttribute('disabled') === '' ||\n row?.getAttribute('aria-disabled') === 'true'\n ) {\n throw new Error(`Cannot toggle selection on disabled row \"${formatTargetNode(opts.row)}\".`);\n }\n\n // this would be better than the check to do nothing in events.ts\n // also, it'd be good to be able to trigger selection on the row instead of having to go to the checkbox directly\n if (interactionType === 'keyboard' && (!checkboxSelection || !rowCheckbox)) {\n await this.keyboardNavigateToRow({\n row,\n selectionOnNav: selectionBehavior === 'replace' ? 'none' : 'default'\n });\n if (selectionBehavior === 'replace') {\n await this.user.keyboard(`[${altKey}>]`);\n }\n await this.user.keyboard('[Space]');\n if (selectionBehavior === 'replace') {\n await this.user.keyboard(`[/${altKey}]`);\n }\n return;\n }\n if (rowCheckbox && checkboxSelection) {\n await pressElement(this.user, rowCheckbox, interactionType);\n } else {\n let cell = within(row).getAllByRole('gridcell')[0];\n if (needsLongPress && interactionType === 'touch') {\n // Note that long press interactions with rows is strictly touch only for grid rows\n await triggerLongPress({\n element: cell,\n advanceTimer: this._advanceTimer!,\n pointerOpts: {pointerType: 'touch'}\n });\n } else {\n if (selectionBehavior === 'replace' && interactionType !== 'touch') {\n await this.user.keyboard(`[${metaKey}>]`);\n }\n await pressElement(this.user, row, interactionType);\n if (selectionBehavior === 'replace' && interactionType !== 'touch') {\n await this.user.keyboard(`[/${metaKey}]`);\n }\n }\n }\n }\n\n /**\n * Triggers the action for the specified gridlist row. Defaults to using the interaction type set\n * on the gridlist tester.\n */\n async triggerRowAction(opts: GridListRowActionOpts): Promise<void> {\n let {row, needsDoubleClick, interactionType = this._interactionType} = opts;\n\n if (typeof row === 'string' || typeof row === 'number') {\n row = this.findRow({indexOrText: row});\n }\n\n if (!row) {\n throw new Error(`Target row \"${formatTargetNode(opts.row)}\" not found in the gridlist.`);\n }\n\n if (row.getAttribute('aria-disabled') === 'true') {\n throw new Error(`Cannot trigger row action on disabled row \"${formatTargetNode(opts.row)}\".`);\n }\n\n if (needsDoubleClick) {\n await this.user.dblClick(row);\n } else if (interactionType === 'keyboard') {\n await this.keyboardNavigateToRow({row, selectionOnNav: 'none'});\n await this.user.keyboard('[Enter]');\n } else {\n await pressElement(this.user, row, interactionType);\n }\n }\n\n /**\n * Returns the gridlist.\n */\n getGridlist(): HTMLElement {\n return this._gridlist;\n }\n\n /**\n * Returns the gridlist's rows if any.\n */\n getRows(): HTMLElement[] {\n return within(this.getGridlist()).queryAllByRole('row');\n }\n\n /**\n * Returns the gridlist's selected rows if any.\n */\n getSelectedRows(): HTMLElement[] {\n return this.getRows().filter(row => row.getAttribute('aria-selected') === 'true');\n }\n\n /**\n * Returns the gridlist's cells if any. Can be filtered against a specific row if provided via\n * `element`.\n */\n getCells(opts: {element?: HTMLElement} = {}): HTMLElement[] {\n let {element = this.getGridlist()} = opts;\n return within(element).queryAllByRole('gridcell');\n }\n}\n"],"names":[],"version":3,"file":"gridlist.cjs.map"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {act as $1350703ef3ac1acc$export$3ba232387fd5d6dd} from "./act.js";
|
|
2
|
+
import {formatTargetNode as $b4e037a2907521c6$export$bc3bc4a9206bf789, getAltKey as $b4e037a2907521c6$export$6fc0ccaebd758b9d, getMetaKey as $b4e037a2907521c6$export$7943c508934e27ce, pressElement as $b4e037a2907521c6$export$6ffa3eb717517feb, triggerLongPress as $b4e037a2907521c6$export$3a22a5a9bc0fd3b} from "./utils.js";
|
|
3
|
+
import {within as $g8PMJ$within} from "@testing-library/dom";
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Copyright 2024 Adobe. All rights reserved.
|
|
7
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
9
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
12
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
13
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
14
|
+
* governing permissions and limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class $ed42840525c46506$export$93a85aaed9fabd83 {
|
|
19
|
+
constructor(opts){
|
|
20
|
+
let { root: root, user: user, interactionType: interactionType, advanceTimer: advanceTimer, direction: direction, layout: layout } = opts;
|
|
21
|
+
this.user = user;
|
|
22
|
+
this._interactionType = interactionType || 'mouse';
|
|
23
|
+
this._advanceTimer = advanceTimer;
|
|
24
|
+
this._direction = direction || 'ltr';
|
|
25
|
+
this._layout = layout || 'stack';
|
|
26
|
+
this._gridlist = root;
|
|
27
|
+
if (root.getAttribute('role') !== 'grid') {
|
|
28
|
+
let gridlist = (0, $g8PMJ$within)(root).queryByRole('grid');
|
|
29
|
+
if (gridlist) this._gridlist = gridlist;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set the interaction type used by the gridlist tester.
|
|
34
|
+
*/ setInteractionType(type) {
|
|
35
|
+
this._interactionType = type;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns a row matching the specified index or text content.
|
|
39
|
+
*/ findRow(opts) {
|
|
40
|
+
let { indexOrText: indexOrText } = opts;
|
|
41
|
+
let row;
|
|
42
|
+
if (typeof indexOrText === 'number') row = this.getRows()[indexOrText];
|
|
43
|
+
else if (typeof indexOrText === 'string') row = (0, $g8PMJ$within)(this.getGridlist()).getByText(indexOrText).closest('[role=row]');
|
|
44
|
+
return row;
|
|
45
|
+
}
|
|
46
|
+
async keyboardNavigateToRow(opts) {
|
|
47
|
+
let { row: row, selectionOnNav: selectionOnNav = 'default' } = opts;
|
|
48
|
+
let altKey = (0, $b4e037a2907521c6$export$6fc0ccaebd758b9d)();
|
|
49
|
+
let rows = this.getRows();
|
|
50
|
+
let targetIndex = rows.indexOf(row);
|
|
51
|
+
if (targetIndex === -1) throw new Error('Row provided is not in the gridlist');
|
|
52
|
+
if (document.activeElement !== this._gridlist && !this._gridlist.contains(document.activeElement)) (0, $1350703ef3ac1acc$export$3ba232387fd5d6dd)(()=>this._gridlist.focus());
|
|
53
|
+
let focusPrevKey = this._direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
|
|
54
|
+
if (document.activeElement === this._gridlist) await this.user.keyboard(`${selectionOnNav === 'none' ? `[${altKey}>]` : ''}[ArrowDown]${selectionOnNav === 'none' ? `[/${altKey}]` : ''}`);
|
|
55
|
+
else if (this._gridlist.contains(document.activeElement) && document.activeElement.getAttribute('role') !== 'row') do await this.user.keyboard(`[${focusPrevKey}]`);
|
|
56
|
+
while (document.activeElement.getAttribute('role') !== 'row');
|
|
57
|
+
let currIndex = rows.indexOf(document.activeElement);
|
|
58
|
+
if (currIndex === -1) throw new Error('ActiveElement is not in the gridlist');
|
|
59
|
+
if (selectionOnNav === 'none') await this.user.keyboard(`[${altKey}>]`);
|
|
60
|
+
if (this._layout === 'grid') while(document.activeElement !== row){
|
|
61
|
+
let curr = document.activeElement.getBoundingClientRect();
|
|
62
|
+
let target = row.getBoundingClientRect();
|
|
63
|
+
let key;
|
|
64
|
+
// basically compare current position with desired position to determine if we need to go up/down/left/right
|
|
65
|
+
// use 1 in the comparison here for subpixels since getBoundingClientRect returns subpixels precision
|
|
66
|
+
if (Math.abs(curr.top - target.top) > 1) key = curr.top < target.top ? 'ArrowDown' : 'ArrowUp';
|
|
67
|
+
else if (Math.abs(curr.left - target.left) > 1) key = curr.left < target.left ? 'ArrowRight' : 'ArrowLeft';
|
|
68
|
+
else // if the diff in current vs desired is < 1 but it is claiming we arent focused on the target
|
|
69
|
+
// then we might be in a case where getBoundingClientRect isnt mocked
|
|
70
|
+
throw new Error('Could not navigate to target row in grid layout. Did the test mock getBoundingClientRect?');
|
|
71
|
+
await this.user.keyboard(`[${key}]`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
let direction = targetIndex > currIndex ? 'down' : 'up';
|
|
75
|
+
for(let i = 0; i < Math.abs(targetIndex - currIndex); i++)await this.user.keyboard(`[${direction === 'down' ? 'ArrowDown' : 'ArrowUp'}]`);
|
|
76
|
+
}
|
|
77
|
+
if (selectionOnNav === 'none') await this.user.keyboard(`[/${altKey}]`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Toggles the selection for the specified gridlist row. Defaults to using the interaction type
|
|
81
|
+
* set on the gridlist tester. Note that this will endevor to always add/remove JUST the provided
|
|
82
|
+
* row to the set of selected rows.
|
|
83
|
+
*/ async toggleRowSelection(opts) {
|
|
84
|
+
let { row: row, needsLongPress: needsLongPress, checkboxSelection: checkboxSelection = true, interactionType: interactionType = this._interactionType, selectionBehavior: selectionBehavior = 'toggle' } = opts;
|
|
85
|
+
let altKey = (0, $b4e037a2907521c6$export$6fc0ccaebd758b9d)();
|
|
86
|
+
let metaKey = (0, $b4e037a2907521c6$export$7943c508934e27ce)();
|
|
87
|
+
if (typeof row === 'string' || typeof row === 'number') row = this.findRow({
|
|
88
|
+
indexOrText: row
|
|
89
|
+
});
|
|
90
|
+
if (!row) throw new Error(`Target row "${(0, $b4e037a2907521c6$export$bc3bc4a9206bf789)(opts.row)}" not found in the gridlist.`);
|
|
91
|
+
let rowCheckbox = (0, $g8PMJ$within)(row).queryByRole('checkbox');
|
|
92
|
+
if (rowCheckbox?.getAttribute('disabled') === '' || row?.getAttribute('aria-disabled') === 'true') throw new Error(`Cannot toggle selection on disabled row "${(0, $b4e037a2907521c6$export$bc3bc4a9206bf789)(opts.row)}".`);
|
|
93
|
+
// this would be better than the check to do nothing in events.ts
|
|
94
|
+
// also, it'd be good to be able to trigger selection on the row instead of having to go to the checkbox directly
|
|
95
|
+
if (interactionType === 'keyboard' && (!checkboxSelection || !rowCheckbox)) {
|
|
96
|
+
await this.keyboardNavigateToRow({
|
|
97
|
+
row: row,
|
|
98
|
+
selectionOnNav: selectionBehavior === 'replace' ? 'none' : 'default'
|
|
99
|
+
});
|
|
100
|
+
if (selectionBehavior === 'replace') await this.user.keyboard(`[${altKey}>]`);
|
|
101
|
+
await this.user.keyboard('[Space]');
|
|
102
|
+
if (selectionBehavior === 'replace') await this.user.keyboard(`[/${altKey}]`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (rowCheckbox && checkboxSelection) await (0, $b4e037a2907521c6$export$6ffa3eb717517feb)(this.user, rowCheckbox, interactionType);
|
|
106
|
+
else {
|
|
107
|
+
let cell = (0, $g8PMJ$within)(row).getAllByRole('gridcell')[0];
|
|
108
|
+
if (needsLongPress && interactionType === 'touch') // Note that long press interactions with rows is strictly touch only for grid rows
|
|
109
|
+
await (0, $b4e037a2907521c6$export$3a22a5a9bc0fd3b)({
|
|
110
|
+
element: cell,
|
|
111
|
+
advanceTimer: this._advanceTimer,
|
|
112
|
+
pointerOpts: {
|
|
113
|
+
pointerType: 'touch'
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
else {
|
|
117
|
+
if (selectionBehavior === 'replace' && interactionType !== 'touch') await this.user.keyboard(`[${metaKey}>]`);
|
|
118
|
+
await (0, $b4e037a2907521c6$export$6ffa3eb717517feb)(this.user, row, interactionType);
|
|
119
|
+
if (selectionBehavior === 'replace' && interactionType !== 'touch') await this.user.keyboard(`[/${metaKey}]`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Triggers the action for the specified gridlist row. Defaults to using the interaction type set
|
|
125
|
+
* on the gridlist tester.
|
|
126
|
+
*/ async triggerRowAction(opts) {
|
|
127
|
+
let { row: row, needsDoubleClick: needsDoubleClick, interactionType: interactionType = this._interactionType } = opts;
|
|
128
|
+
if (typeof row === 'string' || typeof row === 'number') row = this.findRow({
|
|
129
|
+
indexOrText: row
|
|
130
|
+
});
|
|
131
|
+
if (!row) throw new Error(`Target row "${(0, $b4e037a2907521c6$export$bc3bc4a9206bf789)(opts.row)}" not found in the gridlist.`);
|
|
132
|
+
if (row.getAttribute('aria-disabled') === 'true') throw new Error(`Cannot trigger row action on disabled row "${(0, $b4e037a2907521c6$export$bc3bc4a9206bf789)(opts.row)}".`);
|
|
133
|
+
if (needsDoubleClick) await this.user.dblClick(row);
|
|
134
|
+
else if (interactionType === 'keyboard') {
|
|
135
|
+
await this.keyboardNavigateToRow({
|
|
136
|
+
row: row,
|
|
137
|
+
selectionOnNav: 'none'
|
|
138
|
+
});
|
|
139
|
+
await this.user.keyboard('[Enter]');
|
|
140
|
+
} else await (0, $b4e037a2907521c6$export$6ffa3eb717517feb)(this.user, row, interactionType);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Returns the gridlist.
|
|
144
|
+
*/ getGridlist() {
|
|
145
|
+
return this._gridlist;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns the gridlist's rows if any.
|
|
149
|
+
*/ getRows() {
|
|
150
|
+
return (0, $g8PMJ$within)(this.getGridlist()).queryAllByRole('row');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Returns the gridlist's selected rows if any.
|
|
154
|
+
*/ getSelectedRows() {
|
|
155
|
+
return this.getRows().filter((row)=>row.getAttribute('aria-selected') === 'true');
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Returns the gridlist's cells if any. Can be filtered against a specific row if provided via
|
|
159
|
+
* `element`.
|
|
160
|
+
*/ getCells(opts = {}) {
|
|
161
|
+
let { element: element = this.getGridlist() } = opts;
|
|
162
|
+
return (0, $g8PMJ$within)(element).queryAllByRole('gridcell');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
export {$ed42840525c46506$export$93a85aaed9fabd83 as GridListTester};
|
|
168
|
+
//# sourceMappingURL=gridlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/test-utils/src/gridlist.ts"],"sourcesContent":["/*\n * Copyright 2024 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {act} from './act';\nimport {\n Direction,\n GridListTesterOpts,\n GridRowActionOpts,\n ToggleGridRowOpts,\n UserOpts\n} from './types';\nimport {formatTargetNode, getAltKey, getMetaKey, pressElement, triggerLongPress} from './utils';\nimport {within} from '@testing-library/dom';\n\ninterface GridListToggleRowOpts extends ToggleGridRowOpts {}\ninterface GridListRowActionOpts extends GridRowActionOpts {}\n\nexport class GridListTester {\n private user;\n private _interactionType: UserOpts['interactionType'];\n private _advanceTimer: UserOpts['advanceTimer'];\n private _direction: Direction;\n private _gridlist: HTMLElement;\n private _layout: GridListTesterOpts['layout'];\n\n constructor(opts: GridListTesterOpts) {\n let {root, user, interactionType, advanceTimer, direction, layout} = opts;\n this.user = user;\n this._interactionType = interactionType || 'mouse';\n this._advanceTimer = advanceTimer;\n this._direction = direction || 'ltr';\n this._layout = layout || 'stack';\n this._gridlist = root;\n if (root.getAttribute('role') !== 'grid') {\n let gridlist = within(root).queryByRole('grid');\n if (gridlist) {\n this._gridlist = gridlist;\n }\n }\n }\n\n /**\n * Set the interaction type used by the gridlist tester.\n */\n setInteractionType(type: UserOpts['interactionType']): void {\n this._interactionType = type;\n }\n\n /**\n * Returns a row matching the specified index or text content.\n */\n findRow(opts: {indexOrText: number | string}): HTMLElement {\n let {indexOrText} = opts;\n\n let row;\n if (typeof indexOrText === 'number') {\n row = this.getRows()[indexOrText];\n } else if (typeof indexOrText === 'string') {\n row = within(this.getGridlist()!)\n .getByText(indexOrText)\n .closest('[role=row]')! as HTMLElement;\n }\n\n return row;\n }\n\n private async keyboardNavigateToRow(opts: {\n row: HTMLElement;\n selectionOnNav?: 'default' | 'none';\n }) {\n let {row, selectionOnNav = 'default'} = opts;\n let altKey = getAltKey();\n let rows = this.getRows();\n let targetIndex = rows.indexOf(row);\n if (targetIndex === -1) {\n throw new Error('Row provided is not in the gridlist');\n }\n\n if (\n document.activeElement !== this._gridlist &&\n !this._gridlist.contains(document.activeElement)\n ) {\n act(() => this._gridlist.focus());\n }\n\n let focusPrevKey = this._direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';\n if (document.activeElement === this._gridlist) {\n await this.user.keyboard(\n `${selectionOnNav === 'none' ? `[${altKey}>]` : ''}[ArrowDown]${selectionOnNav === 'none' ? `[/${altKey}]` : ''}`\n );\n } else if (\n this._gridlist.contains(document.activeElement) &&\n document.activeElement!.getAttribute('role') !== 'row'\n ) {\n do {\n await this.user.keyboard(`[${focusPrevKey}]`);\n } while (document.activeElement!.getAttribute('role') !== 'row');\n }\n let currIndex = rows.indexOf(document.activeElement as HTMLElement);\n if (currIndex === -1) {\n throw new Error('ActiveElement is not in the gridlist');\n }\n\n if (selectionOnNav === 'none') {\n await this.user.keyboard(`[${altKey}>]`);\n }\n if (this._layout === 'grid') {\n while (document.activeElement !== row) {\n let curr = (document.activeElement as HTMLElement).getBoundingClientRect();\n let target = row.getBoundingClientRect();\n let key: string;\n // basically compare current position with desired position to determine if we need to go up/down/left/right\n // use 1 in the comparison here for subpixels since getBoundingClientRect returns subpixels precision\n if (Math.abs(curr.top - target.top) > 1) {\n key = curr.top < target.top ? 'ArrowDown' : 'ArrowUp';\n } else if (Math.abs(curr.left - target.left) > 1) {\n key = curr.left < target.left ? 'ArrowRight' : 'ArrowLeft';\n } else {\n // if the diff in current vs desired is < 1 but it is claiming we arent focused on the target\n // then we might be in a case where getBoundingClientRect isnt mocked\n throw new Error(\n 'Could not navigate to target row in grid layout. Did the test mock getBoundingClientRect?'\n );\n }\n await this.user.keyboard(`[${key}]`);\n }\n } else {\n let direction = targetIndex > currIndex ? 'down' : 'up';\n for (let i = 0; i < Math.abs(targetIndex - currIndex); i++) {\n await this.user.keyboard(`[${direction === 'down' ? 'ArrowDown' : 'ArrowUp'}]`);\n }\n }\n if (selectionOnNav === 'none') {\n await this.user.keyboard(`[/${altKey}]`);\n }\n }\n\n /**\n * Toggles the selection for the specified gridlist row. Defaults to using the interaction type\n * set on the gridlist tester. Note that this will endevor to always add/remove JUST the provided\n * row to the set of selected rows.\n */\n async toggleRowSelection(opts: GridListToggleRowOpts): Promise<void> {\n let {\n row,\n needsLongPress,\n checkboxSelection = true,\n interactionType = this._interactionType,\n selectionBehavior = 'toggle'\n } = opts;\n\n let altKey = getAltKey();\n let metaKey = getMetaKey();\n\n if (typeof row === 'string' || typeof row === 'number') {\n row = this.findRow({indexOrText: row});\n }\n\n if (!row) {\n throw new Error(`Target row \"${formatTargetNode(opts.row)}\" not found in the gridlist.`);\n }\n\n let rowCheckbox = within(row).queryByRole('checkbox');\n\n if (\n rowCheckbox?.getAttribute('disabled') === '' ||\n row?.getAttribute('aria-disabled') === 'true'\n ) {\n throw new Error(`Cannot toggle selection on disabled row \"${formatTargetNode(opts.row)}\".`);\n }\n\n // this would be better than the check to do nothing in events.ts\n // also, it'd be good to be able to trigger selection on the row instead of having to go to the checkbox directly\n if (interactionType === 'keyboard' && (!checkboxSelection || !rowCheckbox)) {\n await this.keyboardNavigateToRow({\n row,\n selectionOnNav: selectionBehavior === 'replace' ? 'none' : 'default'\n });\n if (selectionBehavior === 'replace') {\n await this.user.keyboard(`[${altKey}>]`);\n }\n await this.user.keyboard('[Space]');\n if (selectionBehavior === 'replace') {\n await this.user.keyboard(`[/${altKey}]`);\n }\n return;\n }\n if (rowCheckbox && checkboxSelection) {\n await pressElement(this.user, rowCheckbox, interactionType);\n } else {\n let cell = within(row).getAllByRole('gridcell')[0];\n if (needsLongPress && interactionType === 'touch') {\n // Note that long press interactions with rows is strictly touch only for grid rows\n await triggerLongPress({\n element: cell,\n advanceTimer: this._advanceTimer!,\n pointerOpts: {pointerType: 'touch'}\n });\n } else {\n if (selectionBehavior === 'replace' && interactionType !== 'touch') {\n await this.user.keyboard(`[${metaKey}>]`);\n }\n await pressElement(this.user, row, interactionType);\n if (selectionBehavior === 'replace' && interactionType !== 'touch') {\n await this.user.keyboard(`[/${metaKey}]`);\n }\n }\n }\n }\n\n /**\n * Triggers the action for the specified gridlist row. Defaults to using the interaction type set\n * on the gridlist tester.\n */\n async triggerRowAction(opts: GridListRowActionOpts): Promise<void> {\n let {row, needsDoubleClick, interactionType = this._interactionType} = opts;\n\n if (typeof row === 'string' || typeof row === 'number') {\n row = this.findRow({indexOrText: row});\n }\n\n if (!row) {\n throw new Error(`Target row \"${formatTargetNode(opts.row)}\" not found in the gridlist.`);\n }\n\n if (row.getAttribute('aria-disabled') === 'true') {\n throw new Error(`Cannot trigger row action on disabled row \"${formatTargetNode(opts.row)}\".`);\n }\n\n if (needsDoubleClick) {\n await this.user.dblClick(row);\n } else if (interactionType === 'keyboard') {\n await this.keyboardNavigateToRow({row, selectionOnNav: 'none'});\n await this.user.keyboard('[Enter]');\n } else {\n await pressElement(this.user, row, interactionType);\n }\n }\n\n /**\n * Returns the gridlist.\n */\n getGridlist(): HTMLElement {\n return this._gridlist;\n }\n\n /**\n * Returns the gridlist's rows if any.\n */\n getRows(): HTMLElement[] {\n return within(this.getGridlist()).queryAllByRole('row');\n }\n\n /**\n * Returns the gridlist's selected rows if any.\n */\n getSelectedRows(): HTMLElement[] {\n return this.getRows().filter(row => row.getAttribute('aria-selected') === 'true');\n }\n\n /**\n * Returns the gridlist's cells if any. Can be filtered against a specific row if provided via\n * `element`.\n */\n getCells(opts: {element?: HTMLElement} = {}): HTMLElement[] {\n let {element = this.getGridlist()} = opts;\n return within(element).queryAllByRole('gridcell');\n }\n}\n"],"names":[],"version":3,"file":"gridlist.js.map"}
|