+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the first checkbox
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Navigate to the first checkbox. Note: it should be in the partially checked state. You may need to press certain commands (such as Down Arrow) multiple times to hear all information relating to the checkbox. using the following commands:
+
+
X
+
F
+
Tab
+
Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'checkbox' is conveyed
+
required
Name 'All condiments' is conveyed
+
required
State of the checkbox (partially checked) is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the first checkbox
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Navigate to the first checkbox. Note: it should be in the partially checked state. You may need to press certain commands (such as Down Arrow) multiple times to hear all information relating to the checkbox. using the following commands:
+
+
X
+
F
+
Tab
+
Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'checkbox' is conveyed
+
required
Name 'All condiments' is conveyed
+
required
State of the checkbox (partially checked) is conveyed
+
+
+
Test 2: Navigate backwards to a partially checked checkbox in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the first checkbox
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate to the first checkbox. Note: it should be in the partially checked state. You may need to press certain commands (such as Control+Option+Right) multiple times to hear all information relating to the checkbox. using the following commands:
+
+
Tab
+
Control+Option+Right
+
Control+Option+Command+J
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'checkbox' is conveyed
+
required
Name 'All condiments' is conveyed
+
required
State of the checkbox (partially checked) is conveyed
+
+
+
Test 6: Navigate backwards to a partially checked checkbox
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the first checkbox
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
When the reading cursor is on the first checkbox, read the first checkbox. Note: it should be in the partially checked state. using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'checkbox' is conveyed
+
required
Name 'All condiments' is conveyed
+
required
State of the checkbox (partially checked) is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the first checkbox
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
When the reading cursor is on the first checkbox, read the first checkbox. Note: it should be in the partially checked state. using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'checkbox' is conveyed
+
required
Name 'All condiments' is conveyed
+
required
State of the checkbox (partially checked) is conveyed
+
+
+
Test 14: Read a partially checked checkbox in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the first checkbox
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate forwards into the checkbox group. Note: You may need to press certain commands (such as Control+Option+Command+J) multiple times to hear all information relating to the checkbox. using the following commands:
+
+
Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'group' is conveyed
+
required
Group name 'Sandwich Condiments' is conveyed
+
required
The group boundary is conveyed
+
+
+
Test 24: Navigate backwards out of a checkbox group
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Navigate from outside the checkbox group to a checkbox inside the group. Test both navigating forward into the start and backward into the end. using the following commands:
+
+
X
+
Shift+X
+
F
+
Shift+F
+
Tab
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
required
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
+
NVDA
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Navigate from outside the checkbox group to a checkbox inside the group. Test both navigating forward into the start and backward into the end. using the following commands:
+
+
X
+
Shift+X
+
F
+
Shift+F
+
Tab
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
required
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
Test 22: Navigate into a checkbox group in interaction mode
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Navigate from outside the checkbox group to a checkbox inside the group. Test both navigating forward into the start and backward into the end. using the following commands:
+
+
Tab
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
required
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
+
NVDA
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Navigate from outside the checkbox group to a checkbox inside the group. Test both navigating forward into the start and backward into the end. using the following commands:
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate from outside the checkbox group to a checkbox inside the group. Test both navigating forward into the start and backward into the end. using the following commands:
+
+
Tab
+
Shift+Tab
+
Control+Option+Right
+
Ctrl+Option+Left
+
Control+Option+Command+J
+
Shift+Control+Option+Command+J
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
required
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
Test 24: Navigate out of a checkbox group in reading mode
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Navigate from a checkbox inside the checkbox group to an element outside the checkbox group. Test both navigating forward out of the end and backward out of the start. using the following commands:
+
+
Tab
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
optional
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
+
NVDA
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Navigate from a checkbox inside the checkbox group to an element outside the checkbox group. Test both navigating forward out of the end and backward out of the start. using the following commands:
+
+
Tab
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
optional
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
Test 25: Navigate out of a checkbox group in interaction mode
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Navigate from a checkbox inside the checkbox group to an element outside the checkbox group. Test both navigating forward out of the end and backward out of the start. using the following commands:
+
+
Tab
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
optional
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
+
NVDA
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Navigate from a checkbox inside the checkbox group to an element outside the checkbox group. Test both navigating forward out of the end and backward out of the start. using the following commands:
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate from a checkbox inside the checkbox group to an element outside the checkbox group. Test both navigating forward out of the end and backward out of the start. using the following commands:
+
+
Tab
+
Shift+Tab
+
Control+Option+Right
+
Ctrl+Option+Left
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
optional
Role 'group' is conveyed
+
optional
Group name 'Sandwich Condiments' is conveyed
+
optional
The group boundary is conveyed
+
+
+
+
+
diff --git a/build/review/combobox-autocomplete-both.html b/build/review/combobox-autocomplete-both.html
new file mode 100644
index 000000000..3c5c97148
--- /dev/null
+++ b/build/review/combobox-autocomplete-both.html
@@ -0,0 +1,2484 @@
+
+
+
+
+ Test plan review for pattern: combobox-autocomplete-both
+
+
+
+
+
+
Test plan review for pattern: combobox-autocomplete-both (30 tests)
+
+
+
+
Test 1: Navigate to empty, editable combobox in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the Combobox
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Navigate to the Combobox. Note: it should be in the collapsed state. You may need to press certain commands (such as Down Arrow) multiple times to hear all information relating to the combobox. using the following commands:
+
+
F
+
C
+
Tab
+
Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'combobox' is conveyed
+
required
Name 'Favorite Fruit' is conveyed
+
required
Text of the selected option ('Choose a Fruit') is conveyed
+
required
State of the combobox (collapsed) is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the Combobox
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Navigate to the Combobox. Note: it should be in the collapsed state. You may need to press certain commands (such as Down Arrow) multiple times to hear all information relating to the combobox. using the following commands:
+
+
F
+
C
+
Tab
+
Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'combobox' is conveyed
+
required
Name 'Favorite Fruit' is conveyed
+
required
Text of the selected option ('Choose a Fruit') is conveyed
+
required
State of the combobox (collapsed) is conveyed
+
+
+
Test 2: Navigate backwards to a collapsed select-only combobox in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link after the Combobox
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Navigate to the Combobox. Note: it should be in the collapsed state. You may need to press certain commands (such as Up Arrow) multiple times to hear all information relating to the combobox. using the following commands:
+
+
Shift+F
+
Shift+C
+
Shift+Tab
+
Up Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'combobox' is conveyed
+
required
Name 'Favorite Fruit' is conveyed
+
required
Text of the selected option ('Choose a Fruit') is conveyed
+
required
State of the combobox (collapsed) is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link after the Combobox
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Navigate to the Combobox. Note: it should be in the collapsed state. You may need to press certain commands (such as Up Arrow) multiple times to hear all information relating to the combobox. using the following commands:
+
+
Shift+F
+
Shift+C
+
Shift+Tab
+
Up Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'combobox' is conveyed
+
required
Name 'Favorite Fruit' is conveyed
+
required
Text of the selected option ('Choose a Fruit') is conveyed
+
required
State of the combobox (collapsed) is conveyed
+
+
+
Test 3: Navigate forwards to a collapsed select-only combobox in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the Combobox
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate to the Combobox. Note: it should be in the collapsed state. You may need to press certain commands (such as Control+Option+Right) multiple times to hear all information relating to the combobox. using the following commands:
+
+
Control+Option+Right
+
Tab
+
Control+Option+Command+J
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'combobox' is conveyed
+
required
Name 'Favorite Fruit' is conveyed
+
required
Text of the selected option ('Choose a Fruit') is conveyed
+
required
State of the combobox (collapsed) is conveyed
+
+
+
Test 6: Navigate backwards to a collapsed select-only combobox
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link after the Combobox
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate to the Combobox. Note: it should be in the collapsed state. You may need to press certain commands (such as Control+Option+Left Arrow) multiple times to hear all information relating to the combobox. using the following commands:
+
+
Ctrl+Option+Left
+
Shift+Tab
+
Shift+Control+Option+Command+J
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'combobox' is conveyed
+
required
Name 'Favorite Fruit' is conveyed
+
required
Text of the selected option ('Choose a Fruit') is conveyed
+
required
State of the combobox (collapsed) is conveyed
+
+
+
Test 7: Read information about a collapsed select-only combobox in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Open "Style/Color" menu with down arrow. |
+Navigate to 'Italic' menu item and check it by pressing enter. (the menu will close.) |
+Re-open "Style/Color" menu with down arrow. |
+Navigate to 'Italic' menu item checkbox using the following commands:
+
+
Up Arrow / Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+
optional
The name and role of the 'Font Style' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Open "Style/Color" menu with down arrow. |
+Navigate to 'Italic' menu item and check it by pressing enter. (the menu will close.) |
+Re-open "Style/Color" menu with down arrow. |
+Navigate to 'Italic' menu item checkbox using the following commands:
+
+
Up Arrow / Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+
optional
The name and role of the 'Font Style' group are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Open "Style/Color" menu with down arrow. |
+Navigate to 'Italic' menu item and check it by pressing enter. (the menu will close.) |
+Re-open "Style/Color" menu with down arrow. |
+Navigate to 'Italic' menu item checkbox using the following commands:
+
+
Up Arrow / Down Arrow
+
Control+Option+Right / Ctrl+Option+Left
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+
optional
The name and role of the 'Font Style' group are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Blue' menu item radio with down arrow. | Activate the screen reader reading cursor. | Read the 'Blue' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Blue' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Blue' menu item radio with down arrow. | Activate the screen reader reading cursor. | Read the 'Blue' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Blue' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
Test 27: Read unchecked menu item radio in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Blue' menu item radio with down arrow. | Read the 'Blue' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Blue' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Blue' menu item radio with down arrow. | Read the 'Blue' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Blue' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Blue' menu item radio with down arrow. | Read the 'Blue' menu item radio using the following commands:
+
+
Control+Option+F3
+
Control+Option+F4
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Blue' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
Test 29: Read checked menu item radio in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Black' menu item radio with down arrow. | Activate the screen reader reading cursor. | Read the 'Black' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Black' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Black' menu item radio with down arrow. | Activate the screen reader reading cursor. | Read the 'Black' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Black' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
Test 30: Read checked menu item radio in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Black' menu item radio with down arrow. | Read the 'Black' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Black' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Black' menu item radio with down arrow. | Read the 'Black' menu item radio using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Black' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Black' menu item radio with down arrow. | Read the 'Black' menu item radio using the following commands:
+
+
Control+Option+F3
+
Control+Option+F4
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Black' is conveyed
+
required
The role 'menu item radio' is conveyed
+
required
The checked state is conveyed
+
optional
The position of the item and number of items in the 'Text Color' group are conveyed
+
required
The name and role of the 'Text Color' group are conveyed
+
+
+
Test 32: Read unchecked menu item checkbox in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item checkbox with down arrow. | Activate the screen reader reading cursor. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item checkbox with down arrow. | Activate the screen reader reading cursor. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+
+
+
Test 33: Read unchecked menu item checkbox in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Italic' menu item checkbox with down arrow. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Italic' menu item checkbox with down arrow. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Open "Style/Color" menu with down arrow. | Navigate to 'Italic' menu item checkbox with down arrow. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Control+Option+F3
+
Control+Option+F4
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+
+
+
Test 35: Read checked menu item checkbox in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item checkbox and check it by pressing enter. (the menu will close.) | Re-open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item checkbox. | Activate the screen reader reading cursor. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Style/Color' menu item with right arrow. | Open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item checkbox and check it by pressing enter. (the menu will close.) | Re-open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item checkbox. | Activate the screen reader reading cursor. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+
+
+
Test 36: Read checked menu item checkbox in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item and check it by pressing enter. (the menu will close.) | Re-open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item and check it by pressing enter. (the menu will close.) | Re-open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Style/Color' menu item
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item and check it by pressing enter. (the menu will close.) | Re-open 'Style/Color' menu with down arrow. | Navigate to 'Italic' menu item. | Read the 'Italic' menu item checkbox using the following commands:
+
+
Control+Option+F3
+
Control+Option+F4
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Italic' is conveyed
+
required
The role 'menu item checkbox' is conveyed
+
required
The unchecked state is conveyed
+
optional
The position of the item and number of items in the 'Font Style ' group are conveyed
+
required
The name and role of the 'Font Style' group are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Size' menu item with right arrow. | Open 'Size' menu with down arrow. | Navigate to 'X-Small' with down arrow. | Activate 'X-Small'. with Enter. | Open 'Size' menu with down arrow. | Activate the screen reader reading cursor. | Read the 'Smaller' menu item using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Smaller' is conveyed
+
optional
The role 'menu item' is conveyed
+
required
The disabled state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link just before the menubar
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Tab to 'Text Formatting' menubar. | Navigate to 'Size' menu item with right arrow. | Open 'Size' menu with down arrow. | Navigate to 'X-Small' with down arrow. | Activate 'X-Small'. with Enter. | Open 'Size' menu with down arrow. | Activate the screen reader reading cursor. | Read the 'Smaller' menu item using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Smaller' is conveyed
+
optional
The role 'menu item' is conveyed
+
required
The disabled state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+
+
+
Test 39: Read disabled menu item in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Size' menu item
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Open 'Size' menu with down arrow. | Navigate to 'X-Small' with down arrow. | Activate 'X-Small'. with Enter. | Open 'Size' menu with down arrow. | Read the 'Smaller' menu item using the following commands:
+
+
Insert+Tab
+
Insert+Up
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Smaller' is conveyed
+
optional
The role 'menu item' is conveyed
+
required
The disabled state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Size' menu item
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Open 'Size' menu with down arrow. | Navigate to 'X-Small' with down arrow. | Activate 'X-Small'. with Enter. | Open 'Size' menu with down arrow. | Read the 'Smaller' menu item using the following commands:
+
+
Insert+Tab
+
Insert+Numpad 5 (or CapsLock+Numpad 5)
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Smaller' is conveyed
+
optional
The role 'menu item' is conveyed
+
required
The disabled state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the 'Size' menu item
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Open 'Size' menu with down arrow. | Navigate to 'X-Small' with down arrow. | Activate 'X-Small'. with Enter. | Open 'Size' menu with down arrow. | Read the 'Smaller' menu item using the following commands:
+
+
Control+Option+F3
+
Control+Option+F4
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
The name 'Smaller' is conveyed
+
optional
The role 'menu item' is conveyed
+
required
The disabled state is conveyed
+
optional
The position of the item and number of items in the menu are conveyed
+
+
+
+
+
diff --git a/build/review/modal-dialog.html b/build/review/modal-dialog.html
new file mode 100644
index 000000000..3f37ce4b6
--- /dev/null
+++ b/build/review/modal-dialog.html
@@ -0,0 +1,2227 @@
+
+
+
+
+ Test plan review for pattern: modal-dialog
+
+
+
+
+
+
Test plan review for pattern: modal-dialog (29 tests)
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the first input
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
With focus on the first focusable element inside a modal dialog, navigate to the last focusable element in the same dialog. using the following commands:
+
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'button' is conveyed
+
required
Name 'Cancel' is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the first input
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
With focus on the first focusable element inside a modal dialog, navigate to the last focusable element in the same dialog. using the following commands:
+
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'button' is conveyed
+
required
Name 'Cancel' is conveyed
+
+
+
Test 11: Navigate to the last focusable element in a modal dialog
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the first input
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
With focus on the first focusable element inside a modal dialog, navigate to the last focusable element in the same dialog. using the following commands:
+
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'button' is conveyed
+
required
Name 'Cancel' is conveyed
+
+
+
Test 12: Navigate to the first focusable element in a modal dialog in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Cancel' button
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
With focus on the last focusable element inside a modal dialog, navigate to the first focusable element in the same dialog. using the following commands:
+
+
Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Name 'Street' is conveyed
+
required
The ability to enter text is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Cancel' button
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
With focus on the last focusable element inside a modal dialog, navigate to the first focusable element in the same dialog. using the following commands:
+
+
Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Name 'Street' is conveyed
+
required
The ability to enter text is conveyed
+
+
+
Test 13: Navigate to the first focusable element in a modal dialog
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Cancel' button
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
With focus on the last focusable element inside a modal dialog, navigate to the first focusable element in the same dialog. using the following commands:
+
+
Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Name 'Street' is conveyed
+
required
The ability to enter text is conveyed
+
+
+
Test 14: Navigate to the beginning of a modal dialog in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Add' button
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
With the reading cursor on the 'Add' button inside a modal dialog, open the nested 'Address Added' dialog. using the following commands:
+
+
Space
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'Address Added' is conveyed
+
required
Description of the dialog ('The address you provided has been added to your list of delivery addresses. It is ready for immediate use. If you wish to remove it, you can do so from your profile.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('OK') is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Add' button
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
With the reading cursor on the 'Add' button inside a modal dialog, open the nested 'Address Added' dialog. using the following commands:
+
+
Space
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'Address Added' is conveyed
+
required
Description of the dialog ('The address you provided has been added to your list of delivery addresses. It is ready for immediate use. If you wish to remove it, you can do so from your profile.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('OK') is conveyed
+
+
+
Test 19: Open a nested modal dialog in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Add' button
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
With focus on the 'Add' button inside a modal dialog, open the nested 'Address Added' dialog. using the following commands:
+
+
Space
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'Address Added' is conveyed
+
required
Description of the dialog ('The address you provided has been added to your list of delivery addresses. It is ready for immediate use. If you wish to remove it, you can do so from your profile.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('OK') is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Add' button
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
With focus on the 'Add' button inside a modal dialog, open the nested 'Address Added' dialog. using the following commands:
+
+
Space
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'Address Added' is conveyed
+
required
Description of the dialog ('The address you provided has been added to your list of delivery addresses. It is ready for immediate use. If you wish to remove it, you can do so from your profile.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' modal dialog, and sets focus on the 'Add' button
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
With focus on the 'Add' button inside a modal dialog, open the nested 'Address Added' dialog. using the following commands:
+
+
Control+Option+Space
+
Space
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'Address Added' is conveyed
+
required
Description of the dialog ('The address you provided has been added to your list of delivery addresses. It is ready for immediate use. If you wish to remove it, you can do so from your profile.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('OK') is conveyed
+
+
+
Test 21: Close a nested modal dialog in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' dialog followed by the 'Address Added' dialog, and sets focus on the 'your profile' link inside the second dialog
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
With the reading cursor on the 'your profile' link inside a modal dialog, open the nested 'End of the Road!' dialog. using the following commands:
+
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'End of the Road!' is conveyed
+
required
Description of the dialog ('You activated a fake link or button that goes nowhere! The link or button is present for demonstration purposes only.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('Close') is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' dialog followed by the 'Address Added' dialog, and sets focus on the 'your profile' link inside the second dialog
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
With the reading cursor on the 'your profile' link inside a modal dialog, open the nested 'End of the Road!' dialog. using the following commands:
+
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'End of the Road!' is conveyed
+
required
Description of the dialog ('You activated a fake link or button that goes nowhere! The link or button is present for demonstration purposes only.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('Close') is conveyed
+
+
+
Test 28: Open a nested modal dialog using a link in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' dialog followed by the 'Address Added' dialog, and sets focus on the 'your profile' link inside the second dialog
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
With focus on the 'your profile' link inside a modal dialog, open the nested 'End of the Road!' dialog. using the following commands:
+
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'End of the Road!' is conveyed
+
required
Description of the dialog ('You activated a fake link or button that goes nowhere! The link or button is present for demonstration purposes only.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('Close') is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' dialog followed by the 'Address Added' dialog, and sets focus on the 'your profile' link inside the second dialog
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
With focus on the 'your profile' link inside a modal dialog, open the nested 'End of the Road!' dialog. using the following commands:
+
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'End of the Road!' is conveyed
+
required
Description of the dialog ('You activated a fake link or button that goes nowhere! The link or button is present for demonstration purposes only.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
opens the 'Add Delivery Address' dialog followed by the 'Address Added' dialog, and sets focus on the 'your profile' link inside the second dialog
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
With focus on the 'your profile' link inside a modal dialog, open the nested 'End of the Road!' dialog. using the following commands:
+
+
Control+Option+Space
+
Enter
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'dialog' is conveyed
+
required
Name 'End of the Road!' is conveyed
+
required
Description of the dialog ('You activated a fake link or button that goes nowhere! The link or button is present for demonstration purposes only.') is conveyed
+
required
Role of the focused element ('button') is conveyed
+
required
Name of the focused element ('Close') is conveyed
+
+
+
+
+
diff --git a/build/review/tabs-manual-activation.html b/build/review/tabs-manual-activation.html
new file mode 100644
index 000000000..89aa82427
--- /dev/null
+++ b/build/review/tabs-manual-activation.html
@@ -0,0 +1,2329 @@
+
+
+
+
+ Test plan review for pattern: tabs-manual-activation
+
+
+
+
+
+
Test plan review for pattern: tabs-manual-activation (29 tests)
+
+
+
+
Test 1: Navigate forwards to a tab list in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the tab list
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Navigate to the tab list. Note: You may need to press certain commands (such as Down Arrow) multiple times to hear all information relating to the tab list. using the following commands:
+
+
Tab
+
Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab list' is conveyed
+
required
Name of the tab list, 'Entertainment', is conveyed
+
required
Role of the selected item, 'tab', is conveyed
+
required
Name of the focused tab, 'Nils Frahm', is conveyed
+
required
State of the focused tab, 'selected', is conveyed
+
required
Position of the focused tab, 1, is conveyed
+
required
Number of tabs in the tab list, 3, is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the tab list
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Navigate to the tab list. Note: You may need to press certain commands (such as Down Arrow) multiple times to hear all information relating to the tab list. using the following commands:
+
+
Tab
+
Down Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab list' is conveyed
+
required
Name of the tab list, 'Entertainment', is conveyed
+
required
Role of the selected item, 'tab', is conveyed
+
required
Name of the focused tab, 'Nils Frahm', is conveyed
+
required
State of the focused tab, 'selected', is conveyed
+
required
Position of the focused tab, 1, is conveyed
+
required
Number of tabs in the tab list, 3, is conveyed
+
+
+
Test 2: Navigate backwards to a tab list in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
activates the third tab in the tab list, and sets focus on the tab panel
+
+
Tester Instructions
+
+
+
Verify the Virtual Cursor is active by pressing Alt+Delete. If it is not, turn on the Virtual Cursor by pressing Insert+Z.
+
Navigate to the tab list. Note: You may need to press certain commands (such as Up Arrow) multiple times to hear all information relating to the tab list. using the following commands:
+
+
Shift+Tab
+
Up Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab list' is conveyed
+
required
Name of the tab list, 'Entertainment', is conveyed
+
required
Role of the selected item, 'tab', is conveyed
+
required
Name of the focused tab, 'Joke', is conveyed
+
required
State of the focused tab, 'selected', is conveyed
+
required
Position of the focused tab in the tab list, 3, is conveyed
+
required
Number of tabs in the tab list, 3, is conveyed
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
activates the third tab in the tab list, and sets focus on the tab panel
+
+
Tester Instructions
+
+
+
Insure NVDA is in browse mode by pressing Escape. Note: This command has no effect if NVDA is already in browse mode.
+
Navigate to the tab list. Note: You may need to press certain commands (such as Up Arrow) multiple times to hear all information relating to the tab list. using the following commands:
+
+
Shift+Tab
+
Up Arrow
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab list' is conveyed
+
required
Name of the tab list, 'Entertainment', is conveyed
+
required
Role of the selected item, 'tab', is conveyed
+
required
Name of the focused tab, 'Joke', is conveyed
+
required
State of the focused tab, 'selected', is conveyed
+
required
Position of the focused tab in the tab list, 3, is conveyed
+
required
Number of tabs in the tab list, 3, is conveyed
+
+
+
Test 3: Navigate forwards to a tab list in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link before the tab list
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate to the tab list. Note: You may need to press certain commands (such as Control+Option+Right Arrow) multiple times to hear all information relating to the tab list. using the following commands:
+
+
Tab
+
Control+Option+Right
+
Control+Option+Command+J
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab list' is conveyed
+
required
Name of the tab list, 'Entertainment', is conveyed
+
required
Role of the selected item, 'tab', is conveyed
+
required
Name of the focused tab, 'Nils Frahm', is conveyed
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
activates the third tab in the tab list, and sets focus on the tab panel
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate to the tab list. Note: You may need to press certain commands (such as Control+Option+Left Arrow) multiple times to hear all information relating to the tab list. using the following commands:
+
+
Shift+Tab
+
Ctrl+Option+Left
+
Shift+Control+Option+Command+J
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab list' is conveyed
+
required
Name of the tab list, 'Entertainment', is conveyed
+
required
Role of the selected item, 'tab', is conveyed
+
required
Name of the focused tab, 'Joke', is conveyed
+
required
State of the focused tab, 'selected', is conveyed
+
required
Position of the focused tab in the tab list, 3, is conveyed
+
required
Number of tabs in the tab list, 3, is conveyed
+
+
+
Test 7: Read information about a tab in a tab list in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the first tab
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Navigate to the tab panel. using the following commands:
+
+
Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab panel' is conveyed
+
required
Name 'Nils Frahm' is conveyed
+
required
Textual content of the tab panel is conveyed as 'Nils Frahm is a German musician, composer and record producer based in Berlin. He is known for combining classical and electronic music and for an unconventional approach to the piano in which he mixes a grand piano, upright piano, Roland Juno-60, Rhodes piano, drum machine, and Moog Taurus.'
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the first tab
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Navigate to the tab panel. using the following commands:
+
+
Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab panel' is conveyed
+
required
Name 'Nils Frahm' is conveyed
+
required
Textual content of the tab panel is conveyed as 'Nils Frahm is a German musician, composer and record producer based in Berlin. He is known for combining classical and electronic music and for an unconventional approach to the piano in which he mixes a grand piano, upright piano, Roland Juno-60, Rhodes piano, drum machine, and Moog Taurus.'
+
+
+
Test 21: Navigate backwards to a tab panel in interaction mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link after the tab panel
+
+
Tester Instructions
+
+
+
Verify the PC Cursor is active by pressing Alt+Delete. If it is not, turn off the Virtual Cursor by pressing Insert+Z.
+
Navigate to the tab panel. using the following commands:
+
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab panel' is conveyed
+
required
Name 'Nils Frahm' is conveyed
+
required
Textual content of the tab panel is conveyed as 'Nils Frahm is a German musician, composer and record producer based in Berlin. He is known for combining classical and electronic music and for an unconventional approach to the piano in which he mixes a grand piano, upright piano, Roland Juno-60, Rhodes piano, drum machine, and Moog Taurus.'
+
+
+
+
NVDA
+
Scripted Instructions
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link after the tab panel
+
+
Tester Instructions
+
+
+
If NVDA did not make the focus mode sound when the test page loaded, press Insert+Space to turn focus mode on.
+
Navigate to the tab panel. using the following commands:
+
+
Shift+Tab
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab panel' is conveyed
+
required
Name 'Nils Frahm' is conveyed
+
required
Textual content of the tab panel is conveyed as 'Nils Frahm is a German musician, composer and record producer based in Berlin. He is known for combining classical and electronic music and for an unconventional approach to the piano in which he mixes a grand piano, upright piano, Roland Juno-60, Rhodes piano, drum machine, and Moog Taurus.'
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the first tab
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate to the tab panel. Note: You may need to press certain commands (such as Control+Option+Right Arrow) multiple times to hear all information relating to the tab panel. using the following commands:
+
+
Tab
+
Control+Option+Right
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab panel' is conveyed
+
required
Name 'Nils Frahm' is conveyed
+
required
Textual content of the tab panel is conveyed as 'Nils Frahm is a German musician, composer and record producer based in Berlin. He is known for combining classical and electronic music and for an unconventional approach to the piano in which he mixes a grand piano, upright piano, Roland Juno-60, Rhodes piano, drum machine, and Moog Taurus.'
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on a link after the tab panel
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
Navigate to the tab panel. Note: You may need to press certain commands (such as Control+Option+Left Arrow) multiple times to hear all information relating to the tab panel. using the following commands:
+
+
Shift+Tab
+
Ctrl+Option+Left
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab panel' is conveyed
+
required
Name 'Nils Frahm' is conveyed
+
required
Textual content of the tab panel is conveyed as 'Nils Frahm is a German musician, composer and record producer based in Berlin. He is known for combining classical and electronic music and for an unconventional approach to the piano in which he mixes a grand piano, upright piano, Roland Juno-60, Rhodes piano, drum machine, and Moog Taurus.'
+
+
+
Test 24: Activate a tab in a tab list in reading mode
+ The following instructions are executed by a script in the test page to initialize the widget:
+
+
sets focus on the third tab
+
+
Tester Instructions
+
+
+
Toggle Quick Nav OFF by pressing the Left Arrow and Right Arrow keys at the same time.
+
With focus on the 'Joke' tab, delete the tab. using the following commands:
+
+
Delete
+
+
+
+
+
Assertions
+
+
Priority
Assertion
+
required
Role 'tab' is conveyed
+
required
Name 'Agnes Obel' is conveyed
+
required
State of the tab, 'selected', is conveyed
+
required
Position of tab in the tab list, 2, is conveyed
+
required
Number of tabs in the tab list, 2, is conveyed
+
+
+
+
+
diff --git a/tests/checkbox-tri-state/commands.json b/build/tests/checkbox-tri-state/commands.json
similarity index 100%
rename from tests/checkbox-tri-state/commands.json
rename to build/tests/checkbox-tri-state/commands.json
diff --git a/tests/checkbox-tri-state/index.html b/build/tests/checkbox-tri-state/index.html
similarity index 100%
rename from tests/checkbox-tri-state/index.html
rename to build/tests/checkbox-tri-state/index.html
diff --git a/build/tests/checkbox-tri-state/reference/checkbox-tri-state.html b/build/tests/checkbox-tri-state/reference/checkbox-tri-state.html
new file mode 100644
index 000000000..4c6038f18
--- /dev/null
+++ b/build/tests/checkbox-tri-state/reference/checkbox-tri-state.html
@@ -0,0 +1,81 @@
+
+
+
+
+ Tri-State Checkbox Example
+
+
+
+
+
+
+
+
+
+
+
+
Tri-State Checkbox Example
+
+ This example demonstrates using the Checkbox Design Pattern to create a tri-state, or mixed-state, checkbox.
+ In this implementation, the mixed-state checkbox represents the state of a set of standard HTML checkboxes.
+ If none of the checkboxes in the set are checked, the mixed state checkbox is not checked, and if all members of the set are checked, the mixed state checkbox is checked.
+ If the set contains both some checked and unchecked checkboxes, the mixed state checkbox is partially checked.
+ Activating the tri-state checkbox changes the states of the checkboxes in the set.
+
+
+ This example also demonstrates use of fieldset and Legend elements for labeling the checkbox group.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/tests/checkbox-tri-state/reference/css/checkbox.css b/build/tests/checkbox-tri-state/reference/css/checkbox.css
new file mode 100644
index 000000000..3eb6b7dc8
--- /dev/null
+++ b/build/tests/checkbox-tri-state/reference/css/checkbox.css
@@ -0,0 +1,89 @@
+ul.checkboxes {
+ list-style: none;
+ margin-left: 0;
+ padding-left: 1em;
+}
+
+[role="checkbox"] {
+ display: inline-block;
+ position: relative;
+ padding-left: 1.4em;
+ cursor: default;
+}
+
+[role="checkbox"]::before,
+[role="checkbox"]::after {
+ position: absolute;
+ top: 50%;
+ left: 7px;
+ transform: translate(-50%, -50%);
+ content: "";
+}
+
+[role="checkbox"]::before {
+ width: 14px;
+ height: 14px;
+ border: 1px solid hsl(0, 0%, 66%);
+ border-radius: 0.2em;
+ background-image: linear-gradient(to bottom, hsl(300, 3%, 93%), #fff 30%);
+}
+
+[role="checkbox"]:active::before {
+ background-image: linear-gradient(
+ to bottom,
+ hsl(300, 3%, 73%),
+ hsl(300, 3%, 93%) 30%
+ );
+}
+
+[role="checkbox"][aria-checked="mixed"]::before,
+[role="checkbox"][aria-checked="true"]::before {
+ border-color: hsl(216, 80%, 50%);
+ background: hsl(217, 95%, 68%);
+ background-image: linear-gradient(
+ to bottom,
+ hsl(217, 95%, 68%),
+ hsl(216, 80%, 57%)
+ );
+}
+
+[role="checkbox"][aria-checked="mixed"]::after {
+ display: block;
+ width: 8px;
+ border-bottom: 0.125em solid #fff;
+ transform: translate(-50%, -50%) rotateZ(45deg);
+ transform-origin: center center;
+}
+
+[role="checkbox"][aria-checked="mixed"]:active::after,
+[role="checkbox"][aria-checked="true"]::after {
+ display: block;
+ width: 0.25em;
+ height: 0.4em;
+ border: solid #fff;
+ border-width: 0 0.125em 0.125em 0;
+ transform: translateY(-65%) translateX(-50%) rotate(45deg);
+}
+
+[role="checkbox"][aria-checked="mixed"]:active::before,
+[role="checkbox"][aria-checked="true"]:active::before {
+ background-image: linear-gradient(
+ to bottom,
+ hsl(216, 80%, 57%),
+ hsl(217, 95%, 68%)
+ );
+}
+
+[role="checkbox"]:focus {
+ outline: none;
+}
+
+[role="checkbox"]:focus::before {
+ width: 16px;
+ height: 16px;
+ box-sizing: content-box;
+ border-color: hsl(216, 94%, 73%);
+ border-width: 3px;
+ border-radius: calc(0.2em + 3px);
+ box-shadow: inset 0 0 0 1px hsl(216, 80%, 50%);
+}
diff --git a/build/tests/checkbox-tri-state/reference/js/checkboxMixed.js b/build/tests/checkbox-tri-state/reference/js/checkboxMixed.js
new file mode 100644
index 000000000..d2ca7f9a8
--- /dev/null
+++ b/build/tests/checkbox-tri-state/reference/js/checkboxMixed.js
@@ -0,0 +1,149 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: CheckboxMixed.js
+ *
+ * Desc: CheckboxMixed widget that implements ARIA Authoring Practices
+ * for a menu of links
+ */
+
+'use strict';
+
+/*
+ * @constructor CheckboxMixed
+ *
+ *
+ */
+var CheckboxMixed = function (domNode) {
+ this.domNode = domNode;
+
+ this.controlledCheckboxes = [];
+
+ this.keyCode = Object.freeze({
+ RETURN: 13,
+ SPACE: 32,
+ });
+};
+
+CheckboxMixed.prototype.init = function () {
+ this.domNode.tabIndex = 0;
+
+ var ids = this.domNode.getAttribute('aria-controls').split(' ');
+
+ for (var i = 0; i < ids.length; i++) {
+ var node = document.getElementById(ids[i]);
+ var ccb = new ControlledCheckbox(node, this);
+ ccb.init();
+ this.controlledCheckboxes.push(ccb);
+ }
+
+ this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
+ this.domNode.addEventListener('click', this.handleClick.bind(this));
+ this.domNode.addEventListener('focus', this.handleFocus.bind(this));
+ this.domNode.addEventListener('blur', this.handleBlur.bind(this));
+
+ this.updateCheckboxMixed();
+};
+
+CheckboxMixed.prototype.updateCheckboxMixed = function () {
+ var count = 0;
+
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ if (this.controlledCheckboxes[i].isChecked()) {
+ count++;
+ }
+ }
+
+ if (count === 0) {
+ this.domNode.setAttribute('aria-checked', 'false');
+ } else {
+ if (count === this.controlledCheckboxes.length) {
+ this.domNode.setAttribute('aria-checked', 'true');
+ } else {
+ this.domNode.setAttribute('aria-checked', 'mixed');
+ this.updateControlledStates();
+ }
+ }
+};
+
+CheckboxMixed.prototype.updateControlledStates = function () {
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ this.controlledCheckboxes[i].lastState = this.controlledCheckboxes[
+ i
+ ].isChecked();
+ }
+};
+
+CheckboxMixed.prototype.anyLastChecked = function () {
+ var count = 0;
+
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ if (this.controlledCheckboxes[i].lastState) {
+ count++;
+ }
+ }
+
+ return count > 0;
+};
+
+CheckboxMixed.prototype.setControlledCheckboxes = function (value) {
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ this.controlledCheckboxes[i].setChecked(value);
+ }
+
+ this.updateCheckboxMixed();
+};
+
+CheckboxMixed.prototype.toggleCheckboxMixed = function () {
+ var state = this.domNode.getAttribute('aria-checked');
+
+ if (state === 'false') {
+ if (this.anyLastChecked()) {
+ this.setControlledCheckboxes('last');
+ } else {
+ this.setControlledCheckboxes('true');
+ }
+ } else {
+ if (state === 'mixed') {
+ this.setControlledCheckboxes('true');
+ } else {
+ this.setControlledCheckboxes('false');
+ }
+ }
+
+ this.updateCheckboxMixed();
+};
+
+/* EVENT HANDLERS */
+
+CheckboxMixed.prototype.handleKeydown = function (event) {
+ var flag = false;
+
+ switch (event.keyCode) {
+ case this.keyCode.SPACE:
+ this.toggleCheckboxMixed();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+CheckboxMixed.prototype.handleClick = function (event) {
+ this.toggleCheckboxMixed();
+};
+
+CheckboxMixed.prototype.handleFocus = function (event) {
+ this.domNode.classList.add('focus');
+};
+
+CheckboxMixed.prototype.handleBlur = function (event) {
+ this.domNode.classList.remove('focus');
+};
diff --git a/build/tests/checkbox-tri-state/reference/js/controlledCheckbox.js b/build/tests/checkbox-tri-state/reference/js/controlledCheckbox.js
new file mode 100644
index 000000000..b44c95eeb
--- /dev/null
+++ b/build/tests/checkbox-tri-state/reference/js/controlledCheckbox.js
@@ -0,0 +1,100 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: controlledCheckbox.js
+ *
+ * Desc: ControlledCheckbox widget that implements ARIA Authoring Practices
+ * for a mixed checkbox
+ */
+
+'use strict';
+
+/*
+ * @constructor ControlledCheckbox
+ *
+ *
+ */
+var ControlledCheckbox = function (domNode, controllerObj) {
+ this.domNode = domNode;
+ this.controller = controllerObj;
+ this.lastState = false;
+};
+
+ControlledCheckbox.prototype.init = function () {
+ this.lastState = this.isChecked();
+
+ console.log(this.lastState);
+
+ this.domNode.addEventListener('change', this.handleChange.bind(this));
+
+ this.domNode.addEventListener('keydown', this.handleKeyup.bind(this), true);
+ this.domNode.addEventListener('click', this.handleClick.bind(this), true);
+};
+
+ControlledCheckbox.prototype.isChecked = function () {
+ // if standard input[type=checkbox]
+ if (typeof this.domNode.checked === 'boolean') {
+ return this.domNode.checked;
+ }
+
+ // If ARIA checkbox widget
+ return this.domNode.getAttribute('aria-checked') === 'true';
+};
+
+ControlledCheckbox.prototype.setChecked = function (value) {
+ // if standard input[type=checkbox]
+ if (typeof this.domNode.checked === 'boolean') {
+ switch (value) {
+ case 'true':
+ this.domNode.checked = true;
+ break;
+
+ case 'false':
+ this.domNode.checked = false;
+ break;
+
+ case 'last':
+ this.domNode.checked = this.lastState;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // If ARIA checkbox widget
+ if (typeof this.domNode.getAttribute('aria-checked') === 'string') {
+ switch (value) {
+ case 'true':
+ case 'false':
+ this.domNode.setAttribute('aria-checked', value);
+ break;
+
+ case 'last':
+ if (this.lastState) {
+ this.domNode.setAttribute('aria-checked', 'true');
+ } else {
+ this.domNode.setAttribute('aria-checked', 'false');
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+};
+
+/* EVENT HANDLERS */
+
+ControlledCheckbox.prototype.handleChange = function (event) {
+ this.controller.updateCheckboxMixed();
+};
+
+ControlledCheckbox.prototype.handleKeyup = function (event) {
+ this.lastState = this.isChecked();
+};
+
+ControlledCheckbox.prototype.handleClick = function (event) {
+ this.lastState = this.isChecked();
+};
diff --git a/tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.html b/build/tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.html
rename to build/tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.html
diff --git a/tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.json b/build/tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.json
rename to build/tests/checkbox-tri-state/test-01-navigate-forwards-to-partially-checked-checkbox-reading.json
diff --git a/tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.html b/build/tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.html
rename to build/tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.html
diff --git a/tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.json b/build/tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.json
rename to build/tests/checkbox-tri-state/test-02-navigate-backwards-to-partially-checked-checkbox-reading.json
diff --git a/tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-03-navigate-forwards-to-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-04-navigate-backwards-to-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-05-navigate-forwards-to-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-06-navigate-backwards-to-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.html b/build/tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.html
rename to build/tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.html
diff --git a/tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.json b/build/tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.json
rename to build/tests/checkbox-tri-state/test-07-operate-partially-checked-checkbox-reading.json
diff --git a/tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-08-operate-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-09-operate-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.html b/build/tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.html
rename to build/tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.html
diff --git a/tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.json b/build/tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.json
rename to build/tests/checkbox-tri-state/test-10-operate-unchecked-checked-checkbox-reading.json
diff --git a/tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-11-operate-unchecked-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-12-operate-unchecked-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.html b/build/tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.html
rename to build/tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.html
diff --git a/tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.json b/build/tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.json
rename to build/tests/checkbox-tri-state/test-13-read-partially-checked-checkbox-reading.json
diff --git a/tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-14-read-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.html b/build/tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.html
rename to build/tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.html
diff --git a/tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.json b/build/tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.json
rename to build/tests/checkbox-tri-state/test-15-read-partially-checked-checkbox-interaction.json
diff --git a/tests/checkbox-tri-state/test-16-read-checkbox-group-reading.html b/build/tests/checkbox-tri-state/test-16-read-checkbox-group-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-16-read-checkbox-group-reading.html
rename to build/tests/checkbox-tri-state/test-16-read-checkbox-group-reading.html
diff --git a/tests/checkbox-tri-state/test-16-read-checkbox-group-reading.json b/build/tests/checkbox-tri-state/test-16-read-checkbox-group-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-16-read-checkbox-group-reading.json
rename to build/tests/checkbox-tri-state/test-16-read-checkbox-group-reading.json
diff --git a/tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.html b/build/tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.html
rename to build/tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.html
diff --git a/tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.json b/build/tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.json
rename to build/tests/checkbox-tri-state/test-17-read-checkbox-group-interaction.json
diff --git a/tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.html b/build/tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.html
rename to build/tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.html
diff --git a/tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.json b/build/tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.json
rename to build/tests/checkbox-tri-state/test-18-read-checkbox-group-interaction.json
diff --git a/tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.html b/build/tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.html
rename to build/tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.html
diff --git a/tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.json b/build/tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.json
rename to build/tests/checkbox-tri-state/test-19-navigate-forwards-into-checkbox-group-reading.json
diff --git a/tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.html b/build/tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.html
similarity index 100%
rename from tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.html
rename to build/tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.html
diff --git a/tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.json b/build/tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.json
similarity index 100%
rename from tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.json
rename to build/tests/checkbox-tri-state/test-20-navigate-backwards-out-of-checkbox-group-reading.json
diff --git a/tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.html b/build/tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.html
rename to build/tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.html
diff --git a/tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.json b/build/tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.json
rename to build/tests/checkbox-tri-state/test-21-navigate-forwards-into-checkbox-group-interaction.json
diff --git a/tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.html b/build/tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.html
rename to build/tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.html
diff --git a/tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.json b/build/tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.json
rename to build/tests/checkbox-tri-state/test-22-navigate-backwards-out-of-checkbox-group-interaction.json
diff --git a/tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.html b/build/tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.html
rename to build/tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.html
diff --git a/tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.json b/build/tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.json
rename to build/tests/checkbox-tri-state/test-23-navigate-forwards-into-checkbox-group-interaction.json
diff --git a/tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.html b/build/tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.html
rename to build/tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.html
diff --git a/tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.json b/build/tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.json
rename to build/tests/checkbox-tri-state/test-24-navigate-backwards-out-of-checkbox-group-interaction.json
diff --git a/tests/checkbox/commands.json b/build/tests/checkbox/commands.json
similarity index 100%
rename from tests/checkbox/commands.json
rename to build/tests/checkbox/commands.json
diff --git a/tests/checkbox/index.html b/build/tests/checkbox/index.html
similarity index 100%
rename from tests/checkbox/index.html
rename to build/tests/checkbox/index.html
diff --git a/build/tests/checkbox/reference/2020-11-23_175030/checkbox-1/checkbox-1.html b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-1/checkbox-1.html
new file mode 100644
index 000000000..5aa42a3c0
--- /dev/null
+++ b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-1/checkbox-1.html
@@ -0,0 +1,46 @@
+
+
+
+
+ Checkbox Example (Two State) | WAI-ARIA Authoring Practices 1.2
+
+
+
+
+
+
+
+
+
+
+
+
+
Checkbox Example (Two State)
+
+
+
Sandwich Condiments
+
+
+
Lettuce
+
Tomato
+
Mustard
+
Sprouts
+
+
+
+
+
+
+
+
diff --git a/build/tests/checkbox/reference/2020-11-23_175030/checkbox-1/js/checkbox.js b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-1/js/checkbox.js
new file mode 100644
index 000000000..092727e97
--- /dev/null
+++ b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-1/js/checkbox.js
@@ -0,0 +1,80 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: Checkbox.js
+ *
+ * Desc: Checkbox widget that implements ARIA Authoring Practices
+ * for a menu of links
+ *
+ */
+
+'use strict';
+
+/*
+ * @constructor Checkbox
+ *
+ *
+ */
+var Checkbox = function (domNode) {
+ this.domNode = domNode;
+
+ this.keyCode = Object.freeze({
+ RETURN: 13,
+ SPACE: 32,
+ });
+};
+
+Checkbox.prototype.init = function () {
+ this.domNode.tabIndex = 0;
+
+ if (!this.domNode.getAttribute('aria-checked')) {
+ this.domNode.setAttribute('aria-checked', 'false');
+ }
+
+ this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
+ this.domNode.addEventListener('click', this.handleClick.bind(this));
+ this.domNode.addEventListener('focus', this.handleFocus.bind(this));
+ this.domNode.addEventListener('blur', this.handleBlur.bind(this));
+};
+
+Checkbox.prototype.toggleCheckbox = function () {
+ if (this.domNode.getAttribute('aria-checked') === 'true') {
+ this.domNode.setAttribute('aria-checked', 'false');
+ } else {
+ this.domNode.setAttribute('aria-checked', 'true');
+ }
+};
+
+/* EVENT HANDLERS */
+
+Checkbox.prototype.handleKeydown = function (event) {
+ var flag = false;
+
+ switch (event.keyCode) {
+ case this.keyCode.SPACE:
+ this.toggleCheckbox();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+Checkbox.prototype.handleClick = function (event) {
+ this.toggleCheckbox();
+};
+
+Checkbox.prototype.handleFocus = function (event) {
+ this.domNode.classList.add('focus');
+};
+
+Checkbox.prototype.handleBlur = function (event) {
+ this.domNode.classList.remove('focus');
+};
diff --git a/build/tests/checkbox/reference/2020-11-23_175030/checkbox-2/js/checkboxMixed.js b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-2/js/checkboxMixed.js
new file mode 100644
index 000000000..d2ca7f9a8
--- /dev/null
+++ b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-2/js/checkboxMixed.js
@@ -0,0 +1,149 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: CheckboxMixed.js
+ *
+ * Desc: CheckboxMixed widget that implements ARIA Authoring Practices
+ * for a menu of links
+ */
+
+'use strict';
+
+/*
+ * @constructor CheckboxMixed
+ *
+ *
+ */
+var CheckboxMixed = function (domNode) {
+ this.domNode = domNode;
+
+ this.controlledCheckboxes = [];
+
+ this.keyCode = Object.freeze({
+ RETURN: 13,
+ SPACE: 32,
+ });
+};
+
+CheckboxMixed.prototype.init = function () {
+ this.domNode.tabIndex = 0;
+
+ var ids = this.domNode.getAttribute('aria-controls').split(' ');
+
+ for (var i = 0; i < ids.length; i++) {
+ var node = document.getElementById(ids[i]);
+ var ccb = new ControlledCheckbox(node, this);
+ ccb.init();
+ this.controlledCheckboxes.push(ccb);
+ }
+
+ this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
+ this.domNode.addEventListener('click', this.handleClick.bind(this));
+ this.domNode.addEventListener('focus', this.handleFocus.bind(this));
+ this.domNode.addEventListener('blur', this.handleBlur.bind(this));
+
+ this.updateCheckboxMixed();
+};
+
+CheckboxMixed.prototype.updateCheckboxMixed = function () {
+ var count = 0;
+
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ if (this.controlledCheckboxes[i].isChecked()) {
+ count++;
+ }
+ }
+
+ if (count === 0) {
+ this.domNode.setAttribute('aria-checked', 'false');
+ } else {
+ if (count === this.controlledCheckboxes.length) {
+ this.domNode.setAttribute('aria-checked', 'true');
+ } else {
+ this.domNode.setAttribute('aria-checked', 'mixed');
+ this.updateControlledStates();
+ }
+ }
+};
+
+CheckboxMixed.prototype.updateControlledStates = function () {
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ this.controlledCheckboxes[i].lastState = this.controlledCheckboxes[
+ i
+ ].isChecked();
+ }
+};
+
+CheckboxMixed.prototype.anyLastChecked = function () {
+ var count = 0;
+
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ if (this.controlledCheckboxes[i].lastState) {
+ count++;
+ }
+ }
+
+ return count > 0;
+};
+
+CheckboxMixed.prototype.setControlledCheckboxes = function (value) {
+ for (var i = 0; i < this.controlledCheckboxes.length; i++) {
+ this.controlledCheckboxes[i].setChecked(value);
+ }
+
+ this.updateCheckboxMixed();
+};
+
+CheckboxMixed.prototype.toggleCheckboxMixed = function () {
+ var state = this.domNode.getAttribute('aria-checked');
+
+ if (state === 'false') {
+ if (this.anyLastChecked()) {
+ this.setControlledCheckboxes('last');
+ } else {
+ this.setControlledCheckboxes('true');
+ }
+ } else {
+ if (state === 'mixed') {
+ this.setControlledCheckboxes('true');
+ } else {
+ this.setControlledCheckboxes('false');
+ }
+ }
+
+ this.updateCheckboxMixed();
+};
+
+/* EVENT HANDLERS */
+
+CheckboxMixed.prototype.handleKeydown = function (event) {
+ var flag = false;
+
+ switch (event.keyCode) {
+ case this.keyCode.SPACE:
+ this.toggleCheckboxMixed();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+CheckboxMixed.prototype.handleClick = function (event) {
+ this.toggleCheckboxMixed();
+};
+
+CheckboxMixed.prototype.handleFocus = function (event) {
+ this.domNode.classList.add('focus');
+};
+
+CheckboxMixed.prototype.handleBlur = function (event) {
+ this.domNode.classList.remove('focus');
+};
diff --git a/build/tests/checkbox/reference/2020-11-23_175030/checkbox-2/js/controlledCheckbox.js b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-2/js/controlledCheckbox.js
new file mode 100644
index 000000000..b44c95eeb
--- /dev/null
+++ b/build/tests/checkbox/reference/2020-11-23_175030/checkbox-2/js/controlledCheckbox.js
@@ -0,0 +1,100 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: controlledCheckbox.js
+ *
+ * Desc: ControlledCheckbox widget that implements ARIA Authoring Practices
+ * for a mixed checkbox
+ */
+
+'use strict';
+
+/*
+ * @constructor ControlledCheckbox
+ *
+ *
+ */
+var ControlledCheckbox = function (domNode, controllerObj) {
+ this.domNode = domNode;
+ this.controller = controllerObj;
+ this.lastState = false;
+};
+
+ControlledCheckbox.prototype.init = function () {
+ this.lastState = this.isChecked();
+
+ console.log(this.lastState);
+
+ this.domNode.addEventListener('change', this.handleChange.bind(this));
+
+ this.domNode.addEventListener('keydown', this.handleKeyup.bind(this), true);
+ this.domNode.addEventListener('click', this.handleClick.bind(this), true);
+};
+
+ControlledCheckbox.prototype.isChecked = function () {
+ // if standard input[type=checkbox]
+ if (typeof this.domNode.checked === 'boolean') {
+ return this.domNode.checked;
+ }
+
+ // If ARIA checkbox widget
+ return this.domNode.getAttribute('aria-checked') === 'true';
+};
+
+ControlledCheckbox.prototype.setChecked = function (value) {
+ // if standard input[type=checkbox]
+ if (typeof this.domNode.checked === 'boolean') {
+ switch (value) {
+ case 'true':
+ this.domNode.checked = true;
+ break;
+
+ case 'false':
+ this.domNode.checked = false;
+ break;
+
+ case 'last':
+ this.domNode.checked = this.lastState;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // If ARIA checkbox widget
+ if (typeof this.domNode.getAttribute('aria-checked') === 'string') {
+ switch (value) {
+ case 'true':
+ case 'false':
+ this.domNode.setAttribute('aria-checked', value);
+ break;
+
+ case 'last':
+ if (this.lastState) {
+ this.domNode.setAttribute('aria-checked', 'true');
+ } else {
+ this.domNode.setAttribute('aria-checked', 'false');
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+};
+
+/* EVENT HANDLERS */
+
+ControlledCheckbox.prototype.handleChange = function (event) {
+ this.controller.updateCheckboxMixed();
+};
+
+ControlledCheckbox.prototype.handleKeyup = function (event) {
+ this.lastState = this.isChecked();
+};
+
+ControlledCheckbox.prototype.handleClick = function (event) {
+ this.lastState = this.isChecked();
+};
diff --git a/build/tests/checkbox/reference/2020-11-23_175030/css/checkbox.css b/build/tests/checkbox/reference/2020-11-23_175030/css/checkbox.css
new file mode 100644
index 000000000..3eb6b7dc8
--- /dev/null
+++ b/build/tests/checkbox/reference/2020-11-23_175030/css/checkbox.css
@@ -0,0 +1,89 @@
+ul.checkboxes {
+ list-style: none;
+ margin-left: 0;
+ padding-left: 1em;
+}
+
+[role="checkbox"] {
+ display: inline-block;
+ position: relative;
+ padding-left: 1.4em;
+ cursor: default;
+}
+
+[role="checkbox"]::before,
+[role="checkbox"]::after {
+ position: absolute;
+ top: 50%;
+ left: 7px;
+ transform: translate(-50%, -50%);
+ content: "";
+}
+
+[role="checkbox"]::before {
+ width: 14px;
+ height: 14px;
+ border: 1px solid hsl(0, 0%, 66%);
+ border-radius: 0.2em;
+ background-image: linear-gradient(to bottom, hsl(300, 3%, 93%), #fff 30%);
+}
+
+[role="checkbox"]:active::before {
+ background-image: linear-gradient(
+ to bottom,
+ hsl(300, 3%, 73%),
+ hsl(300, 3%, 93%) 30%
+ );
+}
+
+[role="checkbox"][aria-checked="mixed"]::before,
+[role="checkbox"][aria-checked="true"]::before {
+ border-color: hsl(216, 80%, 50%);
+ background: hsl(217, 95%, 68%);
+ background-image: linear-gradient(
+ to bottom,
+ hsl(217, 95%, 68%),
+ hsl(216, 80%, 57%)
+ );
+}
+
+[role="checkbox"][aria-checked="mixed"]::after {
+ display: block;
+ width: 8px;
+ border-bottom: 0.125em solid #fff;
+ transform: translate(-50%, -50%) rotateZ(45deg);
+ transform-origin: center center;
+}
+
+[role="checkbox"][aria-checked="mixed"]:active::after,
+[role="checkbox"][aria-checked="true"]::after {
+ display: block;
+ width: 0.25em;
+ height: 0.4em;
+ border: solid #fff;
+ border-width: 0 0.125em 0.125em 0;
+ transform: translateY(-65%) translateX(-50%) rotate(45deg);
+}
+
+[role="checkbox"][aria-checked="mixed"]:active::before,
+[role="checkbox"][aria-checked="true"]:active::before {
+ background-image: linear-gradient(
+ to bottom,
+ hsl(216, 80%, 57%),
+ hsl(217, 95%, 68%)
+ );
+}
+
+[role="checkbox"]:focus {
+ outline: none;
+}
+
+[role="checkbox"]:focus::before {
+ width: 16px;
+ height: 16px;
+ box-sizing: content-box;
+ border-color: hsl(216, 94%, 73%);
+ border-width: 3px;
+ border-radius: calc(0.2em + 3px);
+ box-shadow: inset 0 0 0 1px hsl(216, 80%, 50%);
+}
diff --git a/tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.html b/build/tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.html
rename to build/tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.html
diff --git a/tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.json b/build/tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.json
rename to build/tests/checkbox/test-01-navigate-to-unchecked-checkbox-reading.json
diff --git a/tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.html b/build/tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.html
rename to build/tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.html
diff --git a/tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.json b/build/tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.json
rename to build/tests/checkbox/test-02-navigate-to-unchecked-checkbox-interaction.json
diff --git a/tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.html b/build/tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.html
rename to build/tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.html
diff --git a/tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.json b/build/tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.json
rename to build/tests/checkbox/test-03-navigate-to-unchecked-checkbox-interaction.json
diff --git a/tests/checkbox/test-04-navigate-to-checked-checkbox-reading.html b/build/tests/checkbox/test-04-navigate-to-checked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox/test-04-navigate-to-checked-checkbox-reading.html
rename to build/tests/checkbox/test-04-navigate-to-checked-checkbox-reading.html
diff --git a/tests/checkbox/test-04-navigate-to-checked-checkbox-reading.json b/build/tests/checkbox/test-04-navigate-to-checked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox/test-04-navigate-to-checked-checkbox-reading.json
rename to build/tests/checkbox/test-04-navigate-to-checked-checkbox-reading.json
diff --git a/tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.html b/build/tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.html
rename to build/tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.html
diff --git a/tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.json b/build/tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.json
rename to build/tests/checkbox/test-05-navigate-to-checked-checkbox-interaction.json
diff --git a/tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.html b/build/tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.html
rename to build/tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.html
diff --git a/tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.json b/build/tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.json
rename to build/tests/checkbox/test-06-navigate-to-checked-checkbox-interaction.json
diff --git a/tests/checkbox/test-07-operate-checkbox-reading.html b/build/tests/checkbox/test-07-operate-checkbox-reading.html
similarity index 100%
rename from tests/checkbox/test-07-operate-checkbox-reading.html
rename to build/tests/checkbox/test-07-operate-checkbox-reading.html
diff --git a/tests/checkbox/test-07-operate-checkbox-reading.json b/build/tests/checkbox/test-07-operate-checkbox-reading.json
similarity index 100%
rename from tests/checkbox/test-07-operate-checkbox-reading.json
rename to build/tests/checkbox/test-07-operate-checkbox-reading.json
diff --git a/tests/checkbox/test-08-operate-checkbox-interaction.html b/build/tests/checkbox/test-08-operate-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-08-operate-checkbox-interaction.html
rename to build/tests/checkbox/test-08-operate-checkbox-interaction.html
diff --git a/tests/checkbox/test-08-operate-checkbox-interaction.json b/build/tests/checkbox/test-08-operate-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-08-operate-checkbox-interaction.json
rename to build/tests/checkbox/test-08-operate-checkbox-interaction.json
diff --git a/tests/checkbox/test-09-operate-checkbox-interaction.html b/build/tests/checkbox/test-09-operate-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-09-operate-checkbox-interaction.html
rename to build/tests/checkbox/test-09-operate-checkbox-interaction.html
diff --git a/tests/checkbox/test-09-operate-checkbox-interaction.json b/build/tests/checkbox/test-09-operate-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-09-operate-checkbox-interaction.json
rename to build/tests/checkbox/test-09-operate-checkbox-interaction.json
diff --git a/tests/checkbox/test-10-read-unchecked-checkbox-reading.html b/build/tests/checkbox/test-10-read-unchecked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox/test-10-read-unchecked-checkbox-reading.html
rename to build/tests/checkbox/test-10-read-unchecked-checkbox-reading.html
diff --git a/tests/checkbox/test-10-read-unchecked-checkbox-reading.json b/build/tests/checkbox/test-10-read-unchecked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox/test-10-read-unchecked-checkbox-reading.json
rename to build/tests/checkbox/test-10-read-unchecked-checkbox-reading.json
diff --git a/tests/checkbox/test-11-read-unchecked-checkbox-interaction.html b/build/tests/checkbox/test-11-read-unchecked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-11-read-unchecked-checkbox-interaction.html
rename to build/tests/checkbox/test-11-read-unchecked-checkbox-interaction.html
diff --git a/tests/checkbox/test-11-read-unchecked-checkbox-interaction.json b/build/tests/checkbox/test-11-read-unchecked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-11-read-unchecked-checkbox-interaction.json
rename to build/tests/checkbox/test-11-read-unchecked-checkbox-interaction.json
diff --git a/tests/checkbox/test-12-read-unchecked-checkbox-interaction.html b/build/tests/checkbox/test-12-read-unchecked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-12-read-unchecked-checkbox-interaction.html
rename to build/tests/checkbox/test-12-read-unchecked-checkbox-interaction.html
diff --git a/tests/checkbox/test-12-read-unchecked-checkbox-interaction.json b/build/tests/checkbox/test-12-read-unchecked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-12-read-unchecked-checkbox-interaction.json
rename to build/tests/checkbox/test-12-read-unchecked-checkbox-interaction.json
diff --git a/tests/checkbox/test-13-read-checked-checkbox-reading.html b/build/tests/checkbox/test-13-read-checked-checkbox-reading.html
similarity index 100%
rename from tests/checkbox/test-13-read-checked-checkbox-reading.html
rename to build/tests/checkbox/test-13-read-checked-checkbox-reading.html
diff --git a/tests/checkbox/test-13-read-checked-checkbox-reading.json b/build/tests/checkbox/test-13-read-checked-checkbox-reading.json
similarity index 100%
rename from tests/checkbox/test-13-read-checked-checkbox-reading.json
rename to build/tests/checkbox/test-13-read-checked-checkbox-reading.json
diff --git a/tests/checkbox/test-14-read-checked-checkbox-interaction.html b/build/tests/checkbox/test-14-read-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-14-read-checked-checkbox-interaction.html
rename to build/tests/checkbox/test-14-read-checked-checkbox-interaction.html
diff --git a/tests/checkbox/test-14-read-checked-checkbox-interaction.json b/build/tests/checkbox/test-14-read-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-14-read-checked-checkbox-interaction.json
rename to build/tests/checkbox/test-14-read-checked-checkbox-interaction.json
diff --git a/tests/checkbox/test-15-read-checked-checkbox-interaction.html b/build/tests/checkbox/test-15-read-checked-checkbox-interaction.html
similarity index 100%
rename from tests/checkbox/test-15-read-checked-checkbox-interaction.html
rename to build/tests/checkbox/test-15-read-checked-checkbox-interaction.html
diff --git a/tests/checkbox/test-15-read-checked-checkbox-interaction.json b/build/tests/checkbox/test-15-read-checked-checkbox-interaction.json
similarity index 100%
rename from tests/checkbox/test-15-read-checked-checkbox-interaction.json
rename to build/tests/checkbox/test-15-read-checked-checkbox-interaction.json
diff --git a/tests/checkbox/test-16-read-checkbox-group-reading.html b/build/tests/checkbox/test-16-read-checkbox-group-reading.html
similarity index 100%
rename from tests/checkbox/test-16-read-checkbox-group-reading.html
rename to build/tests/checkbox/test-16-read-checkbox-group-reading.html
diff --git a/tests/checkbox/test-16-read-checkbox-group-reading.json b/build/tests/checkbox/test-16-read-checkbox-group-reading.json
similarity index 100%
rename from tests/checkbox/test-16-read-checkbox-group-reading.json
rename to build/tests/checkbox/test-16-read-checkbox-group-reading.json
diff --git a/tests/checkbox/test-17-read-checkbox-group-interaction.html b/build/tests/checkbox/test-17-read-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox/test-17-read-checkbox-group-interaction.html
rename to build/tests/checkbox/test-17-read-checkbox-group-interaction.html
diff --git a/tests/checkbox/test-17-read-checkbox-group-interaction.json b/build/tests/checkbox/test-17-read-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox/test-17-read-checkbox-group-interaction.json
rename to build/tests/checkbox/test-17-read-checkbox-group-interaction.json
diff --git a/tests/checkbox/test-18-read-checkbox-group-interaction.html b/build/tests/checkbox/test-18-read-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox/test-18-read-checkbox-group-interaction.html
rename to build/tests/checkbox/test-18-read-checkbox-group-interaction.html
diff --git a/tests/checkbox/test-18-read-checkbox-group-interaction.json b/build/tests/checkbox/test-18-read-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox/test-18-read-checkbox-group-interaction.json
rename to build/tests/checkbox/test-18-read-checkbox-group-interaction.json
diff --git a/tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.html b/build/tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.html
similarity index 100%
rename from tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.html
rename to build/tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.html
diff --git a/tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.json b/build/tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.json
similarity index 100%
rename from tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.json
rename to build/tests/checkbox/test-19-navigate-sequentially-through-checkbox-group-reading.json
diff --git a/tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.html b/build/tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.html
rename to build/tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.html
diff --git a/tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.json b/build/tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.json
rename to build/tests/checkbox/test-20-navigate-sequentially-through-checkbox-group-interaction.json
diff --git a/tests/checkbox/test-21-navigate-into-checkbox-group-reading.html b/build/tests/checkbox/test-21-navigate-into-checkbox-group-reading.html
similarity index 100%
rename from tests/checkbox/test-21-navigate-into-checkbox-group-reading.html
rename to build/tests/checkbox/test-21-navigate-into-checkbox-group-reading.html
diff --git a/tests/checkbox/test-21-navigate-into-checkbox-group-reading.json b/build/tests/checkbox/test-21-navigate-into-checkbox-group-reading.json
similarity index 100%
rename from tests/checkbox/test-21-navigate-into-checkbox-group-reading.json
rename to build/tests/checkbox/test-21-navigate-into-checkbox-group-reading.json
diff --git a/tests/checkbox/test-22-navigate-into-checkbox-group-interaction.html b/build/tests/checkbox/test-22-navigate-into-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox/test-22-navigate-into-checkbox-group-interaction.html
rename to build/tests/checkbox/test-22-navigate-into-checkbox-group-interaction.html
diff --git a/tests/checkbox/test-22-navigate-into-checkbox-group-interaction.json b/build/tests/checkbox/test-22-navigate-into-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox/test-22-navigate-into-checkbox-group-interaction.json
rename to build/tests/checkbox/test-22-navigate-into-checkbox-group-interaction.json
diff --git a/tests/checkbox/test-23-navigate-into-checkbox-group-interaction.html b/build/tests/checkbox/test-23-navigate-into-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox/test-23-navigate-into-checkbox-group-interaction.html
rename to build/tests/checkbox/test-23-navigate-into-checkbox-group-interaction.html
diff --git a/tests/checkbox/test-23-navigate-into-checkbox-group-interaction.json b/build/tests/checkbox/test-23-navigate-into-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox/test-23-navigate-into-checkbox-group-interaction.json
rename to build/tests/checkbox/test-23-navigate-into-checkbox-group-interaction.json
diff --git a/tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.html b/build/tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.html
similarity index 100%
rename from tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.html
rename to build/tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.html
diff --git a/tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.json b/build/tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.json
similarity index 100%
rename from tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.json
rename to build/tests/checkbox/test-24-navigate-out-of-checkbox-group-reading.json
diff --git a/tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.html b/build/tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.html
rename to build/tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.html
diff --git a/tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.json b/build/tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.json
rename to build/tests/checkbox/test-25-navigate-out-of-checkbox-group-interaction.json
diff --git a/tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.html b/build/tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.html
similarity index 100%
rename from tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.html
rename to build/tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.html
diff --git a/tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.json b/build/tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.json
similarity index 100%
rename from tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.json
rename to build/tests/checkbox/test-26-navigate-out-of-checkbox-group-interaction.json
diff --git a/tests/combobox-autocomplete-both/commands.json b/build/tests/combobox-autocomplete-both/commands.json
similarity index 100%
rename from tests/combobox-autocomplete-both/commands.json
rename to build/tests/combobox-autocomplete-both/commands.json
diff --git a/tests/combobox-autocomplete-both/index.html b/build/tests/combobox-autocomplete-both/index.html
similarity index 100%
rename from tests/combobox-autocomplete-both/index.html
rename to build/tests/combobox-autocomplete-both/index.html
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-19_142152/combobox-autocomplete-both.html b/build/tests/combobox-autocomplete-both/reference/2020-11-19_142152/combobox-autocomplete-both.html
new file mode 100644
index 000000000..ecd3b4ba2
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-19_142152/combobox-autocomplete-both.html
@@ -0,0 +1,528 @@
+
+
+
+
+Editable Combobox With Both List and Inline Autocomplete Example | WAI-ARIA Authoring Practices 1.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Editable Combobox With Both List and Inline Autocomplete Example
+
+ The below combobox for choosing the name of a US state or territory demonstrates the
+ design pattern for combobox.
+ The design pattern describes four types of autocomplete behavior.
+ This example illustrates the autocomplete behavior referred to in the pattern as list with inline completion.
+ If the user types one or more characters in the edit box and the typed characters match the beginning of the name of one or more states or territories,
+ a listbox popup appears containing the matching names, and the first match is automatically selected.
+ In addition, the portion of the selected suggestion that has not been typed by the user, a completion string, appears inline after the input cursor in the textbox.
+ The automatically selected suggestion becomes the value of the textbox when the combobox loses focus unless the user chooses a different suggestion or changes the character string in the textbox.
+ Note that this implementation enables users to input the name of a state or territory, but it does not prevent input of any other arbitrary value.
+
+
Similar examples include:
+
+
Select-Only Combobox: A single-select combobox with no text input that is functionally similar to an HTML select element.
Editable Combobox with Grid Popup: An editable combobox that presents suggestions in a grid, enabling users to navigate descriptive information about each suggestion.
+
Date Picker Combobox: An editable date input combobox that opens a dialog containing a calendar grid and buttons for navigating by month and year.
If the listbox is displayed and a suggestion is selected, moves visual focus to the next suggested value.
+
If the textbox is empty and the listbox is not displayed, opens the listbox and moves visual focus to the first option.
+
In both cases DOM focus remains on the textbox.
+
+
+
+
+
Alt + Down Arrow
+
+ Opens the listbox without moving focus or changing selection.
+
+
+
+
Up Arrow
+
+
+
If the listbox is displayed and a suggestion is selected, moves visual focus to the last suggested value.
+
If the textbox is empty, first opens the listbox if it is not already displayed and then moves visual focus to the last option.
+
In both cases DOM focus remains on the textbox.
+
+
+
+
+
Enter
+
+
+
Sets the textbox value to the content of the selected option.
+
Closes the listbox.
+
+
+
+
+
Escape
+
+
+
If the listbox is displayed, closes it.
+
If the listbox is not displayed, clears the textbox.
+
+
+
+
+
Standard single line text editing keys
+
+
+
Keys used for cursor movement and text manipulation, such as Delete and Shift + Right Arrow.
+
An HTML input with type="text" is used for the textbox so the browser will provide platform-specific editing keys.
+
+
+
+
+
+
Listbox Popup
+
+ NOTE: When visual focus is in the listbox, DOM focus remains on the textbox and the value of aria-activedescendant on the textbox is set to a value that refers to the listbox option that is visually indicated as focused.
+ Where the following descriptions of keyboard commands mention focus, they are referring to the visual focus indicator.
+ For more information about this focus management technique, see
+ Using aria-activedescendant to Manage Focus.
+
+
+
+
+
Key
+
Function
+
+
+
+
+
Enter
+
+
+
Sets the textbox value to the content of the focused option in the listbox.
+
Closes the listbox.
+
Sets visual focus on the textbox.
+
+
+
+
+
Escape
+
+
+
Closes the listbox.
+
Sets visual focus on the textbox.
+
+
+
+
+
Down Arrow
+
+
+
Moves visual focus to the next option.
+
If visual focus is on the last option, moves visual focus to the first option.
+
Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
+
+
+
+
+
Up Arrow
+
+
+
Moves visual focus to the previous option.
+
If visual focus is on the first option, moves visual focus to the last option.
+
Note: This wrapping behavior is useful when Home and End move the editing cursor as described below.
+
+
+
+
+
Right Arrow
+
Moves visual focus to the textbox and moves the editing cursor one character to the right.
+
+
+
Left Arrow
+
Moves visual focus to the textbox and moves the editing cursor one character to the left.
+
+
+
Home
+
Moves visual focus to the textbox and places the editing cursor at the beginning of the field.
+
+
+
End
+
Moves visual focus to the textbox and places the editing cursor at the end of the field.
+
+
+
Printable Characters
+
+
+
Moves visual focus to the textbox.
+
Types the character in the textbox.
+
Options in the listbox are filtered based on characters in the textbox.
+
+
+
+
+
+
Button
+
+ The button has been removed from the tab sequence of the page, but is still important to assistive technologies for mobile devices that use touch events to open the list of options.
+
Indicates that the autocomplete behavior of the text input is to both show an inline completion string and suggest a list of possible values in a popup where the suggestions are related to the string that is present in the textbox.
+
+
+
+
+ aria-controls="#IDREF"
+
+
input[type="text"]
+
Identifies the element that serves as the popup.
+
+
+
+
+ aria-expanded="false"
+
+
input[type="text"]
+
Indicates that the popup element is not displayed.
+
+
+
+
+ aria-expanded="true"
+
+
input[type="text"]
+
Indicates that the popup element is displayed.
+
+
+
+
+ id="string"
+
+
input[type="text"]
+
+
+
Referenced by for attribute of label element to provide an accessible name.
+
Recommended naming method for HTML input elements because clicking label focuses input.
+
+
+
+
+
+
+ aria-activedescendant="IDREF"
+
+
input[type="text"]
+
+
+
When an option in the listbox is visually indicated as having keyboard focus, refers to that option.
+
When navigation keys, such as Down Arrow, are pressed, the JavaScript changes the value.
+
Enables assistive technologies to know which element the application regards as focused while DOM focus remains on the input element.
The text content of the element provides the accessible name of the option.
+
+
+
+
+
+
+ aria-selected="true"
+
+
li
+
+
+
Specified on an option in the listbox when it is visually highlighted as selected.
+
Occurs when an option in the list is referenced by aria-activedescendant and when focus is in the textbox and the first option is automatically selected.
+
+
+
+
+
+
+
Button
+
+
+
+
Role
+
Attribute
+
Element
+
Usage
+
+
+
+
+
+
+ tabindex="-1"
+
+
button
+
Removes the button from the tab sequence of the page because its function is redundant with the keyboard operation of the combobox.
+
+
+
+
+ aria-label="States"
+
+
button
+
Provides a label for the button.
+
+
+
+
+ aria-controls="#IDREF"
+
+
button
+
Identifies the element that serves as the popup.
+
+
+
+
+ aria-expanded="false"
+
+
button
+
Indicates that the popup element is not displayed.
Editable Combobox With Both List and Inline Autocomplete Example
+
+
+
+
+
+
+
+
+
+
Alabama
+
Alaska
+
American Samoa
+
Arizona
+
Arkansas
+
California
+
Colorado
+
Connecticut
+
Delaware
+
District of Columbia
+
Florida
+
Georgia
+
Guam
+
Hawaii
+
Idaho
+
Illinois
+
Indiana
+
Iowa
+
Kansas
+
Kentucky
+
Louisiana
+
Maine
+
Maryland
+
Massachusetts
+
Michigan
+
Minnesota
+
Mississippi
+
Missouri
+
Montana
+
Nebraska
+
Nevada
+
New Hampshire
+
New Jersey
+
New Mexico
+
New York
+
North Carolina
+
North Dakota
+
Northern Marianas Islands
+
Ohio
+
Oklahoma
+
Oregon
+
Pennsylvania
+
Puerto Rico
+
Rhode Island
+
South Carolina
+
South Dakota
+
Tennessee
+
Texas
+
Utah
+
Vermont
+
Virginia
+
Virgin Islands
+
Washington
+
West Virginia
+
Wisconsin
+
Wyoming
+
+
+
+
+
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/combobox-autocomplete.css b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/combobox-autocomplete.css
new file mode 100644
index 000000000..297f818b5
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/combobox-autocomplete.css
@@ -0,0 +1,114 @@
+.combobox-list {
+ position: relative;
+}
+
+.combobox .group {
+ display: inline-flex;
+}
+
+.combobox input,
+.combobox button {
+ background-color: white;
+ color: black;
+ box-sizing: border-box;
+ height: 1.75rem;
+ padding: 0;
+ margin: 0;
+ vertical-align: bottom;
+ border: 1px solid gray;
+ position: relative;
+}
+
+.combobox input {
+ width: 10.75rem;
+ border-right: none;
+ outline: none;
+ font-size: 87.5%;
+ padding: 0.1em 0.3em;
+}
+
+.combobox button {
+ width: 1.25rem;
+ border-left: none;
+ outline: none;
+}
+
+.combobox button[aria-expanded="true"] svg {
+ transform: rotate(180deg) translate(0, -1px);
+}
+
+.combobox .group.focus {
+ outline: 2px solid black;
+ outline-offset: 1px;
+}
+
+.combobox .group.focus input,
+.combobox .group.focus button {
+ background-color: #def;
+}
+
+.combobox button polygon.outline {
+ stroke: transparent;
+ fill: transparent;
+}
+
+.combobox button[aria-expanded="true"] polygon.outline,
+.combobox .group.focus polygon.outline {
+ stroke: white;
+ fill: white;
+}
+
+.combobox polygon.arrow {
+ fill: gray;
+ stroke: gray;
+}
+
+ul[role="listbox"] {
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ left: 0;
+ top: 1.75rem;
+ list-style: none;
+ background-color: white;
+ display: none;
+ box-sizing: border-box;
+ border: 1px gray solid;
+ border-top: none;
+ max-height: 11.4em;
+ width: 12rem;
+ overflow: scroll;
+ overflow-x: hidden;
+ font-size: 87.5%;
+}
+
+ul[role="listbox"] li[role="option"] {
+ display: block;
+ padding-left: 0.3em;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ margin: 0;
+}
+
+/* focus and hover styling */
+
+[role="listbox"].focus [role="option"][aria-selected="true"] {
+ background-color: #def;
+ padding-top: 0;
+ padding-bottom: 0;
+ border-top: 0.2em solid #8ccbf2;
+ border-bottom: 0.2em solid #8ccbf2;
+}
+
+@media (forced-colors: active), (-ms-high-contrast: active) {
+ [role="listbox"].focus [role="option"][aria-selected="true"] {
+ -ms-high-contrast-adjust: none; /* disable the backgrounds that Edge puts behind text */
+ background-color: highlight;
+ color: highlighttext;
+ border-color: currentColor;
+ }
+}
+
+[role="listbox"] li[role="option"]:hover {
+ background-color: #def;
+}
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/combobox-datepicker.css b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/combobox-datepicker.css
new file mode 100644
index 000000000..5370b4edf
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/combobox-datepicker.css
@@ -0,0 +1,249 @@
+.combobox-datepicker {
+ margin-top: 1em;
+ position: relative;
+}
+
+.combobox-datepicker .group {
+ display: inline-flex;
+ position: relative;
+ width: 12.125rem;
+}
+
+.combobox-datepicker label {
+ display: block;
+}
+
+.combobox-datepicker .group input,
+.combobox-datepicker .group button {
+ background-color: white;
+ color: black;
+ box-sizing: border-box;
+ height: 1.75rem;
+ padding: 0;
+ margin: 0;
+ vertical-align: bottom;
+ border: 1px solid gray;
+ position: relative;
+}
+
+.combobox-datepicker .group input {
+ width: 10.75rem;
+ border-right: none;
+ outline: none;
+ font-size: 87.5%;
+ padding: 0.1em 0.3em;
+}
+
+.combobox-datepicker .group button {
+ position: absolute;
+ left: 10.75rem;
+ padding-right: 0.125rem;
+ border-left: none;
+ outline: none;
+}
+
+.combobox-datepicker .group .desc {
+ position: absolute;
+ top: 2em;
+ left: 0;
+ margin-top: 0.1em;
+ font-size: 90%;
+ font-style: italic;
+ letter-spacing: 0.025em;
+}
+
+.combobox-datepicker .group.focus {
+ outline: 2px solid black;
+ outline-offset: 2px;
+}
+
+.combobox-datepicker .group.focus input,
+.combobox-datepicker .group.focus button {
+ background-color: #def;
+}
+
+.combobox-datepicker .group polygon {
+ fill: gray;
+ stroke: transparent;
+}
+
+.combobox-datepicker .group button[aria-expanded="true"] polygon,
+.combobox-datepicker .group.focus polygon {
+ fill: black;
+ stroke: white;
+}
+
+.combobox-datepicker .group button.open svg {
+ transform: rotate(180deg) translate(0, -1px);
+}
+
+.combobox-datepicker .dialog {
+ position: absolute;
+ width: 320px;
+ clear: both;
+ border: 3px solid hsl(216, 80%, 51%);
+ margin-top: 1em;
+ border-radius: 5px;
+ padding: 0;
+ background-color: #fff;
+}
+
+.combobox-datepicker .header {
+ cursor: default;
+ background-color: hsl(216, 80%, 51%);
+ padding: 7px;
+ font-weight: bold;
+ text-transform: uppercase;
+ color: white;
+ display: flex;
+ justify-content: space-around;
+}
+
+.combobox-datepicker .dialog h2 {
+ margin: 0;
+ padding: 0;
+ display: inline-block;
+ font-size: 1em;
+ color: white;
+ text-transform: none;
+ font-weight: bold;
+}
+
+.combobox-datepicker .dialog button {
+ border-style: none;
+ background: transparent;
+}
+
+.combobox-datepicker .dialog button::-moz-focus-inner {
+ border: 0;
+}
+
+.combobox-datepicker .dates {
+ width: 320px;
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-top: 1em;
+}
+
+.combobox-datepicker .prev-year,
+.combobox-datepicker .prev-month,
+.combobox-datepicker .next-month,
+.combobox-datepicker .next-year {
+ padding: 4px;
+ width: 24px;
+ height: 24px;
+ color: white;
+}
+
+.combobox-datepicker .prev-year:focus,
+.combobox-datepicker .prev-month:focus,
+.combobox-datepicker .next-month:focus,
+.combobox-datepicker .next-year:focus {
+ padding: 2px;
+ border: 2px solid white;
+ border-radius: 4px;
+ outline: 0;
+}
+
+.combobox-datepicker .prev-year:hover,
+.combobox-datepicker .prev-month:hover,
+.combobox-datepicker .next-month:hover,
+.combobox-datepicker .next-year:hover {
+ padding: 3px;
+ border: 1px solid white;
+ border-radius: 4px;
+ outline: 0;
+}
+
+.combobox-datepicker .dialog-ok-cancel-group {
+ text-align: right;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ margin-right: 1em;
+}
+
+.combobox-datepicker .dialog-ok-cancel-group button {
+ padding: 6px;
+ margin-left: 1em;
+ width: 5em;
+ background-color: hsl(216, 80%, 92%);
+ font-size: 0.85em;
+ color: black;
+ outline: none;
+ border-radius: 5px;
+}
+
+.combobox-datepicker .dialog-button:focus {
+ padding: 4px;
+ border: 2px solid black;
+}
+
+.combobox-datepicker .dialog-button:hover {
+ padding: 5px;
+ border: 1px solid black;
+}
+
+.combobox-datepicker .fa-calendar-alt {
+ color: hsl(216, 89%, 51%);
+}
+
+.combobox-datepicker .month-year {
+ display: inline-block;
+ width: 12em;
+ text-align: center;
+}
+
+.combobox-datepicker .dates th,
+.combobox-datepicker .dates td {
+ text-align: center;
+}
+
+.combobox-datepicker .dates tr {
+ border: 1px solid black;
+}
+
+.combobox-datepicker .dates td {
+ padding: 3px;
+ margin: 0;
+ line-height: inherit;
+ height: 40px;
+ width: 40px;
+ border-radius: 5px;
+ font-size: 15px;
+ background: #eee;
+}
+
+.combobox-datepicker .dates td[aria-selected] {
+ padding: 1px;
+ border: 2px dotted black;
+ background-color: hsl(216, 80%, 96%);
+}
+
+.combobox-datepicker .dates td[tabindex="0"] {
+ background-color: hsl(216, 80%, 51%);
+ color: white;
+}
+
+.combobox-datepicker .dates td:hover {
+ padding: 0;
+ background-color: hsl(216, 80%, 92%);
+}
+
+.combobox-datepicker .dates td:not(.disabled):hover {
+ padding: 2px;
+ border: 1px solid rgb(100, 100, 100);
+}
+
+.combobox-datepicker .dates td:focus {
+ padding: 1px;
+ border: 2px solid rgb(100, 100, 100);
+ outline: 0;
+}
+
+.combobox-datepicker .dialog-message {
+ padding-top: 0.25em;
+ padding-left: 1em;
+ height: 1.75em;
+ background: hsl(216, 80%, 51%);
+ color: white;
+}
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/grid-combo.css b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/grid-combo.css
new file mode 100644
index 000000000..29324bea1
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/grid-combo.css
@@ -0,0 +1,88 @@
+.hidden {
+ display: none;
+}
+
+.combobox-wrapper {
+ display: inline-block;
+ position: relative;
+ font-size: 16px;
+}
+
+.combobox-label {
+ font-size: 14px;
+ font-weight: bold;
+ margin-right: 5px;
+}
+
+.listbox,
+.grid {
+ min-width: 230px;
+ background: white;
+ border: 1px solid #ccc;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ top: 1.7em;
+ z-index: 1;
+}
+
+.listbox .result {
+ cursor: default;
+ margin: 0;
+}
+
+.grid .result-row {
+ padding: 2px;
+ cursor: default;
+ margin: 0;
+}
+
+.listbox .result:hover,
+.grid .result-row:hover {
+ background: rgb(139, 189, 225);
+}
+
+.listbox .focused,
+.grid .focused {
+ background: rgb(139, 189, 225);
+}
+
+.grid .focused-cell {
+ outline-style: dotted;
+ outline-color: green;
+}
+
+.combobox-wrapper input {
+ font-size: inherit;
+ border: 1px solid #aaa;
+ border-radius: 2px;
+ line-height: 1.5em;
+ padding-right: 30px;
+ width: 200px;
+}
+
+.combobox-dropdown {
+ position: absolute;
+ right: 0;
+ top: 0;
+ padding: 0 0 2px;
+ height: 1.5em;
+ border-radius: 0 2px 2px 0;
+ border: 1px solid #aaa;
+}
+
+.grid .result-cell {
+ display: inline-block;
+ cursor: default;
+ margin: 0;
+ padding: 0 5px;
+}
+
+.grid .result-cell:last-child {
+ float: right;
+ font-size: 12px;
+ font-weight: 200;
+ color: #333;
+ line-height: 24px;
+}
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/select-only.css b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/select-only.css
new file mode 100644
index 000000000..1d8beb3a7
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/css/select-only.css
@@ -0,0 +1,103 @@
+.combo *,
+.combo *::before,
+.combo *::after {
+ box-sizing: border-box;
+}
+
+.combo {
+ display: block;
+ margin-bottom: 1.5em;
+ max-width: 400px;
+ position: relative;
+}
+
+.combo::after {
+ border-bottom: 2px solid rgba(0, 0, 0, 0.75);
+ border-right: 2px solid rgba(0, 0, 0, 0.75);
+ content: "";
+ display: block;
+ height: 12px;
+ pointer-events: none;
+ position: absolute;
+ right: 16px;
+ top: 50%;
+ transform: translate(0, -65%) rotate(45deg);
+ width: 12px;
+}
+
+.combo-input {
+ background-color: #f5f5f5;
+ border: 2px solid rgba(0, 0, 0, 0.75);
+ border-radius: 4px;
+ display: block;
+ font-size: 1em;
+ min-height: calc(1.4em + 26px);
+ padding: 12px 16px 14px;
+ text-align: left;
+ width: 100%;
+}
+
+.open .combo-input {
+ border-radius: 4px 4px 0 0;
+}
+
+.combo-input:focus {
+ border-color: #0067b8;
+ box-shadow: 0 0 4px 2px #0067b8;
+ outline: 4px solid transparent;
+}
+
+.combo-label {
+ display: block;
+ font-size: 20px;
+ font-weight: 100;
+ margin-bottom: 0.25em;
+}
+
+.combo-menu {
+ background-color: #f5f5f5;
+ border: 1px solid rgba(0, 0, 0, 0.75);
+ border-radius: 0 0 4px 4px;
+ display: none;
+ max-height: 300px;
+ overflow-y: scroll;
+ left: 0;
+ position: absolute;
+ top: 100%;
+ width: 100%;
+ z-index: 100;
+}
+
+.open .combo-menu {
+ display: block;
+}
+
+.combo-option {
+ padding: 10px 12px 12px;
+}
+
+.combo-option:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.combo-option.option-current {
+ outline: 3px solid #0067b8;
+ outline-offset: -3px;
+}
+
+.combo-option[aria-selected="true"] {
+ padding-right: 30px;
+ position: relative;
+}
+
+.combo-option[aria-selected="true"]::after {
+ border-bottom: 2px solid #000;
+ border-right: 2px solid #000;
+ content: "";
+ height: 16px;
+ position: absolute;
+ right: 15px;
+ top: 50%;
+ transform: translate(0, -50%) rotate(45deg);
+ width: 8px;
+}
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/combobox-autocomplete.js b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/combobox-autocomplete.js
new file mode 100644
index 000000000..95f013077
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/combobox-autocomplete.js
@@ -0,0 +1,573 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ */
+
+'use strict';
+
+var ComboboxAutocomplete = function (comboboxNode, buttonNode, listboxNode) {
+ this.comboboxNode = comboboxNode;
+ this.buttonNode = buttonNode;
+ this.listboxNode = listboxNode;
+
+ this.comboboxHasVisualFocus = false;
+ this.listboxHasVisualFocus = false;
+
+ this.hasHover = false;
+
+ this.isNone = false;
+ this.isList = false;
+ this.isBoth = false;
+
+ this.allOptions = [];
+
+ this.option = null;
+ this.firstOption = null;
+ this.lastOption = null;
+
+ this.filteredOptions = [];
+ this.filter = '';
+};
+
+ComboboxAutocomplete.prototype.init = function () {
+ var autocomplete = this.comboboxNode.getAttribute('aria-autocomplete');
+
+ if (typeof autocomplete === 'string') {
+ autocomplete = autocomplete.toLowerCase();
+ this.isNone = autocomplete === 'none';
+ this.isList = autocomplete === 'list';
+ this.isBoth = autocomplete === 'both';
+ } else {
+ // default value of autocomplete
+ this.isNone = true;
+ }
+
+ this.comboboxNode.addEventListener(
+ 'keydown',
+ this.handleComboboxKeyDown.bind(this)
+ );
+ this.comboboxNode.addEventListener(
+ 'keyup',
+ this.handleComboboxKeyUp.bind(this)
+ );
+ this.comboboxNode.addEventListener(
+ 'click',
+ this.handleComboboxClick.bind(this)
+ );
+ this.comboboxNode.addEventListener(
+ 'focus',
+ this.handleComboboxFocus.bind(this)
+ );
+ this.comboboxNode.addEventListener(
+ 'blur',
+ this.handleComboboxBlur.bind(this)
+ );
+
+ // initialize pop up menu
+
+ this.listboxNode.addEventListener(
+ 'mouseover',
+ this.handleListboxMouseover.bind(this)
+ );
+ this.listboxNode.addEventListener(
+ 'mouseout',
+ this.handleListboxMouseout.bind(this)
+ );
+
+ // Traverse the element children of domNode: configure each with
+ // option role behavior and store reference in.options array.
+ var nodes = this.listboxNode.getElementsByTagName('LI');
+
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ this.allOptions.push(node);
+
+ node.addEventListener('click', this.handleOptionClick.bind(this));
+ node.addEventListener('mouseover', this.handleOptionMouseover.bind(this));
+ node.addEventListener('mouseout', this.handleOptionMouseout.bind(this));
+ }
+
+ this.filterOptions();
+
+ // Open Button
+
+ var button = this.comboboxNode.nextElementSibling;
+
+ if (button && button.tagName === 'BUTTON') {
+ button.addEventListener('click', this.handleButtonClick.bind(this));
+ }
+};
+
+ComboboxAutocomplete.prototype.getLowercaseContent = function (node) {
+ return node.textContent.toLowerCase();
+};
+
+ComboboxAutocomplete.prototype.setActiveDescendant = function (option) {
+ if (option && this.listboxHasVisualFocus) {
+ this.comboboxNode.setAttribute('aria-activedescendant', option.id);
+ } else {
+ this.comboboxNode.setAttribute('aria-activedescendant', '');
+ }
+};
+
+ComboboxAutocomplete.prototype.setValue = function (value) {
+ this.filter = value;
+ this.comboboxNode.value = this.filter;
+ this.comboboxNode.setSelectionRange(this.filter.length, this.filter.length);
+ this.filterOptions();
+};
+
+ComboboxAutocomplete.prototype.setOption = function (option, flag) {
+ if (typeof flag !== 'boolean') {
+ flag = false;
+ }
+
+ if (option) {
+ this.option = option;
+ this.setCurrentOptionStyle(this.option);
+ this.setActiveDescendant(this.option);
+
+ if (this.isBoth) {
+ this.comboboxNode.value = this.option.textContent;
+ if (flag) {
+ this.comboboxNode.setSelectionRange(
+ this.option.textContent.length,
+ this.option.textContent.length
+ );
+ } else {
+ this.comboboxNode.setSelectionRange(
+ this.filter.length,
+ this.option.textContent.length
+ );
+ }
+ }
+ }
+};
+
+ComboboxAutocomplete.prototype.setVisualFocusCombobox = function () {
+ this.listboxNode.classList.remove('focus');
+ this.comboboxNode.parentNode.classList.add('focus'); // set the focus class to the parent for easier styling
+ this.comboboxHasVisualFocus = true;
+ this.listboxHasVisualFocus = false;
+ this.setActiveDescendant(false);
+};
+
+ComboboxAutocomplete.prototype.setVisualFocusListbox = function () {
+ this.comboboxNode.parentNode.classList.remove('focus');
+ this.comboboxHasVisualFocus = false;
+ this.listboxHasVisualFocus = true;
+ this.listboxNode.classList.add('focus');
+ this.setActiveDescendant(this.option);
+};
+
+ComboboxAutocomplete.prototype.removeVisualFocusAll = function () {
+ this.comboboxNode.parentNode.classList.remove('focus');
+ this.comboboxHasVisualFocus = false;
+ this.listboxHasVisualFocus = false;
+ this.listboxNode.classList.remove('focus');
+ this.option = null;
+ this.setActiveDescendant(false);
+};
+
+// ComboboxAutocomplete Events
+
+ComboboxAutocomplete.prototype.filterOptions = function () {
+ // do not filter any options if autocomplete is none
+ if (this.isNone) {
+ this.filter = '';
+ }
+
+ var option = null;
+ var currentOption = this.option;
+ var filter = this.filter.toLowerCase();
+
+ this.filteredOptions = [];
+ this.listboxNode.innerHTML = '';
+
+ for (var i = 0; i < this.allOptions.length; i++) {
+ option = this.allOptions[i];
+ if (
+ filter.length === 0 ||
+ this.getLowercaseContent(option).indexOf(filter) === 0
+ ) {
+ this.filteredOptions.push(option);
+ this.listboxNode.appendChild(option);
+ }
+ }
+
+ // Use populated options array to initialize firstOption and lastOption.
+ var numItems = this.filteredOptions.length;
+ if (numItems > 0) {
+ this.firstOption = this.filteredOptions[0];
+ this.lastOption = this.filteredOptions[numItems - 1];
+
+ if (currentOption && this.filteredOptions.indexOf(currentOption) >= 0) {
+ option = currentOption;
+ } else {
+ option = this.firstOption;
+ }
+ } else {
+ this.firstOption = null;
+ option = null;
+ this.lastOption = null;
+ }
+
+ return option;
+};
+
+ComboboxAutocomplete.prototype.setCurrentOptionStyle = function (option) {
+ for (var i = 0; i < this.filteredOptions.length; i++) {
+ var opt = this.filteredOptions[i];
+ if (opt === option) {
+ opt.setAttribute('aria-selected', 'true');
+ if (
+ this.listboxNode.scrollTop + this.listboxNode.offsetHeight <
+ opt.offsetTop + opt.offsetHeight
+ ) {
+ this.listboxNode.scrollTop =
+ opt.offsetTop + opt.offsetHeight - this.listboxNode.offsetHeight;
+ } else if (this.listboxNode.scrollTop > opt.offsetTop + 2) {
+ this.listboxNode.scrollTop = opt.offsetTop;
+ }
+ } else {
+ opt.removeAttribute('aria-selected');
+ }
+ }
+};
+
+ComboboxAutocomplete.prototype.getPreviousOption = function (currentOption) {
+ if (currentOption !== this.firstOption) {
+ var index = this.filteredOptions.indexOf(currentOption);
+ return this.filteredOptions[index - 1];
+ }
+ return this.lastOption;
+};
+
+ComboboxAutocomplete.prototype.getNextOption = function (currentOption) {
+ if (currentOption !== this.lastOption) {
+ var index = this.filteredOptions.indexOf(currentOption);
+ return this.filteredOptions[index + 1];
+ }
+ return this.firstOption;
+};
+
+/* MENU DISPLAY METHODS */
+
+ComboboxAutocomplete.prototype.doesOptionHaveFocus = function () {
+ return this.comboboxNode.getAttribute('aria-activedescendant') !== '';
+};
+
+ComboboxAutocomplete.prototype.isOpen = function () {
+ return this.listboxNode.style.display === 'block';
+};
+
+ComboboxAutocomplete.prototype.isClosed = function () {
+ return this.listboxNode.style.display !== 'block';
+};
+
+ComboboxAutocomplete.prototype.hasOptions = function () {
+ return this.filteredOptions.length;
+};
+
+ComboboxAutocomplete.prototype.open = function () {
+ this.listboxNode.style.display = 'block';
+ this.comboboxNode.setAttribute('aria-expanded', 'true');
+ this.buttonNode.setAttribute('aria-expanded', 'true');
+};
+
+ComboboxAutocomplete.prototype.close = function (force) {
+ if (typeof force !== 'boolean') {
+ force = false;
+ }
+
+ if (
+ force ||
+ (!this.comboboxHasVisualFocus &&
+ !this.listboxHasVisualFocus &&
+ !this.hasHover)
+ ) {
+ this.setCurrentOptionStyle(false);
+ this.listboxNode.style.display = 'none';
+ this.comboboxNode.setAttribute('aria-expanded', 'false');
+ this.buttonNode.setAttribute('aria-expanded', 'false');
+ this.setActiveDescendant(false);
+ }
+};
+
+/* combobox Events */
+
+ComboboxAutocomplete.prototype.handleComboboxKeyDown = function (event) {
+ var flag = false,
+ char = event.key,
+ altKey = event.altKey;
+
+ if (event.ctrlKey || event.shiftKey) {
+ return;
+ }
+
+ switch (event.key) {
+ case 'Enter':
+ if (this.listboxHasVisualFocus) {
+ this.setValue(this.option.textContent);
+ }
+ this.close(true);
+ this.setVisualFocusCombobox();
+ flag = true;
+ break;
+
+ case 'Down':
+ case 'ArrowDown':
+ if (this.filteredOptions.length > 0) {
+ if (altKey) {
+ this.open();
+ } else {
+ this.open();
+ if (
+ this.listboxHasVisualFocus ||
+ (this.isBoth && this.filteredOptions.length > 1)
+ ) {
+ this.setOption(this.getNextOption(this.option), true);
+ this.setVisualFocusListbox();
+ } else {
+ this.setOption(this.firstOption, true);
+ this.setVisualFocusListbox();
+ }
+ }
+ }
+ flag = true;
+ break;
+
+ case 'Up':
+ case 'ArrowUp':
+ if (this.hasOptions()) {
+ if (this.listboxHasVisualFocus) {
+ this.setOption(this.getPreviousOption(this.option), true);
+ } else {
+ this.open();
+ if (!altKey) {
+ this.setOption(this.lastOption, true);
+ this.setVisualFocusListbox();
+ }
+ }
+ }
+ flag = true;
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ if (this.isOpen()) {
+ this.close(true);
+ this.filter = this.comboboxNode.value;
+ this.filterOptions();
+ this.setVisualFocusCombobox();
+ } else {
+ this.setValue('');
+ this.comboboxNode.value = '';
+ }
+ this.option = null;
+ flag = true;
+ break;
+
+ case 'Tab':
+ this.close(true);
+ if (this.listboxHasVisualFocus) {
+ if (this.option) {
+ this.setValue(this.option.textContent);
+ }
+ }
+ break;
+
+ case 'Home':
+ this.comboboxNode.setSelectionRange(0, 0);
+ flag = true;
+ break;
+
+ case 'End':
+ var length = this.comboboxNode.value.length;
+ this.comboboxNode.setSelectionRange(length, length);
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxAutocomplete.prototype.isPrintableCharacter = function (str) {
+ return str.length === 1 && str.match(/\S/);
+};
+
+ComboboxAutocomplete.prototype.handleComboboxKeyUp = function (event) {
+ var flag = false,
+ option = null,
+ char = event.key;
+
+ if (this.isPrintableCharacter(char)) {
+ this.filter += char;
+ }
+
+ // this is for the case when a selection in the textbox has been deleted
+ if (this.comboboxNode.value.length < this.filter.length) {
+ this.filter = this.comboboxNode.value;
+ this.option = null;
+ this.filterOptions();
+ }
+
+ if (event.key === 'Escape' || event.key === 'Esc') {
+ return;
+ }
+
+ switch (event.key) {
+ case 'Backspace':
+ this.setVisualFocusCombobox();
+ this.setCurrentOptionStyle(false);
+ this.filter = this.comboboxNode.value;
+ this.option = null;
+ this.filterOptions();
+ flag = true;
+ break;
+
+ case 'Left':
+ case 'ArrowLeft':
+ case 'Right':
+ case 'ArrowRight':
+ case 'Home':
+ case 'End':
+ if (this.isBoth) {
+ this.filter = this.comboboxNode.value;
+ } else {
+ this.option = null;
+ this.setCurrentOptionStyle(false);
+ }
+ this.setVisualFocusCombobox();
+ flag = true;
+ break;
+
+ default:
+ if (this.isPrintableCharacter(char)) {
+ this.setVisualFocusCombobox();
+ this.setCurrentOptionStyle(false);
+ flag = true;
+
+ if (this.isList || this.isBoth) {
+ option = this.filterOptions();
+ if (option) {
+ if (this.isClosed() && this.comboboxNode.value.length) {
+ this.open();
+ }
+
+ if (
+ this.getLowercaseContent(option).indexOf(
+ this.comboboxNode.value.toLowerCase()
+ ) === 0
+ ) {
+ this.option = option;
+ if (this.isBoth || this.listboxHasVisualFocus) {
+ this.setCurrentOptionStyle(option);
+ if (this.isBoth) {
+ this.setOption(option);
+ }
+ }
+ } else {
+ this.option = null;
+ this.setCurrentOptionStyle(false);
+ }
+ } else {
+ this.close();
+ this.option = null;
+ this.setActiveDescendant(false);
+ }
+ } else if (this.comboboxNode.value.length) {
+ this.open();
+ }
+ }
+
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxAutocomplete.prototype.handleComboboxClick = function (event) {
+ if (this.isOpen()) {
+ this.close(true);
+ } else {
+ this.open();
+ }
+};
+
+ComboboxAutocomplete.prototype.handleComboboxFocus = function (event) {
+ this.filter = this.comboboxNode.value;
+ this.filterOptions();
+ this.setVisualFocusCombobox();
+ this.option = null;
+ this.setCurrentOptionStyle(null);
+};
+
+ComboboxAutocomplete.prototype.handleComboboxBlur = function (event) {
+ this.comboboxHasVisualFocus = false;
+ this.setCurrentOptionStyle(null);
+ this.removeVisualFocusAll();
+ setTimeout(this.close.bind(this, false), 300);
+};
+
+ComboboxAutocomplete.prototype.handleButtonClick = function (event) {
+ if (this.isOpen()) {
+ this.close(true);
+ } else {
+ this.open();
+ }
+ this.comboboxNode.focus();
+ this.setVisualFocusCombobox();
+};
+
+/* Listbox Events */
+
+ComboboxAutocomplete.prototype.handleListboxMouseover = function (event) {
+ this.hasHover = true;
+};
+
+ComboboxAutocomplete.prototype.handleListboxMouseout = function (event) {
+ this.hasHover = false;
+ setTimeout(this.close.bind(this, false), 300);
+};
+
+// Listbox Option Events
+
+ComboboxAutocomplete.prototype.handleOptionClick = function (event) {
+ this.comboboxNode.value = event.target.textContent;
+ this.close(true);
+};
+
+ComboboxAutocomplete.prototype.handleOptionMouseover = function (event) {
+ this.hasHover = true;
+ this.open();
+};
+
+ComboboxAutocomplete.prototype.handleOptionMouseout = function (event) {
+ this.hasHover = false;
+ setTimeout(this.close.bind(this, false), 300);
+};
+
+// Initialize comboboxes
+
+window.addEventListener('load', function () {
+ var comboboxes = document.querySelectorAll('.combobox-list');
+
+ for (var i = 0; i < comboboxes.length; i++) {
+ var combobox = comboboxes[i];
+ var comboboxNode = combobox.querySelector('input');
+ var buttonNode = combobox.querySelector('button');
+ var listboxNode = combobox.querySelector('[role="listbox"]');
+ var cba = new ComboboxAutocomplete(comboboxNode, buttonNode, listboxNode);
+ cba.init();
+ }
+});
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/combobox-datepicker.js b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/combobox-datepicker.js
new file mode 100644
index 000000000..c791141ea
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/combobox-datepicker.js
@@ -0,0 +1,866 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: ComboboxDatePicker.js
+ */
+
+'use strict';
+
+var ComboboxDatePicker = function (cdp) {
+ this.buttonLabel = 'Date';
+ this.monthLabels = [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December',
+ ];
+
+ this.messageCursorKeys = 'Cursor keys can navigate dates';
+ this.lastMessage = '';
+
+ this.comboboxNode = cdp.querySelector('input[type="text"]');
+ this.buttonNode = cdp.querySelector('.group button');
+ this.dialogNode = cdp.querySelector('[role="dialog"]');
+ this.messageNode = this.dialogNode.querySelector('.dialog-message');
+
+ this.monthYearNode = this.dialogNode.querySelector('.month-year');
+
+ this.prevYearNode = this.dialogNode.querySelector('.prev-year');
+ this.prevMonthNode = this.dialogNode.querySelector('.prev-month');
+ this.nextMonthNode = this.dialogNode.querySelector('.next-month');
+ this.nextYearNode = this.dialogNode.querySelector('.next-year');
+
+ this.okButtonNode = this.dialogNode.querySelector('button[value="ok"]');
+ this.cancelButtonNode = this.dialogNode.querySelector(
+ 'button[value="cancel"]'
+ );
+
+ this.tbodyNode = this.dialogNode.querySelector('table.dates tbody');
+
+ this.lastRowNode = null;
+
+ this.days = [];
+
+ this.focusDay = new Date();
+ this.selectedDay = new Date(0, 0, 1);
+
+ this.isMouseDownOnBackground = false;
+};
+
+ComboboxDatePicker.prototype.init = function () {
+ this.comboboxNode.addEventListener(
+ 'keydown',
+ this.handleComboboxKeyDown.bind(this)
+ );
+ this.comboboxNode.addEventListener(
+ 'click',
+ this.handleComboboxClick.bind(this)
+ );
+ this.comboboxNode.addEventListener(
+ 'focus',
+ this.handleComboboxFocus.bind(this)
+ );
+ this.comboboxNode.addEventListener(
+ 'blur',
+ this.handleComboboxBlur.bind(this)
+ );
+
+ this.buttonNode.addEventListener(
+ 'keydown',
+ this.handleButtonKeyDown.bind(this)
+ );
+ this.buttonNode.addEventListener('click', this.handleButtonClick.bind(this));
+
+ this.okButtonNode.addEventListener('click', this.handleOkButton.bind(this));
+ this.okButtonNode.addEventListener('keydown', this.handleOkButton.bind(this));
+
+ this.cancelButtonNode.addEventListener(
+ 'click',
+ this.handleCancelButton.bind(this)
+ );
+ this.cancelButtonNode.addEventListener(
+ 'keydown',
+ this.handleCancelButton.bind(this)
+ );
+
+ this.prevMonthNode.addEventListener(
+ 'click',
+ this.handlePreviousMonthButton.bind(this)
+ );
+ this.nextMonthNode.addEventListener(
+ 'click',
+ this.handleNextMonthButton.bind(this)
+ );
+ this.prevYearNode.addEventListener(
+ 'click',
+ this.handlePreviousYearButton.bind(this)
+ );
+ this.nextYearNode.addEventListener(
+ 'click',
+ this.handleNextYearButton.bind(this)
+ );
+
+ this.prevMonthNode.addEventListener(
+ 'keydown',
+ this.handlePreviousMonthButton.bind(this)
+ );
+ this.nextMonthNode.addEventListener(
+ 'keydown',
+ this.handleNextMonthButton.bind(this)
+ );
+ this.prevYearNode.addEventListener(
+ 'keydown',
+ this.handlePreviousYearButton.bind(this)
+ );
+ this.nextYearNode.addEventListener(
+ 'keydown',
+ this.handleNextYearButton.bind(this)
+ );
+
+ document.body.addEventListener(
+ 'mouseup',
+ this.handleBackgroundMouseUp.bind(this),
+ true
+ );
+
+ // Create Grid of Dates
+
+ this.tbodyNode.innerHTML = '';
+ for (var i = 0; i < 6; i++) {
+ var row = this.tbodyNode.insertRow(i);
+ this.lastRowNode = row;
+ for (var j = 0; j < 7; j++) {
+ var cell = document.createElement('td');
+
+ cell.setAttribute('tabindex', '-1');
+ cell.addEventListener('click', this.handleDayClick.bind(this));
+ cell.addEventListener('keydown', this.handleDayKeyDown.bind(this));
+ cell.addEventListener('focus', this.handleDayFocus.bind(this));
+
+ cell.innerHTML = '-1';
+
+ row.appendChild(cell);
+ this.days.push(cell);
+ }
+ }
+
+ this.updateGrid();
+ this.close(false);
+};
+
+ComboboxDatePicker.prototype.isSameDay = function (day1, day2) {
+ return (
+ day1.getFullYear() == day2.getFullYear() &&
+ day1.getMonth() == day2.getMonth() &&
+ day1.getDate() == day2.getDate()
+ );
+};
+
+ComboboxDatePicker.prototype.isNotSameMonth = function (day1, day2) {
+ return (
+ day1.getFullYear() != day2.getFullYear() ||
+ day1.getMonth() != day2.getMonth()
+ );
+};
+
+ComboboxDatePicker.prototype.updateGrid = function () {
+ var i, flag;
+ var fd = this.focusDay;
+
+ this.monthYearNode.innerHTML =
+ this.monthLabels[fd.getMonth()] + ' ' + fd.getFullYear();
+
+ var firstDayOfMonth = new Date(fd.getFullYear(), fd.getMonth(), 1);
+ var dayOfWeek = firstDayOfMonth.getDay();
+
+ firstDayOfMonth.setDate(firstDayOfMonth.getDate() - dayOfWeek);
+
+ var d = new Date(firstDayOfMonth);
+
+ for (i = 0; i < this.days.length; i++) {
+ flag = d.getMonth() != fd.getMonth();
+ this.updateDate(this.days[i], flag, d, this.isSameDay(d, this.selectedDay));
+ d.setDate(d.getDate() + 1);
+
+ // Hide last row if all disabled dates
+ if (i === 35) {
+ if (flag) {
+ this.lastRowNode.style.visibility = 'hidden';
+ } else {
+ this.lastRowNode.style.visibility = 'visible';
+ }
+ }
+ }
+};
+
+ComboboxDatePicker.prototype.setFocusDay = function (flag) {
+ if (typeof flag !== 'boolean') {
+ flag = true;
+ }
+
+ var fd = this.focusDay;
+ var getDayFromDataDateAttribute = this.getDayFromDataDateAttribute;
+
+ function checkDay(domNode) {
+ var d = getDayFromDataDateAttribute(domNode);
+
+ domNode.setAttribute('tabindex', '-1');
+ if (this.isSameDay(d, fd)) {
+ domNode.setAttribute('tabindex', '0');
+ if (flag) {
+ domNode.focus();
+ }
+ }
+ }
+
+ this.days.forEach(checkDay.bind(this));
+};
+
+ComboboxDatePicker.prototype.open = function () {
+ this.dialogNode.style.display = 'block';
+ this.dialogNode.style.zIndex = 2;
+
+ this.comboboxNode.setAttribute('aria-expanded', 'true');
+ this.buttonNode.classList.add('open');
+ this.getDateFromCombobox();
+ this.updateGrid();
+};
+
+ComboboxDatePicker.prototype.isOpen = function () {
+ return window.getComputedStyle(this.dialogNode).display !== 'none';
+};
+
+ComboboxDatePicker.prototype.close = function (flag) {
+ if (typeof flag !== 'boolean') {
+ // Default is to move focus to combobox
+ flag = true;
+ }
+
+ this.setMessage('');
+ this.dialogNode.style.display = 'none';
+ this.comboboxNode.setAttribute('aria-expanded', 'false');
+ this.buttonNode.classList.remove('open');
+
+ if (flag) {
+ this.comboboxNode.focus();
+ }
+};
+
+ComboboxDatePicker.prototype.handleOkButton = function (event) {
+ var flag = false;
+
+ switch (event.type) {
+ case 'keydown':
+ switch (event.key) {
+ case 'Tab':
+ if (!event.shiftKey) {
+ this.prevYearNode.focus();
+ flag = true;
+ }
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ this.close();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'click':
+ this.setComboboxDate();
+ this.close();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handleCancelButton = function (event) {
+ var flag = false;
+
+ switch (event.type) {
+ case 'keydown':
+ switch (event.key) {
+ case 'Esc':
+ case 'Escape':
+ this.close();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'click':
+ this.close();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handleNextYearButton = function (event) {
+ var flag = false;
+
+ switch (event.type) {
+ case 'keydown':
+ switch (event.key) {
+ case 'Esc':
+ case 'Escape':
+ this.close();
+ flag = true;
+ break;
+
+ case 'Enter':
+ this.moveToNextYear();
+ this.setFocusDay(false);
+ flag = true;
+ break;
+ }
+
+ break;
+
+ case 'click':
+ this.moveToNextYear();
+ this.setFocusDay(false);
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handlePreviousYearButton = function (event) {
+ var flag = false;
+
+ switch (event.type) {
+ case 'keydown':
+ switch (event.key) {
+ case 'Enter':
+ this.moveToPreviousYear();
+ this.setFocusDay(false);
+ flag = true;
+ break;
+
+ case 'Tab':
+ if (event.shiftKey) {
+ this.okButtonNode.focus();
+ flag = true;
+ }
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ this.close();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case 'click':
+ this.moveToPreviousYear();
+ this.setFocusDay(false);
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handleNextMonthButton = function (event) {
+ var flag = false;
+
+ switch (event.type) {
+ case 'keydown':
+ switch (event.key) {
+ case 'Esc':
+ case 'Escape':
+ this.close();
+ flag = true;
+ break;
+
+ case 'Enter':
+ this.moveToNextMonth();
+ this.setFocusDay(false);
+ flag = true;
+ break;
+ }
+
+ break;
+
+ case 'click':
+ this.moveToNextMonth();
+ this.setFocusDay(false);
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handlePreviousMonthButton = function (event) {
+ var flag = false;
+
+ switch (event.type) {
+ case 'keydown':
+ switch (event.key) {
+ case 'Esc':
+ case 'Escape':
+ this.close();
+ flag = true;
+ break;
+
+ case 'Enter':
+ this.moveToPreviousMonth();
+ this.setFocusDay(false);
+ flag = true;
+ break;
+ }
+
+ break;
+
+ case 'click':
+ this.moveToPreviousMonth();
+ this.setFocusDay(false);
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.moveFocusToDay = function (day) {
+ var d = this.focusDay;
+
+ this.focusDay = day;
+
+ if (
+ d.getMonth() != this.focusDay.getMonth() ||
+ d.getYear() != this.focusDay.getYear()
+ ) {
+ this.updateGrid();
+ }
+ this.setFocusDay();
+};
+
+ComboboxDatePicker.prototype.moveToNextYear = function () {
+ this.focusDay.setFullYear(this.focusDay.getFullYear() + 1);
+ this.updateGrid();
+};
+
+ComboboxDatePicker.prototype.moveToPreviousYear = function () {
+ this.focusDay.setFullYear(this.focusDay.getFullYear() - 1);
+ this.updateGrid();
+};
+
+ComboboxDatePicker.prototype.moveToNextMonth = function () {
+ this.focusDay.setMonth(this.focusDay.getMonth() + 1);
+ this.updateGrid();
+};
+
+ComboboxDatePicker.prototype.moveToPreviousMonth = function () {
+ this.focusDay.setMonth(this.focusDay.getMonth() - 1);
+ this.updateGrid();
+};
+
+ComboboxDatePicker.prototype.moveFocusToNextDay = function () {
+ var d = new Date(this.focusDay);
+ d.setDate(d.getDate() + 1);
+ this.moveFocusToDay(d);
+};
+
+ComboboxDatePicker.prototype.moveFocusToNextWeek = function () {
+ var d = new Date(this.focusDay);
+ d.setDate(d.getDate() + 7);
+ this.moveFocusToDay(d);
+};
+
+ComboboxDatePicker.prototype.moveFocusToPreviousDay = function () {
+ var d = new Date(this.focusDay);
+ d.setDate(d.getDate() - 1);
+ this.moveFocusToDay(d);
+};
+
+ComboboxDatePicker.prototype.moveFocusToPreviousWeek = function () {
+ var d = new Date(this.focusDay);
+ d.setDate(d.getDate() - 7);
+ this.moveFocusToDay(d);
+};
+
+ComboboxDatePicker.prototype.moveFocusToFirstDayOfWeek = function () {
+ var d = new Date(this.focusDay);
+ d.setDate(d.getDate() - d.getDay());
+ this.moveFocusToDay(d);
+};
+
+ComboboxDatePicker.prototype.moveFocusToLastDayOfWeek = function () {
+ var d = new Date(this.focusDay);
+ d.setDate(d.getDate() + (6 - d.getDay()));
+ this.moveFocusToDay(d);
+};
+
+// Day methods
+
+ComboboxDatePicker.prototype.isDayDisabled = function (domNode) {
+ return domNode.classList.contains('disabled');
+};
+
+ComboboxDatePicker.prototype.getDayFromDataDateAttribute = function (domNode) {
+ var parts = domNode.getAttribute('data-date').split('-');
+ return new Date(parts[0], parseInt(parts[1]) - 1, parts[2]);
+};
+
+ComboboxDatePicker.prototype.updateDate = function (
+ domNode,
+ disable,
+ day,
+ selected
+) {
+ var d = day.getDate().toString();
+ if (day.getDate() <= 9) {
+ d = '0' + d;
+ }
+
+ var m = day.getMonth() + 1;
+ if (day.getMonth() < 9) {
+ m = '0' + m;
+ }
+
+ domNode.setAttribute('tabindex', '-1');
+ domNode.removeAttribute('aria-selected');
+ domNode.setAttribute('data-date', day.getFullYear() + '-' + m + '-' + d);
+
+ if (disable) {
+ domNode.classList.add('disabled');
+ domNode.innerHTML = '';
+ } else {
+ domNode.classList.remove('disabled');
+ domNode.innerHTML = day.getDate();
+ if (selected) {
+ domNode.setAttribute('aria-selected', 'true');
+ domNode.setAttribute('tabindex', '0');
+ }
+ }
+};
+
+ComboboxDatePicker.prototype.updateSelected = function (domNode) {
+ for (var i = 0; i < this.days.length; i++) {
+ var day = this.days[i];
+ if (day === domNode) {
+ day.setAttribute('aria-selected', 'true');
+ } else {
+ day.removeAttribute('aria-selected');
+ }
+ }
+};
+
+ComboboxDatePicker.prototype.handleDayKeyDown = function (event) {
+ var flag = false;
+
+ switch (event.key) {
+ case 'Esc':
+ case 'Escape':
+ this.close();
+ break;
+
+ case ' ':
+ this.updateSelected(event.currentTarget);
+ this.setComboboxDate(event.currentTarget);
+ flag = true;
+ break;
+
+ case 'Enter':
+ this.setComboboxDate(event.currentTarget);
+ this.close();
+ break;
+
+ case 'Tab':
+ this.cancelButtonNode.focus();
+ if (event.shiftKey) {
+ this.nextYearNode.focus();
+ }
+ this.setMessage('');
+ flag = true;
+ break;
+
+ case 'Right':
+ case 'ArrowRight':
+ this.moveFocusToNextDay();
+ flag = true;
+ break;
+
+ case 'Left':
+ case 'ArrowLeft':
+ this.moveFocusToPreviousDay();
+ flag = true;
+ break;
+
+ case 'Down':
+ case 'ArrowDown':
+ this.moveFocusToNextWeek();
+ flag = true;
+ break;
+
+ case 'Up':
+ case 'ArrowUp':
+ this.moveFocusToPreviousWeek();
+ flag = true;
+ break;
+
+ case 'PageUp':
+ if (event.shiftKey) {
+ this.moveToPreviousYear();
+ } else {
+ this.moveToPreviousMonth();
+ }
+ this.setFocusDay();
+ flag = true;
+ break;
+
+ case 'PageDown':
+ if (event.shiftKey) {
+ this.moveToNextYear();
+ } else {
+ this.moveToNextMonth();
+ }
+ this.setFocusDay();
+ flag = true;
+ break;
+
+ case 'Home':
+ this.moveFocusToFirstDayOfWeek();
+ flag = true;
+ break;
+
+ case 'End':
+ this.moveFocusToLastDayOfWeek();
+ flag = true;
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handleDayClick = function (event) {
+ if (!this.isDayDisabled(event.currentTarget)) {
+ this.setComboboxDate(event.currentTarget);
+ this.close();
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+};
+
+ComboboxDatePicker.prototype.handleDayFocus = function () {
+ this.setMessage(this.messageCursorKeys);
+};
+
+// Combobox methods
+
+ComboboxDatePicker.prototype.setComboboxDate = function (domNode) {
+ var d = this.focusDay;
+
+ if (domNode) {
+ d = this.getDayFromDataDateAttribute(domNode);
+ }
+
+ this.comboboxNode.value =
+ d.getMonth() + 1 + '/' + d.getDate() + '/' + d.getFullYear();
+};
+
+ComboboxDatePicker.prototype.getDateFromCombobox = function () {
+ var parts = this.comboboxNode.value.split('/');
+
+ if (
+ parts.length === 3 &&
+ Number.isInteger(parseInt(parts[0])) &&
+ Number.isInteger(parseInt(parts[1])) &&
+ Number.isInteger(parseInt(parts[2]))
+ ) {
+ this.focusDay = new Date(
+ parseInt(parts[2]),
+ parseInt(parts[0]) - 1,
+ parseInt(parts[1])
+ );
+ this.selectedDay = new Date(this.focusDay);
+ } else {
+ // If not a valid date (MM/DD/YY) initialize with todays date
+ this.focusDay = new Date();
+ this.selectedDay = new Date(0, 0, 1);
+ }
+};
+
+ComboboxDatePicker.prototype.setMessage = function (str) {
+ function setMessageDelayed() {
+ this.messageNode.textContent = str;
+ }
+
+ if (str !== this.lastMessage) {
+ setTimeout(setMessageDelayed.bind(this), 200);
+ this.lastMessage = str;
+ }
+};
+
+ComboboxDatePicker.prototype.handleComboboxKeyDown = function (event) {
+ var flag = false,
+ char = event.key,
+ altKey = event.altKey;
+
+ if (event.ctrlKey || event.shiftKey) {
+ return;
+ }
+
+ switch (event.key) {
+ case 'Down':
+ case 'ArrowDown':
+ this.open();
+ this.setFocusDay();
+ flag = true;
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ if (this.isOpen()) {
+ this.close(false);
+ } else {
+ this.comboboxNode.value = '';
+ }
+ this.option = null;
+ flag = true;
+ break;
+
+ case 'Tab':
+ this.close(false);
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handleComboboxClick = function (event) {
+ if (this.isOpen()) {
+ this.close(false);
+ } else {
+ this.open();
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+};
+
+ComboboxDatePicker.prototype.handleComboboxFocus = function (event) {
+ event.currentTarget.parentNode.classList.add('focus');
+};
+
+ComboboxDatePicker.prototype.handleComboboxBlur = function (event) {
+ event.currentTarget.parentNode.classList.remove('focus');
+};
+
+ComboboxDatePicker.prototype.handleButtonKeyDown = function (event) {
+ if (event.key === 'Enter' || event.key === ' ') {
+ this.open();
+ this.setFocusDay();
+
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+ComboboxDatePicker.prototype.handleButtonClick = function (event) {
+ if (this.isOpen()) {
+ this.close();
+ } else {
+ this.open();
+ this.setFocusDay();
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+};
+
+ComboboxDatePicker.prototype.handleBackgroundMouseUp = function (event) {
+ if (
+ !this.comboboxNode.contains(event.target) &&
+ !this.buttonNode.contains(event.target) &&
+ !this.dialogNode.contains(event.target)
+ ) {
+ if (this.isOpen()) {
+ this.close(false);
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ }
+};
+
+// Initialize menu button date picker
+
+window.addEventListener('load', function () {
+ var comboboxDatePickers = document.querySelectorAll('.combobox-datepicker');
+
+ comboboxDatePickers.forEach(function (dp) {
+ var datePicker = new ComboboxDatePicker(dp);
+ datePicker.init();
+ });
+});
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/grid-combo-example.js b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/grid-combo-example.js
new file mode 100644
index 000000000..8bc03ac77
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/grid-combo-example.js
@@ -0,0 +1,99 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * ARIA Combobox Examples
+ */
+
+'use strict';
+
+var FRUITS_AND_VEGGIES = [
+ ['Apple', 'Fruit'],
+ ['Artichoke', 'Vegetable'],
+ ['Asparagus', 'Vegetable'],
+ ['Banana', 'Fruit'],
+ ['Beets', 'Vegetable'],
+ ['Bell pepper', 'Vegetable'],
+ ['Broccoli', 'Vegetable'],
+ ['Brussels sprout', 'Vegetable'],
+ ['Cabbage', 'Vegetable'],
+ ['Carrot', 'Vegetable'],
+ ['Cauliflower', 'Vegetable'],
+ ['Celery', 'Vegetable'],
+ ['Chard', 'Vegetable'],
+ ['Chicory', 'Vegetable'],
+ ['Corn', 'Vegetable'],
+ ['Cucumber', 'Vegetable'],
+ ['Daikon', 'Vegetable'],
+ ['Date', 'Fruit'],
+ ['Edamame', 'Vegetable'],
+ ['Eggplant', 'Vegetable'],
+ ['Elderberry', 'Fruit'],
+ ['Fennel', 'Vegetable'],
+ ['Fig', 'Fruit'],
+ ['Garlic', 'Vegetable'],
+ ['Grape', 'Fruit'],
+ ['Honeydew melon', 'Fruit'],
+ ['Iceberg lettuce', 'Vegetable'],
+ ['Jerusalem artichoke', 'Vegetable'],
+ ['Kale', 'Vegetable'],
+ ['Kiwi', 'Fruit'],
+ ['Leek', 'Vegetable'],
+ ['Lemon', 'Fruit'],
+ ['Mango', 'Fruit'],
+ ['Mangosteen', 'Fruit'],
+ ['Melon', 'Fruit'],
+ ['Mushroom', 'Fungus'],
+ ['Nectarine', 'Fruit'],
+ ['Okra', 'Vegetable'],
+ ['Olive', 'Vegetable'],
+ ['Onion', 'Vegetable'],
+ ['Orange', 'Fruit'],
+ ['Parsnip', 'Vegetable'],
+ ['Pea', 'Vegetable'],
+ ['Pear', 'Fruit'],
+ ['Pineapple', 'Fruit'],
+ ['Potato', 'Vegetable'],
+ ['Pumpkin', 'Fruit'],
+ ['Quince', 'Fruit'],
+ ['Radish', 'Vegetable'],
+ ['Rhubarb', 'Vegetable'],
+ ['Shallot', 'Vegetable'],
+ ['Spinach', 'Vegetable'],
+ ['Squash', 'Vegetable'],
+ ['Strawberry', 'Fruit'],
+ ['Sweet potato', 'Vegetable'],
+ ['Tomato', 'Fruit'],
+ ['Turnip', 'Vegetable'],
+ ['Ugli fruit', 'Fruit'],
+ ['Victoria plum', 'Fruit'],
+ ['Watercress', 'Vegetable'],
+ ['Watermelon', 'Fruit'],
+ ['Yam', 'Vegetable'],
+ ['Zucchini', 'Vegetable'],
+];
+
+function searchVeggies(searchString) {
+ var results = [];
+
+ for (var i = 0; i < FRUITS_AND_VEGGIES.length; i++) {
+ var veggie = FRUITS_AND_VEGGIES[i][0].toLowerCase();
+ if (veggie.indexOf(searchString.toLowerCase()) === 0) {
+ results.push(FRUITS_AND_VEGGIES[i]);
+ }
+ }
+
+ return results;
+}
+
+/**
+ * @function onload
+ * @desc Initialize the combobox examples once the page has loaded
+ */
+window.addEventListener('load', function () {
+ var ex1Combobox = new aria.GridCombobox(
+ document.getElementById('ex1-input'),
+ document.getElementById('ex1-grid'),
+ searchVeggies
+ );
+});
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/grid-combo.js b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/grid-combo.js
new file mode 100644
index 000000000..78bc5c107
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/grid-combo.js
@@ -0,0 +1,297 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ */
+
+'use strict';
+
+/**
+ * @constructor
+ *
+ * @desc
+ * Combobox object representing the state and interactions for a combobox
+ * widget
+ *
+ * @param comboboxNode
+ * The DOM node pointing to the combobox
+ * @param input
+ * The input node
+ * @param grid
+ * The grid node to load results in
+ * @param searchFn
+ * The search function. The function accepts a search string and returns an
+ * array of results.
+ */
+aria.GridCombobox = function (input, grid, searchFn) {
+ this.input = input;
+ this.grid = grid;
+ this.searchFn = searchFn;
+ this.activeRowIndex = -1;
+ this.activeColIndex = 0;
+ this.rowsCount = 0;
+ this.colsCount = 0;
+ this.gridFocused = false;
+ this.shown = false;
+ this.selectionCol = 0;
+
+ this.setupEvents();
+};
+
+aria.GridCombobox.prototype.setupEvents = function () {
+ document.body.addEventListener('click', this.handleBodyClick.bind(this));
+ this.input.addEventListener('keyup', this.handleInputKeyUp.bind(this));
+ this.input.addEventListener('keydown', this.handleInputKeyDown.bind(this));
+ this.input.addEventListener('focus', this.handleInputFocus.bind(this));
+ this.grid.addEventListener('click', this.handleGridClick.bind(this));
+};
+
+aria.GridCombobox.prototype.handleBodyClick = function (evt) {
+ if (evt.target === this.input || this.grid.contains(evt.target)) {
+ return;
+ }
+ this.hideResults();
+};
+
+aria.GridCombobox.prototype.handleInputKeyUp = function (evt) {
+ var key = evt.which || evt.keyCode;
+
+ switch (key) {
+ case aria.KeyCode.UP:
+ case aria.KeyCode.DOWN:
+ case aria.KeyCode.ESC:
+ case aria.KeyCode.RETURN:
+ evt.preventDefault();
+ return;
+ case aria.KeyCode.LEFT:
+ case aria.KeyCode.RIGHT:
+ if (this.gridFocused) {
+ evt.preventDefault();
+ return;
+ }
+ break;
+ default:
+ this.updateResults();
+ }
+};
+
+aria.GridCombobox.prototype.handleInputKeyDown = function (evt) {
+ var key = evt.which || evt.keyCode;
+ var activeRowIndex = this.activeRowIndex;
+ var activeColIndex = this.activeColIndex;
+
+ if (key === aria.KeyCode.ESC) {
+ if (this.gridFocused) {
+ this.gridFocused = false;
+ this.removeFocusCell(this.activeRowIndex, this.activeColIndex);
+ this.activeRowIndex = -1;
+ this.activeColIndex = 0;
+ this.input.setAttribute('aria-activedescendant', '');
+ } else {
+ if (!this.shown) {
+ setTimeout(
+ function () {
+ // On Firefox, input does not get cleared here unless wrapped in
+ // a setTimeout
+ this.input.value = '';
+ }.bind(this),
+ 1
+ );
+ }
+ }
+ if (this.shown) {
+ this.hideResults();
+ }
+ return;
+ }
+
+ if (this.rowsCount < 1) {
+ return;
+ }
+
+ var prevActive = this.getItemAt(activeRowIndex, this.selectionCol);
+ var activeItem;
+
+ switch (key) {
+ case aria.KeyCode.UP:
+ this.gridFocused = true;
+ activeRowIndex = this.getRowIndex(key);
+ evt.preventDefault();
+ break;
+ case aria.KeyCode.DOWN:
+ this.gridFocused = true;
+ activeRowIndex = this.getRowIndex(key);
+ evt.preventDefault();
+ break;
+ case aria.KeyCode.LEFT:
+ if (activeColIndex <= 0) {
+ activeColIndex = this.colsCount - 1;
+ activeRowIndex = this.getRowIndex(key);
+ } else {
+ activeColIndex--;
+ }
+ if (this.gridFocused) {
+ evt.preventDefault();
+ }
+ break;
+ case aria.KeyCode.RIGHT:
+ if (activeColIndex === -1 || activeColIndex >= this.colsCount - 1) {
+ activeColIndex = 0;
+ activeRowIndex = this.getRowIndex(key);
+ } else {
+ activeColIndex++;
+ }
+ if (this.gridFocused) {
+ evt.preventDefault();
+ }
+ break;
+ case aria.KeyCode.RETURN:
+ activeItem = this.getItemAt(activeRowIndex, this.selectionCol);
+ this.selectItem(activeItem);
+ this.gridFocused = false;
+ return;
+ case aria.KeyCode.TAB:
+ this.hideResults();
+ return;
+ default:
+ return;
+ }
+
+ if (prevActive) {
+ this.removeFocusCell(this.activeRowIndex, this.activeColIndex);
+ prevActive.setAttribute('aria-selected', 'false');
+ }
+
+ activeItem = this.getItemAt(activeRowIndex, activeColIndex);
+ this.activeRowIndex = activeRowIndex;
+ this.activeColIndex = activeColIndex;
+
+ if (activeItem) {
+ this.input.setAttribute(
+ 'aria-activedescendant',
+ 'result-item-' + activeRowIndex + 'x' + activeColIndex
+ );
+ this.focusCell(activeRowIndex, activeColIndex);
+ var selectedItem = this.getItemAt(activeRowIndex, this.selectionCol);
+ selectedItem.setAttribute('aria-selected', 'true');
+ } else {
+ this.input.setAttribute('aria-activedescendant', '');
+ }
+};
+
+aria.GridCombobox.prototype.handleInputFocus = function (evt) {
+ this.updateResults();
+};
+
+aria.GridCombobox.prototype.handleGridClick = function (evt) {
+ if (!evt.target) {
+ return;
+ }
+
+ var row;
+ if (evt.target.getAttribute('role') === 'row') {
+ row = evt.target;
+ } else if (evt.target.getAttribute('role') === 'gridcell') {
+ row = evt.target.parentNode;
+ } else {
+ return;
+ }
+
+ var selectItem = row.querySelector('.result-cell');
+ this.selectItem(selectItem);
+};
+
+aria.GridCombobox.prototype.updateResults = function () {
+ var searchString = this.input.value;
+ var results = this.searchFn(searchString);
+
+ this.hideResults();
+
+ if (!searchString) {
+ results = [];
+ }
+
+ if (results.length) {
+ for (var row = 0; row < results.length; row++) {
+ var resultRow = document.createElement('div');
+ resultRow.className = 'result-row';
+ resultRow.setAttribute('role', 'row');
+ resultRow.setAttribute('id', 'result-row-' + row);
+ for (var col = 0; col < results[row].length; col++) {
+ var resultCell = document.createElement('div');
+ resultCell.className = 'result-cell';
+ resultCell.setAttribute('role', 'gridcell');
+ resultCell.setAttribute('id', 'result-item-' + row + 'x' + col);
+ resultCell.innerText = results[row][col];
+ resultRow.appendChild(resultCell);
+ }
+ this.grid.appendChild(resultRow);
+ }
+ aria.Utils.removeClass(this.grid, 'hidden');
+ this.input.setAttribute('aria-expanded', 'true');
+ this.rowsCount = results.length;
+ this.colsCount = results.length ? results[0].length : 0;
+ this.shown = true;
+ }
+};
+
+aria.GridCombobox.prototype.getRowIndex = function (key) {
+ var activeRowIndex = this.activeRowIndex;
+
+ switch (key) {
+ case aria.KeyCode.UP:
+ case aria.KeyCode.LEFT:
+ if (activeRowIndex <= 0) {
+ activeRowIndex = this.rowsCount - 1;
+ } else {
+ activeRowIndex--;
+ }
+ break;
+ case aria.KeyCode.DOWN:
+ case aria.KeyCode.RIGHT:
+ if (activeRowIndex === -1 || activeRowIndex >= this.rowsCount - 1) {
+ activeRowIndex = 0;
+ } else {
+ activeRowIndex++;
+ }
+ }
+
+ return activeRowIndex;
+};
+
+aria.GridCombobox.prototype.getItemAt = function (rowIndex, colIndex) {
+ return document.getElementById('result-item-' + rowIndex + 'x' + colIndex);
+};
+
+aria.GridCombobox.prototype.selectItem = function (item) {
+ if (item) {
+ this.input.value = item.innerText;
+ this.hideResults();
+ }
+};
+
+aria.GridCombobox.prototype.hideResults = function () {
+ this.gridFocused = false;
+ this.shown = false;
+ this.activeRowIndex = -1;
+ this.activeColIndex = 0;
+ this.grid.innerHTML = '';
+ aria.Utils.addClass(this.grid, 'hidden');
+ this.input.setAttribute('aria-expanded', 'false');
+ this.rowsCount = 0;
+ this.colsCount = 0;
+ this.input.setAttribute('aria-activedescendant', '');
+};
+
+aria.GridCombobox.prototype.removeFocusCell = function (rowIndex, colIndex) {
+ var row = document.getElementById('result-row-' + rowIndex);
+ aria.Utils.removeClass(row, 'focused');
+ var cell = this.getItemAt(rowIndex, colIndex);
+ aria.Utils.removeClass(cell, 'focused-cell');
+};
+
+aria.GridCombobox.prototype.focusCell = function (rowIndex, colIndex) {
+ var row = document.getElementById('result-row-' + rowIndex);
+ aria.Utils.addClass(row, 'focused');
+ var cell = this.getItemAt(rowIndex, colIndex);
+ aria.Utils.addClass(cell, 'focused-cell');
+};
diff --git a/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/select-only.js b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/select-only.js
new file mode 100644
index 000000000..f8441cfc5
--- /dev/null
+++ b/build/tests/combobox-autocomplete-both/reference/2020-11-23_175618/js/select-only.js
@@ -0,0 +1,393 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ */
+
+'use strict';
+
+// Save a list of named combobox actions, for future readability
+const SelectActions = {
+ Close: 0,
+ CloseSelect: 1,
+ First: 2,
+ Last: 3,
+ Next: 4,
+ Open: 5,
+ PageDown: 6,
+ PageUp: 7,
+ Previous: 8,
+ Select: 9,
+ Type: 10,
+};
+
+/*
+ * Helper functions
+ */
+
+// filter an array of options against an input string
+// returns an array of options that begin with the filter string, case-independent
+function filterOptions(options = [], filter, exclude = []) {
+ return options.filter((option) => {
+ const matches = option.toLowerCase().indexOf(filter.toLowerCase()) === 0;
+ return matches && exclude.indexOf(option) < 0;
+ });
+}
+
+// map a key press to an action
+function getActionFromKey(event, menuOpen) {
+ const { key, altKey, ctrlKey, metaKey } = event;
+ const openKeys = ['ArrowDown', 'ArrowUp', 'Enter', ' ']; // all keys that will do the default open action
+ // handle opening when closed
+ if (!menuOpen && openKeys.includes(key)) {
+ return SelectActions.Open;
+ }
+
+ // home and end move the selected option when open or closed
+ if (key === 'Home') {
+ return SelectActions.First;
+ }
+ if (key === 'End') {
+ return SelectActions.Last;
+ }
+
+ // handle typing characters when open or closed
+ if (
+ key === 'Backspace' ||
+ key === 'Clear' ||
+ (key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey)
+ ) {
+ return SelectActions.Type;
+ }
+
+ // handle keys when open
+ if (menuOpen) {
+ if (key === 'ArrowUp' && altKey) {
+ return SelectActions.CloseSelect;
+ } else if (key === 'ArrowDown' && !altKey) {
+ return SelectActions.Next;
+ } else if (key === 'ArrowUp') {
+ return SelectActions.Previous;
+ } else if (key === 'PageUp') {
+ return SelectActions.PageUp;
+ } else if (key === 'PageDown') {
+ return SelectActions.PageDown;
+ } else if (key === 'Escape') {
+ return SelectActions.Close;
+ } else if (key === 'Enter' || key === ' ') {
+ return SelectActions.CloseSelect;
+ }
+ }
+}
+
+// return the index of an option from an array of options, based on a search string
+// if the filter is multiple iterations of the same letter (e.g "aaa"), then cycle through first-letter matches
+function getIndexByLetter(options, filter, startIndex = 0) {
+ const orderedOptions = [
+ ...options.slice(startIndex),
+ ...options.slice(0, startIndex),
+ ];
+ const firstMatch = filterOptions(orderedOptions, filter)[0];
+ const allSameLetter = (array) => array.every((letter) => letter === array[0]);
+
+ // first check if there is an exact match for the typed string
+ if (firstMatch) {
+ return options.indexOf(firstMatch);
+ }
+
+ // if the same letter is being repeated, cycle through first-letter matches
+ else if (allSameLetter(filter.split(''))) {
+ const matches = filterOptions(orderedOptions, filter[0]);
+ return options.indexOf(matches[0]);
+ }
+
+ // if no matches, return -1
+ else {
+ return -1;
+ }
+}
+
+// get an updated option index after performing an action
+function getUpdatedIndex(currentIndex, maxIndex, action) {
+ const pageSize = 10; // used for pageup/pagedown
+
+ switch (action) {
+ case SelectActions.First:
+ return 0;
+ case SelectActions.Last:
+ return maxIndex;
+ case SelectActions.Previous:
+ return Math.max(0, currentIndex - 1);
+ case SelectActions.Next:
+ return Math.min(maxIndex, currentIndex + 1);
+ case SelectActions.PageUp:
+ return Math.max(0, currentIndex - pageSize);
+ case SelectActions.PageDown:
+ return Math.min(maxIndex, currentIndex + pageSize);
+ default:
+ return currentIndex;
+ }
+}
+
+// check if an element is currently scrollable
+function isScrollable(element) {
+ return element && element.clientHeight < element.scrollHeight;
+}
+
+// ensure a given child element is within the parent's visible scroll area
+// if the child is not visible, scroll the parent
+function maintainScrollVisibility(activeElement, scrollParent) {
+ const { offsetHeight, offsetTop } = activeElement;
+ const { offsetHeight: parentOffsetHeight, scrollTop } = scrollParent;
+
+ const isAbove = offsetTop < scrollTop;
+ const isBelow = offsetTop + offsetHeight > scrollTop + parentOffsetHeight;
+
+ if (isAbove) {
+ scrollParent.scrollTo(0, offsetTop);
+ } else if (isBelow) {
+ scrollParent.scrollTo(0, offsetTop - parentOffsetHeight + offsetHeight);
+ }
+}
+
+/*
+ * Select Component
+ * Accepts a combobox element and an array of string options
+ */
+const Select = function (el, options = []) {
+ // element refs
+ this.el = el;
+ this.comboEl = el.querySelector('[role=combobox]');
+ this.listboxEl = el.querySelector('[role=listbox]');
+
+ // data
+ this.idBase = this.comboEl.id || 'combo';
+ this.options = options;
+
+ // state
+ this.activeIndex = 0;
+ this.open = false;
+ this.searchString = '';
+ this.searchTimeout = null;
+
+ // init
+ if (el && this.comboEl && this.listboxEl) {
+ this.init();
+ }
+};
+
+Select.prototype.init = function () {
+ // select first option by default
+ this.comboEl.innerHTML = this.options[0];
+
+ // add event listeners
+ this.comboEl.addEventListener('blur', this.onComboBlur.bind(this));
+ this.comboEl.addEventListener('click', this.onComboClick.bind(this));
+ this.comboEl.addEventListener('keydown', this.onComboKeyDown.bind(this));
+
+ // create options
+ this.options.map((option, index) => {
+ const optionEl = this.createOption(option, index);
+ this.listboxEl.appendChild(optionEl);
+ });
+};
+
+Select.prototype.createOption = function (optionText, index) {
+ const optionEl = document.createElement('div');
+ optionEl.setAttribute('role', 'option');
+ optionEl.id = `${this.idBase}-${index}`;
+ optionEl.className =
+ index === 0 ? 'combo-option option-current' : 'combo-option';
+ optionEl.setAttribute('aria-selected', `${index === 0}`);
+ optionEl.innerText = optionText;
+
+ optionEl.addEventListener('click', (event) => {
+ event.stopPropagation();
+ this.onOptionClick(index);
+ });
+ optionEl.addEventListener('mousedown', this.onOptionMouseDown.bind(this));
+
+ return optionEl;
+};
+
+Select.prototype.getSearchString = function (char) {
+ // reset typing timeout and start new timeout
+ // this allows us to make multiple-letter matches, like a native select
+ if (typeof this.searchTimeout === 'number') {
+ window.clearTimeout(this.searchTimeout);
+ }
+
+ this.searchTimeout = window.setTimeout(() => {
+ this.searchString = '';
+ }, 500);
+
+ // add most recent letter to saved search string
+ this.searchString += char;
+ return this.searchString;
+};
+
+Select.prototype.onComboBlur = function () {
+ // do not do blur action if ignoreBlur flag has been set
+ if (this.ignoreBlur) {
+ this.ignoreBlur = false;
+ return;
+ }
+
+ // select current option and close
+ if (this.open) {
+ this.selectOption(this.activeIndex);
+ this.updateMenuState(false, false);
+ }
+};
+
+Select.prototype.onComboClick = function () {
+ this.updateMenuState(!this.open, false);
+};
+
+Select.prototype.onComboKeyDown = function (event) {
+ const { key } = event;
+ const max = this.options.length - 1;
+
+ const action = getActionFromKey(event, this.open);
+
+ switch (action) {
+ case SelectActions.Last:
+ case SelectActions.First:
+ this.updateMenuState(true);
+ // intentional fallthrough
+ case SelectActions.Next:
+ case SelectActions.Previous:
+ case SelectActions.PageUp:
+ case SelectActions.PageDown:
+ event.preventDefault();
+ return this.onOptionChange(
+ getUpdatedIndex(this.activeIndex, max, action)
+ );
+ case SelectActions.CloseSelect:
+ event.preventDefault();
+ this.selectOption(this.activeIndex);
+ // intentional fallthrough
+ case SelectActions.Close:
+ event.preventDefault();
+ return this.updateMenuState(false);
+ case SelectActions.Type:
+ return this.onComboType(key);
+ case SelectActions.Open:
+ event.preventDefault();
+ return this.updateMenuState(true);
+ }
+};
+
+Select.prototype.onComboType = function (letter) {
+ // open the listbox if it is closed
+ this.updateMenuState(true);
+
+ // find the index of the first matching option
+ const searchString = this.getSearchString(letter);
+ const searchIndex = getIndexByLetter(
+ this.options,
+ searchString,
+ this.activeIndex + 1
+ );
+
+ // if a match was found, go to it
+ if (searchIndex >= 0) {
+ this.onOptionChange(searchIndex);
+ }
+ // if no matches, clear the timeout and search string
+ else {
+ window.clearTimeout(this.searchTimeout);
+ this.searchString = '';
+ }
+};
+
+Select.prototype.onOptionChange = function (index) {
+ // update state
+ this.activeIndex = index;
+
+ // update aria-activedescendant
+ this.comboEl.setAttribute('aria-activedescendant', `${this.idBase}-${index}`);
+
+ // update active option styles
+ const options = this.el.querySelectorAll('[role=option]');
+ [...options].forEach((optionEl) => {
+ optionEl.classList.remove('option-current');
+ });
+ options[index].classList.add('option-current');
+
+ // ensure the new option is in view
+ if (isScrollable(this.listboxEl)) {
+ maintainScrollVisibility(options[index], this.listboxEl);
+ }
+};
+
+Select.prototype.onOptionClick = function (index) {
+ this.onOptionChange(index);
+ this.selectOption(index);
+ this.updateMenuState(false);
+};
+
+Select.prototype.onOptionMouseDown = function () {
+ // Clicking an option will cause a blur event,
+ // but we don't want to perform the default keyboard blur action
+ this.ignoreBlur = true;
+};
+
+Select.prototype.selectOption = function (index) {
+ // update state
+ this.activeIndex = index;
+
+ // update displayed value
+ const selected = this.options[index];
+ this.comboEl.innerHTML = selected;
+
+ // update aria-selected
+ const options = this.el.querySelectorAll('[role=option]');
+ [...options].forEach((optionEl) => {
+ optionEl.setAttribute('aria-selected', 'false');
+ });
+ options[index].setAttribute('aria-selected', 'true');
+};
+
+Select.prototype.updateMenuState = function (open, callFocus = true) {
+ if (this.open === open) {
+ return;
+ }
+
+ // update state
+ this.open = open;
+
+ // update aria-expanded and styles
+ this.comboEl.setAttribute('aria-expanded', `${open}`);
+ open ? this.el.classList.add('open') : this.el.classList.remove('open');
+
+ // update activedescendant
+ const activeID = open ? `${this.idBase}-${this.activeIndex}` : '';
+ this.comboEl.setAttribute('aria-activedescendant', activeID);
+
+ // move focus back to the combobox, if needed
+ callFocus && this.comboEl.focus();
+};
+
+// init select
+window.addEventListener('load', function () {
+ const options = [
+ 'Choose a Fruit',
+ 'Apple',
+ 'Banana',
+ 'Blueberry',
+ 'Boysenberry',
+ 'Cherry',
+ 'Cranberry',
+ 'Durian',
+ 'Eggplant',
+ 'Fig',
+ 'Grape',
+ 'Guava',
+ 'Huckleberry',
+ ];
+ const selectEls = document.querySelectorAll('.js-select');
+
+ selectEls.forEach((el) => {
+ new Select(el, options);
+ });
+});
diff --git a/tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-01-navigate-to-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-02-navigate-by-line-to-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-03-navigate-to-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-04-read-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-04-read-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-04-read-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-04-read-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-04-read-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-04-read-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-04-read-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-04-read-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-05-read-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-05-read-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-05-read-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-05-read-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-05-read-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-05-read-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-05-read-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-05-read-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-06-navigate-to-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-07-navigate-to-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-08-read-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-08-read-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-08-read-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-08-read-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-08-read-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-08-read-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-08-read-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-08-read-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-09-read-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-09-read-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-09-read-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-09-read-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-09-read-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-09-read-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-09-read-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-09-read-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-10-navigate-to-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-11-navigate-by-line-to-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-12-navigate-to-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-13-read-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-13-read-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-13-read-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-13-read-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-13-read-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-13-read-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-13-read-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-13-read-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-14-read-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-14-read-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-14-read-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-14-read-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-14-read-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-14-read-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-14-read-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-14-read-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.html b/build/tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.html
rename to build/tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.html
diff --git a/tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.json b/build/tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.json
rename to build/tests/combobox-autocomplete-both/test-15-navigate-to-combobox-with-keys-that-switch-modes-reading.json
diff --git a/tests/combobox-autocomplete-both/test-16-activate-combobox-reading.html b/build/tests/combobox-autocomplete-both/test-16-activate-combobox-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-16-activate-combobox-reading.html
rename to build/tests/combobox-autocomplete-both/test-16-activate-combobox-reading.html
diff --git a/tests/combobox-autocomplete-both/test-16-activate-combobox-reading.json b/build/tests/combobox-autocomplete-both/test-16-activate-combobox-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-16-activate-combobox-reading.json
rename to build/tests/combobox-autocomplete-both/test-16-activate-combobox-reading.json
diff --git a/tests/combobox-autocomplete-both/test-17-open-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-17-open-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-17-open-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-17-open-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-17-open-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-17-open-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-17-open-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-17-open-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-18-close-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-18-close-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-18-close-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-18-close-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-18-close-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-18-close-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-18-close-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-18-close-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-19-activate-states-button-reading.html b/build/tests/combobox-autocomplete-both/test-19-activate-states-button-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-19-activate-states-button-reading.html
rename to build/tests/combobox-autocomplete-both/test-19-activate-states-button-reading.html
diff --git a/tests/combobox-autocomplete-both/test-19-activate-states-button-reading.json b/build/tests/combobox-autocomplete-both/test-19-activate-states-button-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-19-activate-states-button-reading.json
rename to build/tests/combobox-autocomplete-both/test-19-activate-states-button-reading.json
diff --git a/tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.html b/build/tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.html
rename to build/tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.json b/build/tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.json
rename to build/tests/combobox-autocomplete-both/test-20-activate-states-button-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-21-activate-states-button-reading.html b/build/tests/combobox-autocomplete-both/test-21-activate-states-button-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-21-activate-states-button-reading.html
rename to build/tests/combobox-autocomplete-both/test-21-activate-states-button-reading.html
diff --git a/tests/combobox-autocomplete-both/test-21-activate-states-button-reading.json b/build/tests/combobox-autocomplete-both/test-21-activate-states-button-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-21-activate-states-button-reading.json
rename to build/tests/combobox-autocomplete-both/test-21-activate-states-button-reading.json
diff --git a/tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.html b/build/tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.html
rename to build/tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.json b/build/tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.json
rename to build/tests/combobox-autocomplete-both/test-22-activate-states-button-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-23-navigate-into-popup-from-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.html b/build/tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.json b/build/tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-24-type-in-empty-combobox-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.html b/build/tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.html
rename to build/tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.json b/build/tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.json
rename to build/tests/combobox-autocomplete-both/test-25-navigate-listbox-popup-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.html b/build/tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.html
rename to build/tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.html
diff --git a/tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.json b/build/tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.json
rename to build/tests/combobox-autocomplete-both/test-26-read-option-in-listbox-popup-reading.json
diff --git a/tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.html b/build/tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.json b/build/tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-autocomplete-both/test-27-read-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.html b/build/tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.html
rename to build/tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.json b/build/tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.json
rename to build/tests/combobox-autocomplete-both/test-28-choose-combobox-option-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.html b/build/tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.html
rename to build/tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.json b/build/tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.json
rename to build/tests/combobox-autocomplete-both/test-29-cancel-selection-interaction.json
diff --git a/tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.html b/build/tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.html
similarity index 100%
rename from tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.html
rename to build/tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.html
diff --git a/tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.json b/build/tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.json
similarity index 100%
rename from tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.json
rename to build/tests/combobox-autocomplete-both/test-30-initiate-caret-movement-from-listbox-interaction.json
diff --git a/tests/combobox-select-only/commands.json b/build/tests/combobox-select-only/commands.json
similarity index 100%
rename from tests/combobox-select-only/commands.json
rename to build/tests/combobox-select-only/commands.json
diff --git a/tests/combobox-select-only/index.html b/build/tests/combobox-select-only/index.html
similarity index 100%
rename from tests/combobox-select-only/index.html
rename to build/tests/combobox-select-only/index.html
diff --git a/build/tests/combobox-select-only/reference/combobox-select-only.html b/build/tests/combobox-select-only/reference/combobox-select-only.html
new file mode 100644
index 000000000..c67aa4049
--- /dev/null
+++ b/build/tests/combobox-select-only/reference/combobox-select-only.html
@@ -0,0 +1,51 @@
+
+
+
+
+Select-Only Combobox Example
+
+
+
+
+
+
+
+
Select-Only Combobox Example
+
+ The following example implementation of the ARIA design pattern for combobox
+ demonstrates a single-select combobox widget that is functionally similar to an HTML select element.
+ Unlike the editable combobox examples, this select-only combobox is not made with an <input> element, and it does not accept freeform user input.
+ However, like an HTML <select>, users can type characters to select matching options.
+
+ Navigate backwards from here
+
+
+
+
+
diff --git a/build/tests/combobox-select-only/reference/css/select-only.css b/build/tests/combobox-select-only/reference/css/select-only.css
new file mode 100644
index 000000000..1d8beb3a7
--- /dev/null
+++ b/build/tests/combobox-select-only/reference/css/select-only.css
@@ -0,0 +1,103 @@
+.combo *,
+.combo *::before,
+.combo *::after {
+ box-sizing: border-box;
+}
+
+.combo {
+ display: block;
+ margin-bottom: 1.5em;
+ max-width: 400px;
+ position: relative;
+}
+
+.combo::after {
+ border-bottom: 2px solid rgba(0, 0, 0, 0.75);
+ border-right: 2px solid rgba(0, 0, 0, 0.75);
+ content: "";
+ display: block;
+ height: 12px;
+ pointer-events: none;
+ position: absolute;
+ right: 16px;
+ top: 50%;
+ transform: translate(0, -65%) rotate(45deg);
+ width: 12px;
+}
+
+.combo-input {
+ background-color: #f5f5f5;
+ border: 2px solid rgba(0, 0, 0, 0.75);
+ border-radius: 4px;
+ display: block;
+ font-size: 1em;
+ min-height: calc(1.4em + 26px);
+ padding: 12px 16px 14px;
+ text-align: left;
+ width: 100%;
+}
+
+.open .combo-input {
+ border-radius: 4px 4px 0 0;
+}
+
+.combo-input:focus {
+ border-color: #0067b8;
+ box-shadow: 0 0 4px 2px #0067b8;
+ outline: 4px solid transparent;
+}
+
+.combo-label {
+ display: block;
+ font-size: 20px;
+ font-weight: 100;
+ margin-bottom: 0.25em;
+}
+
+.combo-menu {
+ background-color: #f5f5f5;
+ border: 1px solid rgba(0, 0, 0, 0.75);
+ border-radius: 0 0 4px 4px;
+ display: none;
+ max-height: 300px;
+ overflow-y: scroll;
+ left: 0;
+ position: absolute;
+ top: 100%;
+ width: 100%;
+ z-index: 100;
+}
+
+.open .combo-menu {
+ display: block;
+}
+
+.combo-option {
+ padding: 10px 12px 12px;
+}
+
+.combo-option:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.combo-option.option-current {
+ outline: 3px solid #0067b8;
+ outline-offset: -3px;
+}
+
+.combo-option[aria-selected="true"] {
+ padding-right: 30px;
+ position: relative;
+}
+
+.combo-option[aria-selected="true"]::after {
+ border-bottom: 2px solid #000;
+ border-right: 2px solid #000;
+ content: "";
+ height: 16px;
+ position: absolute;
+ right: 15px;
+ top: 50%;
+ transform: translate(0, -50%) rotate(45deg);
+ width: 8px;
+}
diff --git a/build/tests/combobox-select-only/reference/js/select-only.js b/build/tests/combobox-select-only/reference/js/select-only.js
new file mode 100644
index 000000000..2398e1b9d
--- /dev/null
+++ b/build/tests/combobox-select-only/reference/js/select-only.js
@@ -0,0 +1,390 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ */
+
+'use strict';
+
+// Save a list of named combobox actions, for future readability
+const SelectActions = {
+ Close: 0,
+ CloseSelect: 1,
+ First: 2,
+ Last: 3,
+ Next: 4,
+ Open: 5,
+ PageDown: 6,
+ PageUp: 7,
+ Previous: 8,
+ Select: 9,
+ Type: 10,
+};
+
+/*
+ * Helper functions
+ */
+
+// filter an array of options against an input string
+// returns an array of options that begin with the filter string, case-independent
+function filterOptions(options = [], filter, exclude = []) {
+ return options.filter((option) => {
+ const matches = option.toLowerCase().indexOf(filter.toLowerCase()) === 0;
+ return matches && exclude.indexOf(option) < 0;
+ });
+}
+
+// map a key press to an action
+function getActionFromKey(event, menuOpen) {
+ const { key, altKey, ctrlKey, metaKey } = event;
+ const openKeys = ['ArrowDown', 'ArrowUp', 'Enter', ' ']; // all keys that will do the default open action
+ // handle opening when closed
+ if (!menuOpen && openKeys.includes(key)) {
+ return SelectActions.Open;
+ }
+
+ // home and end move the selected option when open or closed
+ if (key === 'Home') {
+ return SelectActions.First;
+ }
+ if (key === 'End') {
+ return SelectActions.Last;
+ }
+
+ // handle typing characters when open or closed
+ if (
+ key === 'Backspace' ||
+ key === 'Clear' ||
+ (key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey)
+ ) {
+ return SelectActions.Type;
+ }
+
+ // handle keys when open
+ if (menuOpen) {
+ if (key === 'ArrowUp' && altKey) {
+ return SelectActions.CloseSelect;
+ } else if (key === 'ArrowDown' && !altKey) {
+ return SelectActions.Next;
+ } else if (key === 'ArrowUp') {
+ return SelectActions.Previous;
+ } else if (key === 'PageUp') {
+ return SelectActions.PageUp;
+ } else if (key === 'PageDown') {
+ return SelectActions.PageDown;
+ } else if (key === 'Escape') {
+ return SelectActions.Close;
+ } else if (key === 'Enter' || key === ' ') {
+ return SelectActions.CloseSelect;
+ }
+ }
+}
+
+// return the index of an option from an array of options, based on a search string
+// if the filter is multiple iterations of the same letter (e.g "aaa"), then cycle through first-letter matches
+function getIndexByLetter(options, filter, startIndex = 0) {
+ const orderedOptions = [
+ ...options.slice(startIndex),
+ ...options.slice(0, startIndex),
+ ];
+ const firstMatch = filterOptions(orderedOptions, filter)[0];
+ const allSameLetter = (array) => array.every((letter) => letter === array[0]);
+
+ // first check if there is an exact match for the typed string
+ if (firstMatch) {
+ return options.indexOf(firstMatch);
+ }
+
+ // if the same letter is being repeated, cycle through first-letter matches
+ else if (allSameLetter(filter.split(''))) {
+ const matches = filterOptions(orderedOptions, filter[0]);
+ return options.indexOf(matches[0]);
+ }
+
+ // if no matches, return -1
+ else {
+ return -1;
+ }
+}
+
+// get an updated option index after performing an action
+function getUpdatedIndex(currentIndex, maxIndex, action) {
+ const pageSize = 10; // used for pageup/pagedown
+
+ switch (action) {
+ case SelectActions.First:
+ return 0;
+ case SelectActions.Last:
+ return maxIndex;
+ case SelectActions.Previous:
+ return Math.max(0, currentIndex - 1);
+ case SelectActions.Next:
+ return Math.min(maxIndex, currentIndex + 1);
+ case SelectActions.PageUp:
+ return Math.max(0, currentIndex - pageSize);
+ case SelectActions.PageDown:
+ return Math.min(maxIndex, currentIndex + pageSize);
+ default:
+ return currentIndex;
+ }
+}
+
+// check if an element is currently scrollable
+function isScrollable(element) {
+ return element && element.clientHeight < element.scrollHeight;
+}
+
+// ensure a given child element is within the parent's visible scroll area
+// if the child is not visible, scroll the parent
+function maintainScrollVisibility(activeElement, scrollParent) {
+ const { offsetHeight, offsetTop } = activeElement;
+ const { offsetHeight: parentOffsetHeight, scrollTop } = scrollParent;
+
+ const isAbove = offsetTop < scrollTop;
+ const isBelow = offsetTop + offsetHeight > scrollTop + parentOffsetHeight;
+
+ if (isAbove) {
+ scrollParent.scrollTo(0, offsetTop);
+ } else if (isBelow) {
+ scrollParent.scrollTo(0, offsetTop - parentOffsetHeight + offsetHeight);
+ }
+}
+
+/*
+ * Select Component
+ * Accepts a combobox element and an array of string options
+ */
+const Select = function (el, options = []) {
+ // element refs
+ this.el = el;
+ this.comboEl = el.querySelector('[role=combobox]');
+ this.listboxEl = el.querySelector('[role=listbox]');
+
+ // data
+ this.idBase = this.comboEl.id || 'combo';
+ this.options = options;
+
+ // state
+ this.activeIndex = 0;
+ this.open = false;
+ this.searchString = '';
+ this.searchTimeout = null;
+
+ // init
+ if (el && this.comboEl && this.listboxEl) {
+ this.init();
+ }
+};
+
+Select.prototype.init = function () {
+ // select first option by default
+ this.comboEl.innerHTML = this.options[0];
+
+ // add event listeners
+ this.comboEl.addEventListener('blur', this.onComboBlur.bind(this));
+ this.comboEl.addEventListener('click', this.onComboClick.bind(this));
+ this.comboEl.addEventListener('keydown', this.onComboKeyDown.bind(this));
+
+ // create options
+ this.options.map((option, index) => {
+ const optionEl = this.createOption(option, index);
+ this.listboxEl.appendChild(optionEl);
+ });
+};
+
+Select.prototype.createOption = function (optionText, index) {
+ const optionEl = document.createElement('div');
+ optionEl.setAttribute('role', 'option');
+ optionEl.id = `${this.idBase}-${index}`;
+ optionEl.className =
+ index === 0 ? 'combo-option option-current' : 'combo-option';
+ optionEl.setAttribute('aria-selected', `${index === 0}`);
+ optionEl.innerText = optionText;
+
+ optionEl.addEventListener('click', (event) => {
+ event.stopPropagation();
+ this.onOptionClick(index);
+ });
+ optionEl.addEventListener('mousedown', this.onOptionMouseDown.bind(this));
+
+ return optionEl;
+};
+
+Select.prototype.getSearchString = function (char) {
+ // reset typing timeout and start new timeout
+ // this allows us to make multiple-letter matches, like a native select
+ if (typeof this.searchTimeout === 'number') {
+ window.clearTimeout(this.searchTimeout);
+ }
+
+ this.searchTimeout = window.setTimeout(() => {
+ this.searchString = '';
+ }, 500);
+
+ // add most recent letter to saved search string
+ this.searchString += char;
+ return this.searchString;
+};
+
+Select.prototype.onComboBlur = function () {
+ // do not do blur action if ignoreBlur flag has been set
+ if (this.ignoreBlur) {
+ this.ignoreBlur = false;
+ return;
+ }
+
+ // select current option and close
+ if (this.open) {
+ this.selectOption(this.activeIndex);
+ this.updateMenuState(false, false);
+ }
+};
+
+Select.prototype.onComboClick = function () {
+ this.updateMenuState(!this.open, false);
+};
+
+Select.prototype.onComboKeyDown = function (event) {
+ const { key } = event;
+ const max = this.options.length - 1;
+
+ const action = getActionFromKey(event, this.open);
+
+ switch (action) {
+ case SelectActions.Last:
+ case SelectActions.First:
+ this.updateMenuState(true);
+ // intentional fallthrough
+ case SelectActions.Next:
+ case SelectActions.Previous:
+ case SelectActions.PageUp:
+ case SelectActions.PageDown:
+ event.preventDefault();
+ return this.onOptionChange(
+ getUpdatedIndex(this.activeIndex, max, action)
+ );
+ case SelectActions.CloseSelect:
+ event.preventDefault();
+ this.selectOption(this.activeIndex);
+ // intentional fallthrough
+ case SelectActions.Close:
+ event.preventDefault();
+ return this.updateMenuState(false);
+ case SelectActions.Type:
+ return this.onComboType(key);
+ case SelectActions.Open:
+ event.preventDefault();
+ return this.updateMenuState(true);
+ }
+};
+
+Select.prototype.onComboType = function (letter) {
+ // open the listbox if it is closed
+ this.updateMenuState(true);
+
+ // find the index of the first matching option
+ const searchString = this.getSearchString(letter);
+ const searchIndex = getIndexByLetter(
+ this.options,
+ searchString,
+ this.activeIndex + 1
+ );
+
+ // if a match was found, go to it
+ if (searchIndex >= 0) {
+ this.onOptionChange(searchIndex);
+ }
+ // if no matches, clear the timeout and search string
+ else {
+ window.clearTimeout(this.searchTimeout);
+ this.searchString = '';
+ }
+};
+
+Select.prototype.onOptionChange = function (index) {
+ // update state
+ this.activeIndex = index;
+
+ // update aria-activedescendant
+ this.comboEl.setAttribute('aria-activedescendant', `${this.idBase}-${index}`);
+
+ // update active option styles
+ const options = this.el.querySelectorAll('[role=option]');
+ [...options].forEach((optionEl) => {
+ optionEl.classList.remove('option-current');
+ });
+ options[index].classList.add('option-current');
+
+ // ensure the new option is in view
+ if (isScrollable(this.listboxEl)) {
+ maintainScrollVisibility(options[index], this.listboxEl);
+ }
+};
+
+Select.prototype.onOptionClick = function (index) {
+ this.onOptionChange(index);
+ this.selectOption(index);
+ this.updateMenuState(false);
+};
+
+Select.prototype.onOptionMouseDown = function () {
+ // Clicking an option will cause a blur event,
+ // but we don't want to perform the default keyboard blur action
+ this.ignoreBlur = true;
+};
+
+Select.prototype.selectOption = function (index) {
+ // update state
+ this.activeIndex = index;
+
+ // update displayed value
+ const selected = this.options[index];
+ this.comboEl.innerHTML = selected;
+
+ // update aria-selected
+ const options = this.el.querySelectorAll('[role=option]');
+ [...options].forEach((optionEl) => {
+ optionEl.setAttribute('aria-selected', 'false');
+ });
+ options[index].setAttribute('aria-selected', 'true');
+};
+
+Select.prototype.updateMenuState = function (open, callFocus = true) {
+ if (this.open === open) {
+ return;
+ }
+
+ // update state
+ this.open = open;
+
+ // update aria-expanded and styles
+ this.comboEl.setAttribute('aria-expanded', `${open}`);
+ open ? this.el.classList.add('open') : this.el.classList.remove('open');
+
+ // update activedescendant
+ const activeID = open ? `${this.idBase}-${this.activeIndex}` : '';
+ this.comboEl.setAttribute('aria-activedescendant', activeID);
+
+ // move focus back to the combobox, if needed
+ callFocus && this.comboEl.focus();
+};
+
+// init select
+window.addEventListener('load', function () {
+ const options = [
+ 'Choose a Fruit',
+ 'Apple',
+ 'Banana',
+ 'Blueberry',
+ 'Boysenberry',
+ 'Cherry',
+ 'Cranberry',
+ 'Durian',
+ 'Eggplant',
+ 'Fig',
+ 'Grape',
+ 'Guava',
+ 'Huckleberry',
+ ];
+ const selectEl = document.querySelector('.js-select');
+ document.defaultView.selectController = new Select(selectEl, options);
+});
diff --git a/tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.html b/build/tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.html
similarity index 100%
rename from tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.html
rename to build/tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.html
diff --git a/tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.json b/build/tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.json
similarity index 100%
rename from tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.json
rename to build/tests/combobox-select-only/test-01-navigate-forwards-to-collapsed-select-only-combobox-reading.json
diff --git a/tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.html b/build/tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.html
similarity index 100%
rename from tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.html
rename to build/tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.html
diff --git a/tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.json b/build/tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.json
similarity index 100%
rename from tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.json
rename to build/tests/combobox-select-only/test-02-navigate-backwards-to-collapsed-select-only-combobox-reading.json
diff --git a/tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-03-navigate-forwards-to-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-04-navigate-backwards-to-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-05-navigate-forwards-to-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-06-navigate-backwards-to-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.html b/build/tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.html
similarity index 100%
rename from tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.html
rename to build/tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.html
diff --git a/tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.json b/build/tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.json
similarity index 100%
rename from tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.json
rename to build/tests/combobox-select-only/test-07-read-information-about-collapsed-select-only-combobox-reading.json
diff --git a/tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-08-read-information-about-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-09-read-information-about-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.html b/build/tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.html
similarity index 100%
rename from tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.html
rename to build/tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.html
diff --git a/tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.json b/build/tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.json
similarity index 100%
rename from tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.json
rename to build/tests/combobox-select-only/test-10-open-collapsed-select-only-combobox-reading.json
diff --git a/tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-11-open-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.html b/build/tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.html
rename to build/tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.html
diff --git a/tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.json b/build/tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.json
rename to build/tests/combobox-select-only/test-12-open-collapsed-select-only-combobox-interaction.json
diff --git a/tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.html b/build/tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.html
rename to build/tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.html
diff --git a/tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.json b/build/tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.json
rename to build/tests/combobox-select-only/test-13-open-collapsed-select-only-combobox-to-first-option-interaction.json
diff --git a/tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.html b/build/tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.html
rename to build/tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.html
diff --git a/tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.json b/build/tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.json
rename to build/tests/combobox-select-only/test-14-open-collapsed-select-only-combobox-to-first-option-interaction.json
diff --git a/tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.html b/build/tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.html
rename to build/tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.html
diff --git a/tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.json b/build/tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.json
rename to build/tests/combobox-select-only/test-15-open-collapsed-select-only-combobox-to-specific-option-interaction.json
diff --git a/tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.html b/build/tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.html
rename to build/tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.html
diff --git a/tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.json b/build/tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.json
rename to build/tests/combobox-select-only/test-16-open-collapsed-select-only-combobox-to-specific-option-interaction.json
diff --git a/tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.html b/build/tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.html
rename to build/tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.html
diff --git a/tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.json b/build/tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.json
rename to build/tests/combobox-select-only/test-17-open-collapsed-select-only-combobox-to-last-option-interaction.json
diff --git a/tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.html b/build/tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.html
rename to build/tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.html
diff --git a/tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.json b/build/tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.json
rename to build/tests/combobox-select-only/test-18-open-collapsed-select-only-combobox-to-last-option-interaction.json
diff --git a/tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-19-read-information-about-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-20-read-information-about-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-21-navigate-forwards-to-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-22-navigate-backwards-to-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-23-navigate-forwards-to-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-24-navigate-backwards-to-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-25-navigate-to-specific-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-26-navigate-to-specific-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-27-navigate-to-first-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-28-navigate-to-last-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-29-navigate-to-first-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-30-navigate-to-last-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-31-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-32-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-33-navigate-forwards-by-ten-options-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-34-navigate-backwards-by-ten-options-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-35-select-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-36-select-option-in-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-37-close-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-37-close-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-37-close-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-37-close-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-37-close-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-37-close-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-37-close-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-37-close-listbox-popup-interaction.json
diff --git a/tests/combobox-select-only/test-38-close-listbox-popup-interaction.html b/build/tests/combobox-select-only/test-38-close-listbox-popup-interaction.html
similarity index 100%
rename from tests/combobox-select-only/test-38-close-listbox-popup-interaction.html
rename to build/tests/combobox-select-only/test-38-close-listbox-popup-interaction.html
diff --git a/tests/combobox-select-only/test-38-close-listbox-popup-interaction.json b/build/tests/combobox-select-only/test-38-close-listbox-popup-interaction.json
similarity index 100%
rename from tests/combobox-select-only/test-38-close-listbox-popup-interaction.json
rename to build/tests/combobox-select-only/test-38-close-listbox-popup-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/commands.json b/build/tests/menu-button-actions-active-descendant/commands.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/commands.json
rename to build/tests/menu-button-actions-active-descendant/commands.json
diff --git a/tests/menu-button-actions-active-descendant/index.html b/build/tests/menu-button-actions-active-descendant/index.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/index.html
rename to build/tests/menu-button-actions-active-descendant/index.html
diff --git a/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/css/menu-button-actions.css b/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/css/menu-button-actions.css
new file mode 100644
index 000000000..391043fa3
--- /dev/null
+++ b/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/css/menu-button-actions.css
@@ -0,0 +1,82 @@
+.menu-button-actions {
+ margin: 0;
+ padding: 0;
+}
+
+.menu-button-actions button {
+ margin: 0;
+ padding: 6px;
+ display: inline-block;
+ position: relative;
+ background-color: #034575;
+ border: 1px solid #034575;
+ font-size: 0.9em;
+ color: white;
+ border-radius: 5px;
+}
+
+.menu-button-actions [role="menu"] {
+ display: none;
+ position: absolute;
+ margin: 0;
+ padding: 7px 4px;
+ border: 2px solid #034575;
+ border-radius: 5px;
+ background-color: #eee;
+}
+
+.menu-button-actions [role="menuitem"],
+.menu-button-actions [role="separator"] {
+ margin: 0;
+ padding: 6px;
+ display: block;
+ width: 4em;
+ background-color: #eee;
+ color: black;
+ border-radius: 5px;
+}
+
+.menu-button-actions [role="separator"] {
+ padding-top: 3px;
+ background-image: url("../images/separator.svg");
+ background-position: center;
+ background-repeat: repeat-x;
+}
+
+.menu-button-actions button svg.down {
+ padding-left: 0.125em;
+ fill: currentColor;
+ stroke: currentColor;
+}
+
+.menu-button-actions button[aria-expanded="true"] svg.down {
+ transform: rotate(180deg);
+}
+
+/* focus styling */
+
+.menu-button-actions button:hover,
+.menu-button-actions button:focus,
+.menu-button-actions button[aria-expanded="true"] {
+ padding: 4px;
+ border: 3px solid #034575;
+ background: #eee;
+ color: #222;
+ outline: none;
+ margin: 0;
+}
+
+.menu-button-actions [role="menuitem"].focus,
+.menu-button-actions [role="menuitem"]:focus {
+ padding: 4px;
+ border: 2px solid #034575;
+ background: #034575;
+ color: #fff;
+ outline: none;
+ margin: 0;
+}
+
+input.action:focus {
+ outline: 2px solid #034575;
+ background: #def;
+}
diff --git a/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/js/menu-button-actions-active-descendant.js b/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/js/menu-button-actions-active-descendant.js
new file mode 100644
index 000000000..685e270ef
--- /dev/null
+++ b/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/js/menu-button-actions-active-descendant.js
@@ -0,0 +1,347 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: menu-button-actives-active-descendant.js
+ *
+ * Desc: Creates a menu button that opens a menu of actions using aria-activedescendants
+ */
+
+'use strict';
+
+class MenuButtonActionsActiveDescendant {
+ constructor(domNode, performMenuAction) {
+ this.domNode = domNode;
+ this.performMenuAction = performMenuAction;
+ this.buttonNode = domNode.querySelector('button');
+ this.menuNode = domNode.querySelector('[role="menu"]');
+ this.currentMenuitem = {};
+ this.menuitemNodes = [];
+ this.firstMenuitem = false;
+ this.lastMenuitem = false;
+ this.firstChars = [];
+
+ this.buttonNode.addEventListener(
+ 'keydown',
+ this.onButtonKeydown.bind(this)
+ );
+
+ this.buttonNode.addEventListener('click', this.onButtonClick.bind(this));
+
+ this.menuNode.addEventListener('keydown', this.onMenuKeydown.bind(this));
+
+ var nodes = domNode.querySelectorAll('[role="menuitem"]');
+
+ for (var i = 0; i < nodes.length; i++) {
+ var menuitem = nodes[i];
+ this.menuitemNodes.push(menuitem);
+ menuitem.tabIndex = -1;
+ this.firstChars.push(menuitem.textContent.trim()[0].toLowerCase());
+
+ menuitem.addEventListener('click', this.onMenuitemClick.bind(this));
+
+ menuitem.addEventListener(
+ 'mouseover',
+ this.onMenuitemMouseover.bind(this)
+ );
+
+ if (!this.firstMenuitem) {
+ this.firstMenuitem = menuitem;
+ }
+ this.lastMenuitem = menuitem;
+ }
+
+ domNode.addEventListener('focusin', this.onFocusin.bind(this));
+ domNode.addEventListener('focusout', this.onFocusout.bind(this));
+
+ window.addEventListener(
+ 'mousedown',
+ this.onBackgroundMousedown.bind(this),
+ true
+ );
+ }
+
+ setFocusToMenuitem(newMenuitem) {
+ for (var i = 0; i < this.menuitemNodes.length; i++) {
+ var menuitem = this.menuitemNodes[i];
+ if (menuitem === newMenuitem) {
+ this.currentMenuitem = newMenuitem;
+ menuitem.classList.add('focus');
+ this.menuNode.setAttribute('aria-activedescendant', newMenuitem.id);
+ } else {
+ menuitem.classList.remove('focus');
+ }
+ }
+ }
+
+ setFocusToFirstMenuitem() {
+ this.setFocusToMenuitem(this.firstMenuitem);
+ }
+
+ setFocusToLastMenuitem() {
+ this.setFocusToMenuitem(this.lastMenuitem);
+ }
+
+ setFocusToPreviousMenuitem() {
+ var newMenuitem, index;
+
+ if (this.currentMenuitem === this.firstMenuitem) {
+ newMenuitem = this.lastMenuitem;
+ } else {
+ index = this.menuitemNodes.indexOf(this.currentMenuitem);
+ newMenuitem = this.menuitemNodes[index - 1];
+ }
+
+ this.setFocusToMenuitem(newMenuitem);
+
+ return newMenuitem;
+ }
+
+ setFocusToNextMenuitem() {
+ var newMenuitem, index;
+
+ if (this.currentMenuitem === this.lastMenuitem) {
+ newMenuitem = this.firstMenuitem;
+ } else {
+ index = this.menuitemNodes.indexOf(this.currentMenuitem);
+ newMenuitem = this.menuitemNodes[index + 1];
+ }
+ this.setFocusToMenuitem(newMenuitem);
+
+ return newMenuitem;
+ }
+
+ setFocusByFirstCharacter(char) {
+ var start, index;
+
+ if (char.length > 1) {
+ return;
+ }
+
+ char = char.toLowerCase();
+
+ // Get start index for search based on position of currentItem
+ start = this.menuitemNodes.indexOf(this.currentMenuitem) + 1;
+ if (start >= this.menuitemNodes.length) {
+ start = 0;
+ }
+
+ // Check remaining slots in the menu
+ index = this.firstChars.indexOf(char, start);
+
+ // If not found in remaining slots, check from beginning
+ if (index === -1) {
+ index = this.firstChars.indexOf(char, 0);
+ }
+
+ // If match was found...
+ if (index > -1) {
+ this.setFocusToMenuitem(this.menuitemNodes[index]);
+ }
+ }
+
+ // Utilities
+
+ getIndexFirstChars(startIndex, char) {
+ for (var i = startIndex; i < this.firstChars.length; i++) {
+ if (char === this.firstChars[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ // Popup menu methods
+
+ openPopup() {
+ this.menuNode.style.display = 'block';
+ this.buttonNode.setAttribute('aria-expanded', 'true');
+ this.menuNode.focus();
+ this.setFocusToFirstMenuitem();
+ }
+
+ closePopup() {
+ if (this.isOpen()) {
+ this.buttonNode.removeAttribute('aria-expanded');
+ this.menuNode.setAttribute('aria-activedescendant', '');
+ for (var i = 0; i < this.menuitemNodes.length; i++) {
+ this.menuitemNodes[i].classList.remove('focus');
+ }
+ this.menuNode.style.display = 'none';
+ this.buttonNode.focus();
+ }
+ }
+
+ isOpen() {
+ return this.buttonNode.getAttribute('aria-expanded') === 'true';
+ }
+
+ // Menu event handlers
+
+ onFocusin() {
+ this.domNode.classList.add('focus');
+ }
+
+ onFocusout() {
+ this.domNode.classList.remove('focus');
+ }
+
+ onButtonKeydown(event) {
+ var key = event.key,
+ flag = false;
+
+ switch (key) {
+ case ' ':
+ case 'Enter':
+ case 'ArrowDown':
+ case 'Down':
+ this.openPopup();
+ this.setFocusToFirstMenuitem();
+ flag = true;
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ this.closePopup();
+ flag = true;
+ break;
+
+ case 'Up':
+ case 'ArrowUp':
+ this.openPopup();
+ this.setFocusToLastMenuitem();
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ }
+
+ onButtonClick(event) {
+ if (this.isOpen()) {
+ this.closePopup();
+ } else {
+ this.openPopup();
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ }
+
+ onMenuKeydown(event) {
+ var key = event.key,
+ flag = false;
+
+ function isPrintableCharacter(str) {
+ return str.length === 1 && str.match(/\S/);
+ }
+
+ if (event.ctrlKey || event.altKey || event.metaKey) {
+ return;
+ }
+
+ if (event.shiftKey) {
+ if (isPrintableCharacter(key)) {
+ this.setFocusByFirstCharacter(key);
+ flag = true;
+ }
+
+ if (event.key === 'Tab') {
+ this.closePopup();
+ flag = true;
+ }
+ } else {
+ switch (key) {
+ case ' ':
+ case 'Enter':
+ this.closePopup();
+ this.performMenuAction(this.currentMenuitem);
+ flag = true;
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ this.closePopup();
+ flag = true;
+ break;
+
+ case 'Up':
+ case 'ArrowUp':
+ this.setFocusToPreviousMenuitem();
+ flag = true;
+ break;
+
+ case 'ArrowDown':
+ case 'Down':
+ this.setFocusToNextMenuitem();
+ flag = true;
+ break;
+
+ case 'Home':
+ case 'PageUp':
+ this.setFocusToFirstMenuitem();
+ flag = true;
+ break;
+
+ case 'End':
+ case 'PageDown':
+ this.setFocusToLastMenuitem();
+ flag = true;
+ break;
+
+ case 'Tab':
+ this.closePopup();
+ break;
+
+ default:
+ if (isPrintableCharacter(key)) {
+ this.setFocusByFirstCharacter(key);
+ flag = true;
+ }
+ break;
+ }
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ }
+
+ onMenuitemMouseover(event) {
+ var tgt = event.currentTarget;
+ this.setFocusToMenuitem(tgt);
+ }
+
+ onMenuitemClick(event) {
+ var tgt = event.currentTarget;
+ this.closePopup();
+ this.performMenuAction(tgt);
+ }
+
+ onBackgroundMousedown(event) {
+ if (!this.domNode.contains(event.target)) {
+ if (this.isOpen()) {
+ this.closePopup();
+ }
+ }
+ }
+}
+
+// Initialize menu buttons
+
+window.addEventListener('load', function () {
+ document.getElementById('action_output').value = 'none';
+
+ function performMenuAction(node) {
+ document.getElementById('action_output').value = node.textContent.trim();
+ }
+
+ var menuButton = document.querySelector('.menu-button-actions');
+ document.defaultView.menuController = new MenuButtonActionsActiveDescendant(menuButton, performMenuAction);
+});
diff --git a/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/menu-button-actions-active-descendant.html b/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/menu-button-actions-active-descendant.html
new file mode 100644
index 000000000..1fb1900c9
--- /dev/null
+++ b/build/tests/menu-button-actions-active-descendant/reference/2021-1-14_171136/menu-button-actions-active-descendant.html
@@ -0,0 +1,57 @@
+
+
+
+ Actions Menu Button Example Using aria-activedescendant
+
+
+
+
+
+
+
+
Actions Menu Button Example Using aria-activedescendant
+
+ This example demonstrates how the
+ menu button design pattern
+ can be used to create a button that opens an actions menu.
+ In this example, choosing an action from the menu will cause the chosen action to be displayed in the Last Action edit box.
+
+
+
+
+
+
diff --git a/tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.html b/build/tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.html
rename to build/tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.html
diff --git a/tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.json b/build/tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.json
rename to build/tests/menu-button-actions-active-descendant/test-01-navigate-forwards-to-menu-button-reading.json
diff --git a/tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.html b/build/tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.html
rename to build/tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.html
diff --git a/tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.json b/build/tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.json
rename to build/tests/menu-button-actions-active-descendant/test-02-navigate-backwards-to-menu-button-reading.json
diff --git a/tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.html b/build/tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.json b/build/tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-03-navigate-forwards-to-menu-button-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.html b/build/tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.json b/build/tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-04-navigate-backwards-to-menu-button-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.html b/build/tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.json b/build/tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-05-navigate-forwards-to-menu-button-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.html b/build/tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.json b/build/tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-06-navigate-backwards-to-menu-button-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.html b/build/tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.html
rename to build/tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.html
diff --git a/tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.json b/build/tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.json
rename to build/tests/menu-button-actions-active-descendant/test-07-read-information-about-menu-button-reading.json
diff --git a/tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.html b/build/tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.json b/build/tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-08-read-information-about-menu-button-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.html b/build/tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.json b/build/tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-09-read-information-about-menu-button-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-10-open-menu-reading.html b/build/tests/menu-button-actions-active-descendant/test-10-open-menu-reading.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-10-open-menu-reading.html
rename to build/tests/menu-button-actions-active-descendant/test-10-open-menu-reading.html
diff --git a/tests/menu-button-actions-active-descendant/test-10-open-menu-reading.json b/build/tests/menu-button-actions-active-descendant/test-10-open-menu-reading.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-10-open-menu-reading.json
rename to build/tests/menu-button-actions-active-descendant/test-10-open-menu-reading.json
diff --git a/tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-11-open-menu-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-12-open-menu-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.html b/build/tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.json b/build/tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-13-open-menu-to-last-item-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.html b/build/tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.json b/build/tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-14-open-menu-to-last-item-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.html b/build/tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.json b/build/tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-15-read-information-about-menu-item-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.html b/build/tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.json b/build/tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-16-read-information-about-menu-item-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-17-navigate-to-first-item-in-menu-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-18-navigate-to-first-item-in-menu-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-19-navigate-to-last-item-in-menu-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-20-navigate-to-last-item-in-menu-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.html b/build/tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.json b/build/tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-21-navigate-to-item-in-menu-by-typing-character-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.html b/build/tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.json b/build/tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-22-navigate-to-item-in-menu-by-typing-character-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.html b/build/tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.json b/build/tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-23-activate-menu-item-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.html b/build/tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.json b/build/tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-24-activate-menu-item-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-25-close-menu-interaction.json
diff --git a/tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.html b/build/tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.html
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.html
rename to build/tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.html
diff --git a/tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.json b/build/tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.json
similarity index 100%
rename from tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.json
rename to build/tests/menu-button-actions-active-descendant/test-26-close-menu-interaction.json
diff --git a/tests/menubar-editor/commands.json b/build/tests/menubar-editor/commands.json
similarity index 100%
rename from tests/menubar-editor/commands.json
rename to build/tests/menubar-editor/commands.json
diff --git a/tests/menubar-editor/index.html b/build/tests/menubar-editor/index.html
similarity index 100%
rename from tests/menubar-editor/index.html
rename to build/tests/menubar-editor/index.html
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/css/menubar-editor.css b/build/tests/menubar-editor/reference/2020-11-19_152059/css/menubar-editor.css
new file mode 100644
index 000000000..72a3f573c
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/css/menubar-editor.css
@@ -0,0 +1,200 @@
+.menubar-editor {
+ margin: 0;
+ padding: 2px;
+ width: 560px;
+}
+
+.menubar-editor.focus {
+ padding: 0;
+ border: solid 2px #034575;
+}
+
+.menubar-editor textarea {
+ padding: 4px;
+ margin: 0;
+ border: 2px solid #eee;
+ height: 400px;
+ width: 548px;
+ font-size: medium;
+ font-family: sans-serif;
+}
+
+.menubar-editor [role="menubar"] {
+ margin: 0;
+ padding: 2px;
+ border: 2px solid #eee;
+ font-size: 110%;
+ list-style: none;
+ background-color: #eee;
+ height: 32px;
+ display: block;
+}
+
+.menubar-editor [role="menubar"] li {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.menubar-editor [role="menubar"] > li {
+ display: inline-block;
+ position: relative;
+ top: 3px;
+ left: 1px;
+}
+
+.menubar-editor [role="menubar"] > li > [role="menuitem"]::after {
+ content: url("../images/down-arrow.svg");
+ padding-left: 0.25em;
+}
+
+.menubar-editor [role="menubar"] > li > [role="menuitem"]:focus::after {
+ content: url("../images/down-arrow-focus.svg");
+}
+
+.menubar-editor
+ [role="menubar"]
+ > li
+ > [role="menuitem"][aria-expanded="true"]::after {
+ content: url("../images/up-arrow-focus.svg");
+}
+
+.menubar-editor [role="menubar"] [role="menu"] {
+ display: none;
+ margin: 0;
+ padding: 2px;
+ position: absolute;
+ border: 2px solid #034575;
+ background-color: #eee;
+}
+
+.menubar-editor [role="menubar"] [role="group"] {
+ margin: 0;
+ padding: 0;
+}
+
+.menubar-editor [role="menubar"] [role="menuitem"][aria-disabled="true"] {
+ color: #666;
+ text-decoration: line-through;
+}
+
+.menubar-editor [role="menubar"] [role="menuitem"],
+.menubar-editor [role="menubar"] [role="menuitemcheckbox"],
+.menubar-editor [role="menubar"] [role="menuitemradio"],
+.menubar-editor [role="menubar"] [role="separator"] {
+ padding: 6px;
+ background-color: #eee;
+ border: 0 solid #eee;
+ color: black;
+}
+
+.menubar-editor [role="menubar"] [role="menuitem"][aria-expanded="true"] {
+ padding: 4px;
+ border: 2px solid #034575;
+ background-color: #034575;
+ color: #fff;
+ outline: none;
+}
+
+.menubar-editor [role="menubar"] [role="menu"] [role="menuitem"],
+.menubar-editor [role="menubar"] [role="menu"] [role="menuitemcheckbox"],
+.menubar-editor [role="menubar"] [role="menu"] [role="menuitemradio"],
+.menubar-editor [role="menubar"] [role="menu"] [role="separator"] {
+ padding-left: 27px;
+ width: 8em;
+}
+
+.menubar-editor [role="menubar"] [role="separator"] {
+ padding-top: 3px;
+ background-image: url("../images/separator.svg");
+ background-position: center;
+ background-repeat: repeat-x;
+}
+
+.menubar-editor [role="menubar"] [role="menu"] [aria-checked="true"] {
+ padding: 6px;
+ padding-left: 8px;
+ padding-right: 18px;
+}
+
+.menubar-editor
+ [role="menubar"]
+ [role="menuitemradio"][aria-checked="true"]::before {
+ content: url("../images/radio-checked.svg");
+ padding-right: 3px;
+}
+
+.menubar-editor
+ [role="menubar"]
+ [role="menuitemcheckbox"][aria-checked="true"]::before {
+ content: url("../images/checkbox-checked.svg");
+ padding-right: 3px;
+}
+
+/* focus and hover styling */
+
+.menubar-editor [role="menubar"] [role="menuitem"]:focus,
+.menubar-editor [role="menubar"] [role="menuitemcheckbox"]:focus,
+.menubar-editor [role="menubar"] [role="menuitemradio"]:focus {
+ padding: 4px;
+ border: 2px solid #034575;
+ background-color: #034575;
+ color: #fff;
+ outline: none;
+}
+
+.menubar-editor
+ [role="menubar"]
+ [role="menuitemradio"][aria-checked="true"]:focus::before {
+ content: url("../images/radio-checked-focus.svg");
+ padding-right: 3px;
+}
+
+.menubar-editor
+ [role="menubar"]
+ [role="menuitemcheckbox"][aria-checked="true"]:focus::before {
+ content: url("../images/checkbox-checked-focus.svg");
+ padding-right: 3px;
+}
+
+.menubar-editor [role="menubar"] [role="menuitem"]:hover {
+ padding: 4px;
+ border: 2px solid #034575;
+}
+
+.menubar-editor [role="menubar"] [role="menu"] [role="menuitem"]:focus,
+.menubar-editor [role="menubar"] [role="menu"] [role="menuitemcheckbox"]:focus,
+.menubar-editor [role="menubar"] [role="menu"] [role="menuitemradio"]:focus {
+ padding-left: 25px;
+}
+
+.menubar-editor
+ [role="menubar"]
+ [role="menu"]
+ [role="menuitem"][aria-checked="true"]:focus,
+.menubar-editor
+ [role="menubar"]
+ [role="menu"]
+ [role="menuitemcheckbox"][aria-checked="true"]:focus,
+.menubar-editor
+ [role="menubar"]
+ [role="menu"]
+ [role="menuitemradio"][aria-checked="true"]:focus {
+ padding-left: 8px;
+ padding-right: 21px;
+}
+
+/*
+* Text area styles
+*/
+.menubar-editor .italic {
+ font-style: italic;
+}
+
+.menubar-editor .bold {
+ font-weight: bold;
+}
+
+.menubar-editor .underline {
+ text-decoration: underline;
+}
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/css/menubar-navigation.css b/build/tests/menubar-editor/reference/2020-11-19_152059/css/menubar-navigation.css
new file mode 100644
index 000000000..dddce81d0
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/css/menubar-navigation.css
@@ -0,0 +1,114 @@
+.menubar-navigation {
+ margin: 0;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ padding: 7px;
+ font-size: 110%;
+ list-style: none;
+ background-color: #eee;
+ border: #eee solid 1px;
+ border-radius: 5px;
+}
+
+.menubar-navigation li {
+ margin: 0;
+ padding: 0;
+ border: 0 solid black;
+ list-style: none;
+}
+
+.menubar-navigation > li {
+ display: inline-block;
+ position: relative;
+}
+
+.menubar-navigation > li li {
+ display: block;
+}
+
+.menubar-navigation [role="menu"] [role="menuitem"],
+.menubar-navigation [role="menu"] [role="separator"] {
+ display: block;
+ width: 12em;
+ margin: 0;
+}
+
+.menubar-navigation [role="menuitem"],
+.menubar-navigation [role="separator"] {
+ padding: 6px;
+ background-color: #eee;
+ border: 0 solid #eee;
+ color: black;
+ border-radius: 5px;
+}
+
+.menubar-navigation [role="menuitem"] svg {
+ fill: currentColor;
+ stroke: currentColor;
+}
+
+.menubar-navigation [role="menuitem"] svg.down {
+ padding-left: 0.125em;
+}
+
+.menubar-navigation [role="menuitem"] svg.right {
+ position: absolute;
+ padding-top: 0.35em;
+ right: 0.75em;
+}
+
+.menubar-navigation [role="menuitem"][aria-expanded="true"] svg.down {
+ transform: rotate(180deg);
+}
+
+.menubar-navigation [role="menuitem"][aria-expanded="true"] svg.right {
+ transform: rotate(90deg) translate(5px, -5px);
+}
+
+.menubar-navigation > li > [role="menuitem"] {
+ display: inline-block;
+}
+
+.menubar-navigation [role="menu"] {
+ display: none;
+ position: absolute;
+ margin: 0;
+ padding: 0;
+ padding: 7px 4px;
+ border: 2px solid #034575;
+ border-radius: 5px;
+ background-color: #eee;
+}
+
+.menubar-navigation [role="group"] {
+ margin: 0;
+ padding: 0;
+}
+
+.menubar-navigation [role="separator"] {
+ padding-top: 3px;
+ background-image: url("../images/separator.svg");
+ background-position: center;
+ background-repeat: repeat-x;
+}
+
+/* focus styling */
+
+.menubar-navigation.focus {
+ padding: 6px;
+ border: #034575 solid 2px;
+}
+
+.menubar-navigation [role="menuitem"][aria-expanded="true"],
+.menubar-navigation [role="menuitem"]:focus,
+.menubar-navigation [role="menuitem"]:hover {
+ background: #034575;
+ color: #fff;
+ outline: none;
+}
+
+.menubar-navigation [role="menuitem"]:focus,
+.menubar-navigation [role="menuitem"]:hover {
+ padding: 2px;
+ border: 4px solid #034575;
+}
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/checkbox-checked-focus.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/checkbox-checked-focus.svg
new file mode 100644
index 000000000..47f273cec
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/checkbox-checked-focus.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/checkbox-checked.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/checkbox-checked.svg
new file mode 100644
index 000000000..4a68a8a97
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/checkbox-checked.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/down-arrow-focus.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/down-arrow-focus.svg
new file mode 100644
index 000000000..8f6ea6637
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/down-arrow-focus.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/down-arrow.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/down-arrow.svg
new file mode 100644
index 000000000..a0a2556a5
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/down-arrow.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/radio-checked-focus.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/radio-checked-focus.svg
new file mode 100644
index 000000000..04c1a03d0
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/radio-checked-focus.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/radio-checked.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/radio-checked.svg
new file mode 100644
index 000000000..f34a549a3
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/radio-checked.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/separator.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/separator.svg
new file mode 100644
index 000000000..b4b39760b
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/separator.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/up-arrow-focus.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/up-arrow-focus.svg
new file mode 100644
index 000000000..ede8a5d71
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/up-arrow-focus.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/images/up-arrow.svg b/build/tests/menubar-editor/reference/2020-11-19_152059/images/up-arrow.svg
new file mode 100644
index 000000000..d034dad15
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/images/up-arrow.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/js/menubar-editor.js b/build/tests/menubar-editor/reference/2020-11-19_152059/js/menubar-editor.js
new file mode 100644
index 000000000..905b4ce87
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/js/menubar-editor.js
@@ -0,0 +1,709 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: menubar-editor.js
+ *
+ * Desc: Creates a menubar to control the styling of text in a textarea element
+ */
+
+'use strict';
+
+var MenubarEditor = function (domNode) {
+ this.domNode = domNode;
+ this.menubarNode = domNode.querySelector('[role=menubar]');
+ this.textareaNode = domNode.querySelector('textarea');
+ this.actionManager = new StyleManager(this.textareaNode);
+
+ this.popups = [];
+ this.menuitemGroups = {};
+ this.menuOrientation = {};
+ this.isPopup = {};
+
+ this.firstChars = {}; // see Menubar init method
+ this.firstMenuitem = {}; // see Menubar init method
+ this.lastMenuitem = {}; // see Menubar init method
+
+ this.initMenu(this.menubarNode);
+ this.domNode.addEventListener('focusin', this.handleFocusin.bind(this));
+ this.domNode.addEventListener('focusout', this.handleFocusout.bind(this));
+
+ window.addEventListener(
+ 'mousedown',
+ this.handleBackgroundMousedown.bind(this),
+ true
+ );
+};
+
+MenubarEditor.prototype.getMenuitems = function (domNode) {
+ var nodes = [];
+
+ var initMenu = this.initMenu.bind(this);
+ var getGroupId = this.getGroupId.bind(this);
+ var menuitemGroups = this.menuitemGroups;
+ var popups = this.popups;
+
+ function findMenuitems(node, group) {
+ var role, flag, groupId;
+
+ while (node) {
+ flag = true;
+ role = node.getAttribute('role');
+
+ switch (role) {
+ case 'menu':
+ node.tabIndex = -1;
+ initMenu(node);
+ flag = false;
+ break;
+
+ case 'group':
+ groupId = getGroupId(node);
+ menuitemGroups[groupId] = [];
+ break;
+
+ case 'menuitem':
+ case 'menuitemradio':
+ case 'menuitemcheckbox':
+ if (node.getAttribute('aria-haspopup') === 'true') {
+ popups.push(node);
+ }
+ nodes.push(node);
+ if (group) {
+ group.push(node);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag && node.firstElementChild) {
+ findMenuitems(node.firstElementChild, menuitemGroups[groupId]);
+ }
+
+ node = node.nextElementSibling;
+ }
+ }
+
+ findMenuitems(domNode.firstElementChild, false);
+
+ return nodes;
+};
+
+MenubarEditor.prototype.initMenu = function (menu) {
+ var i, menuitems, menuitem, role, nextElement;
+
+ var menuId = this.getMenuId(menu);
+
+ menuitems = this.getMenuitems(menu);
+ this.menuOrientation[menuId] = this.getMenuOrientation(menu);
+ this.isPopup[menuId] = menu.getAttribute('role') === 'menu';
+
+ this.menuitemGroups[menuId] = [];
+ this.firstChars[menuId] = [];
+ this.firstMenuitem[menuId] = null;
+ this.lastMenuitem[menuId] = null;
+
+ for (i = 0; i < menuitems.length; i++) {
+ menuitem = menuitems[i];
+ role = menuitem.getAttribute('role');
+
+ if (role.indexOf('menuitem') < 0) {
+ continue;
+ }
+
+ menuitem.tabIndex = -1;
+ this.menuitemGroups[menuId].push(menuitem);
+ this.firstChars[menuId].push(menuitem.textContent[0].toLowerCase());
+
+ menuitem.addEventListener('keydown', this.handleKeydown.bind(this));
+ menuitem.addEventListener('click', this.handleMenuitemClick.bind(this));
+
+ menuitem.addEventListener(
+ 'mouseover',
+ this.handleMenuitemMouseover.bind(this)
+ );
+
+ if (!this.firstMenuitem[menuId]) {
+ if (this.hasPopup(menuitem)) {
+ menuitem.tabIndex = 0;
+ }
+ this.firstMenuitem[menuId] = menuitem;
+ }
+ this.lastMenuitem[menuId] = menuitem;
+ }
+};
+
+/* MenubarEditor FOCUS MANAGEMENT METHODS */
+
+MenubarEditor.prototype.setFocusToMenuitem = function (menuId, newMenuitem) {
+ var isAnyPopupOpen = this.isAnyPopupOpen();
+
+ this.closePopupAll(newMenuitem);
+
+ if (this.hasPopup(newMenuitem)) {
+ if (isAnyPopupOpen) {
+ this.openPopup(newMenuitem);
+ }
+ } else {
+ var menu = this.getMenu(newMenuitem);
+ var cmi = menu.previousElementSibling;
+ if (!this.isOpen(cmi)) {
+ this.openPopup(cmi);
+ }
+ }
+
+ if (this.hasPopup(newMenuitem)) {
+ if (this.menuitemGroups[menuId]) {
+ this.menuitemGroups[menuId].forEach(function (item) {
+ item.tabIndex = -1;
+ });
+ }
+ newMenuitem.tabIndex = 0;
+ }
+
+ newMenuitem.focus();
+};
+
+MenubarEditor.prototype.setFocusToFirstMenuitem = function (menuId) {
+ this.setFocusToMenuitem(menuId, this.firstMenuitem[menuId]);
+};
+
+MenubarEditor.prototype.setFocusToLastMenuitem = function (menuId) {
+ this.setFocusToMenuitem(menuId, this.lastMenuitem[menuId]);
+};
+
+MenubarEditor.prototype.setFocusToPreviousMenuitem = function (
+ menuId,
+ currentMenuitem
+) {
+ var newMenuitem, index;
+
+ if (currentMenuitem === this.firstMenuitem[menuId]) {
+ newMenuitem = this.lastMenuitem[menuId];
+ } else {
+ index = this.menuitemGroups[menuId].indexOf(currentMenuitem);
+ newMenuitem = this.menuitemGroups[menuId][index - 1];
+ }
+
+ this.setFocusToMenuitem(menuId, newMenuitem);
+
+ return newMenuitem;
+};
+
+MenubarEditor.prototype.setFocusToNextMenuitem = function (
+ menuId,
+ currentMenuitem
+) {
+ var newMenuitem, index;
+
+ if (currentMenuitem === this.lastMenuitem[menuId]) {
+ newMenuitem = this.firstMenuitem[menuId];
+ } else {
+ index = this.menuitemGroups[menuId].indexOf(currentMenuitem);
+ newMenuitem = this.menuitemGroups[menuId][index + 1];
+ }
+ this.setFocusToMenuitem(menuId, newMenuitem);
+
+ return newMenuitem;
+};
+
+MenubarEditor.prototype.setFocusByFirstCharacter = function (
+ menuId,
+ currentMenuitem,
+ char
+) {
+ var start, index;
+
+ char = char.toLowerCase();
+
+ // Get start index for search based on position of currentItem
+ start = this.menuitemGroups[menuId].indexOf(currentMenuitem) + 1;
+ if (start >= this.menuitemGroups[menuId].length) {
+ start = 0;
+ }
+
+ // Check remaining slots in the menu
+ index = this.getIndexFirstChars(menuId, start, char);
+
+ // If not found in remaining slots, check from beginning
+ if (index === -1) {
+ index = this.getIndexFirstChars(menuId, 0, char);
+ }
+
+ // If match was found...
+ if (index > -1) {
+ this.setFocusToMenuitem(menuId, this.menuitemGroups[menuId][index]);
+ }
+};
+
+// Utilities
+
+MenubarEditor.prototype.getIndexFirstChars = function (
+ menuId,
+ startIndex,
+ char
+) {
+ for (var i = startIndex; i < this.firstChars[menuId].length; i++) {
+ if (char === this.firstChars[menuId][i]) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+MenubarEditor.prototype.isPrintableCharacter = function (str) {
+ return str.length === 1 && str.match(/\S/);
+};
+
+MenubarEditor.prototype.getIdFromAriaLabel = function (node) {
+ var id = node.getAttribute('aria-label');
+ if (id) {
+ id = id.trim().toLowerCase().replace(' ', '-').replace('/', '-');
+ }
+ return id;
+};
+
+MenubarEditor.prototype.getMenuOrientation = function (node) {
+ var orientation = node.getAttribute('aria-orientation');
+
+ if (!orientation) {
+ var role = node.getAttribute('role');
+
+ switch (role) {
+ case 'menubar':
+ orientation = 'horizontal';
+ break;
+
+ case 'menu':
+ orientation = 'vertical';
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return orientation;
+};
+
+MenubarEditor.prototype.getDataOption = function (node) {
+ var option = false;
+ var hasOption = node.hasAttribute('data-option');
+ var role = node.hasAttribute('role');
+
+ if (!hasOption) {
+ while (node && !hasOption && role !== 'menu' && role !== 'menubar') {
+ node = node.parentNode;
+ if (node) {
+ role = node.getAttribute('role');
+ hasOption = node.hasAttribute('data-option');
+ }
+ }
+ }
+
+ if (node) {
+ option = node.getAttribute('data-option');
+ }
+
+ return option;
+};
+
+MenubarEditor.prototype.getGroupId = function (node) {
+ var id = false;
+ var role = node.getAttribute('role');
+
+ while (node && role !== 'group' && role !== 'menu' && role !== 'menubar') {
+ node = node.parentNode;
+ if (node) {
+ role = node.getAttribute('role');
+ }
+ }
+
+ if (node) {
+ id = role + '-' + this.getIdFromAriaLabel(node);
+ }
+
+ return id;
+};
+
+MenubarEditor.prototype.getMenuId = function (node) {
+ var id = false;
+ var role = node.getAttribute('role');
+
+ while (node && role !== 'menu' && role !== 'menubar') {
+ node = node.parentNode;
+ if (node) {
+ role = node.getAttribute('role');
+ }
+ }
+
+ if (node) {
+ id = role + '-' + this.getIdFromAriaLabel(node);
+ }
+
+ return id;
+};
+
+MenubarEditor.prototype.getMenu = function (menuitem) {
+ var id = false;
+ var menu = menuitem;
+ var role = menuitem.getAttribute('role');
+
+ while (menu && role !== 'menu' && role !== 'menubar') {
+ menu = menu.parentNode;
+ if (menu) {
+ role = menu.getAttribute('role');
+ }
+ }
+
+ return menu;
+};
+
+MenubarEditor.prototype.toggleCheckbox = function (menuitem) {
+ if (menuitem.getAttribute('aria-checked') === 'true') {
+ menuitem.setAttribute('aria-checked', 'false');
+ return false;
+ }
+ menuitem.setAttribute('aria-checked', 'true');
+ return true;
+};
+
+MenubarEditor.prototype.setRadioButton = function (menuitem) {
+ var groupId = this.getGroupId(menuitem);
+ var radiogroupItems = this.menuitemGroups[groupId];
+ radiogroupItems.forEach(function (item) {
+ item.setAttribute('aria-checked', 'false');
+ });
+ menuitem.setAttribute('aria-checked', 'true');
+ return menuitem.textContent;
+};
+
+MenubarEditor.prototype.updateFontSizeMenu = function (menuId) {
+ var fontSizeMenuitems = this.menuitemGroups[menuId];
+ var currentValue = this.actionManager.getFontSize();
+
+ for (var i = 0; i < fontSizeMenuitems.length; i++) {
+ var mi = fontSizeMenuitems[i];
+ var dataOption = mi.getAttribute('data-option');
+ var value = mi.textContent.trim().toLowerCase();
+
+ switch (dataOption) {
+ case 'font-smaller':
+ if (currentValue === 'x-small') {
+ mi.setAttribute('aria-disabled', 'true');
+ } else {
+ mi.removeAttribute('aria-disabled');
+ }
+ break;
+
+ case 'font-larger':
+ if (currentValue === 'x-large') {
+ mi.setAttribute('aria-disabled', 'true');
+ } else {
+ mi.removeAttribute('aria-disabled');
+ }
+ break;
+
+ default:
+ if (currentValue === value) {
+ mi.setAttribute('aria-checked', 'true');
+ } else {
+ mi.setAttribute('aria-checked', 'false');
+ }
+ break;
+ }
+ }
+};
+
+// Popup menu methods
+
+MenubarEditor.prototype.isAnyPopupOpen = function () {
+ for (var i = 0; i < this.popups.length; i++) {
+ if (this.popups[i].getAttribute('aria-expanded') === 'true') {
+ return true;
+ }
+ }
+ return false;
+};
+
+MenubarEditor.prototype.openPopup = function (menuitem) {
+ // set aria-expanded attribute
+ var popupMenu = menuitem.nextElementSibling;
+
+ var rect = menuitem.getBoundingClientRect();
+
+ // set CSS properties
+ popupMenu.style.position = 'absolute';
+ popupMenu.style.top = rect.height - 3 + 'px';
+ popupMenu.style.left = '0px';
+ popupMenu.style.zIndex = 100;
+ popupMenu.style.display = 'block';
+
+ menuitem.setAttribute('aria-expanded', 'true');
+
+ return this.getMenuId(popupMenu);
+};
+
+MenubarEditor.prototype.closePopup = function (menuitem) {
+ var menu, cmi;
+
+ if (this.hasPopup(menuitem)) {
+ if (this.isOpen(menuitem)) {
+ menuitem.setAttribute('aria-expanded', 'false');
+ menuitem.nextElementSibling.style.display = 'none';
+ menuitem.nextElementSibling.style.zIndex = 0;
+ }
+ } else {
+ menu = this.getMenu(menuitem);
+ cmi = menu.previousElementSibling;
+ cmi.setAttribute('aria-expanded', 'false');
+ cmi.focus();
+ menu.style.display = 'none';
+ menu.style.zIndex = 0;
+ }
+ return cmi;
+};
+
+MenubarEditor.prototype.doesNotContain = function (popup, menuitem) {
+ if (menuitem) {
+ return !popup.nextElementSibling.contains(menuitem);
+ }
+ return true;
+};
+
+MenubarEditor.prototype.closePopupAll = function (menuitem) {
+ if (typeof menuitem !== 'object') {
+ menuitem = false;
+ }
+
+ for (var i = 0; i < this.popups.length; i++) {
+ var popup = this.popups[i];
+ if (this.isOpen(popup) && this.doesNotContain(popup, menuitem)) {
+ this.closePopup(popup);
+ }
+ }
+};
+
+MenubarEditor.prototype.hasPopup = function (menuitem) {
+ return menuitem.getAttribute('aria-haspopup') === 'true';
+};
+
+MenubarEditor.prototype.isOpen = function (menuitem) {
+ return menuitem.getAttribute('aria-expanded') === 'true';
+};
+
+// Menu event handlers
+
+MenubarEditor.prototype.handleFocusin = function (event) {
+ this.domNode.classList.add('focus');
+};
+
+MenubarEditor.prototype.handleFocusout = function (event) {
+ this.domNode.classList.remove('focus');
+};
+
+MenubarEditor.prototype.handleBackgroundMousedown = function (event) {
+ if (!this.menubarNode.contains(event.target)) {
+ this.closePopupAll();
+ }
+};
+
+MenubarEditor.prototype.handleKeydown = function (event) {
+ var tgt = event.currentTarget,
+ key = event.key,
+ flag = false,
+ menuId = this.getMenuId(tgt),
+ id,
+ popupMenuId,
+ mi,
+ role,
+ option,
+ value;
+
+ switch (key) {
+ case ' ':
+ case 'Enter':
+ if (this.hasPopup(tgt)) {
+ popupMenuId = this.openPopup(tgt);
+ this.setFocusToFirstMenuitem(popupMenuId);
+ } else {
+ role = tgt.getAttribute('role');
+ option = this.getDataOption(tgt);
+ switch (role) {
+ case 'menuitem':
+ this.actionManager.setOption(option, tgt.textContent);
+ break;
+
+ case 'menuitemcheckbox':
+ value = this.toggleCheckbox(tgt);
+ this.actionManager.setOption(option, value);
+ break;
+
+ case 'menuitemradio':
+ value = this.setRadioButton(tgt);
+ this.actionManager.setOption(option, value);
+ break;
+
+ default:
+ break;
+ }
+
+ if (this.getMenuId(tgt) === 'menu-size') {
+ this.updateFontSizeMenu('menu-size');
+ }
+ this.closePopup(tgt);
+ }
+ flag = true;
+ break;
+
+ case 'ArrowDown':
+ case 'Down':
+ if (this.menuOrientation[menuId] === 'vertical') {
+ this.setFocusToNextMenuitem(menuId, tgt);
+ flag = true;
+ } else {
+ if (this.hasPopup(tgt)) {
+ popupMenuId = this.openPopup(tgt);
+ this.setFocusToFirstMenuitem(popupMenuId);
+ flag = true;
+ }
+ }
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ this.closePopup(tgt);
+ flag = true;
+ break;
+
+ case 'Left':
+ case 'ArrowLeft':
+ if (this.menuOrientation[menuId] === 'horizontal') {
+ this.setFocusToPreviousMenuitem(menuId, tgt);
+ flag = true;
+ } else {
+ mi = this.closePopup(tgt);
+ id = this.getMenuId(mi);
+ mi = this.setFocusToPreviousMenuitem(id, mi);
+ this.openPopup(mi);
+ }
+ break;
+
+ case 'Right':
+ case 'ArrowRight':
+ if (this.menuOrientation[menuId] === 'horizontal') {
+ this.setFocusToNextMenuitem(menuId, tgt);
+ flag = true;
+ } else {
+ mi = this.closePopup(tgt);
+ id = this.getMenuId(mi);
+ mi = this.setFocusToNextMenuitem(id, mi);
+ this.openPopup(mi);
+ }
+ break;
+
+ case 'Up':
+ case 'ArrowUp':
+ if (this.menuOrientation[menuId] === 'vertical') {
+ this.setFocusToPreviousMenuitem(menuId, tgt);
+ flag = true;
+ } else {
+ if (this.hasPopup(tgt)) {
+ popupMenuId = this.openPopup(tgt);
+ this.setFocusToLastMenuitem(popupMenuId);
+ flag = true;
+ }
+ }
+ break;
+
+ case 'Home':
+ case 'PageUp':
+ this.setFocusToFirstMenuitem(menuId, tgt);
+ flag = true;
+ break;
+
+ case 'End':
+ case 'PageDown':
+ this.setFocusToLastMenuitem(menuId, tgt);
+ flag = true;
+ break;
+
+ case 'Tab':
+ this.closePopup(tgt);
+ break;
+
+ default:
+ if (this.isPrintableCharacter(key)) {
+ this.setFocusByFirstCharacter(menuId, tgt, key);
+ flag = true;
+ }
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+MenubarEditor.prototype.handleMenuitemClick = function (event) {
+ var tgt = event.currentTarget;
+ var value;
+
+ if (this.hasPopup(tgt)) {
+ if (this.isOpen(tgt)) {
+ this.closePopup(tgt);
+ } else {
+ var menuId = this.openPopup(tgt);
+ this.setFocusToMenuitem(menuId, tgt);
+ }
+ } else {
+ var role = tgt.getAttribute('role');
+ var option = this.getDataOption(tgt);
+ switch (role) {
+ case 'menuitem':
+ this.actionManager.setOption(option, tgt.textContent);
+ break;
+
+ case 'menuitemcheckbox':
+ value = this.toggleCheckbox(tgt);
+ this.actionManager.setOption(option, value);
+ break;
+
+ case 'menuitemradio':
+ value = this.setRadioButton(tgt);
+ this.actionManager.setOption(option, value);
+ break;
+
+ default:
+ break;
+ }
+
+ if (this.getMenuId(tgt) === 'menu-size') {
+ this.updateFontSizeMenu('menu-size');
+ }
+ this.closePopup(tgt);
+ }
+
+ event.stopPropagation();
+ event.preventDefault();
+};
+
+MenubarEditor.prototype.handleMenuitemMouseover = function (event) {
+ var tgt = event.currentTarget;
+
+ if (this.isAnyPopupOpen() && this.getMenu(tgt)) {
+ this.setFocusToMenuitem(this.getMenu(tgt), tgt);
+ }
+};
+
+// Initialize menubar editor
+
+window.addEventListener('load', function () {
+ var menubarEditors = document.querySelectorAll('.menubar-editor');
+ for (var i = 0; i < menubarEditors.length; i++) {
+ var menubarEditor = new MenubarEditor(menubarEditors[i]);
+ }
+});
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/js/menubar-navigation.js b/build/tests/menubar-editor/reference/2020-11-19_152059/js/menubar-navigation.js
new file mode 100644
index 000000000..ebb2dee4d
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/js/menubar-navigation.js
@@ -0,0 +1,630 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: menubar-navigation.js
+ *
+ * Desc: Creates a menubar of hierarchical set of links
+ */
+
+'use strict';
+
+var MenubarNavigation = function (domNode) {
+ this.domNode = domNode;
+
+ this.popups = [];
+ this.menuitemGroups = {};
+ this.menuOrientation = {};
+ this.isPopup = {};
+ this.isPopout = {};
+ this.openPopups = false;
+
+ this.firstChars = {}; // see Menubar init method
+ this.firstMenuitem = {}; // see Menubar init method
+ this.lastMenuitem = {}; // see Menubar init method
+
+ this.initMenu(domNode, 0);
+
+ domNode.addEventListener('focusin', this.handleMenubarFocusin.bind(this));
+ domNode.addEventListener('focusout', this.handleMenubarFocusout.bind(this));
+
+ window.addEventListener(
+ 'mousedown',
+ this.handleBackgroundMousedown.bind(this),
+ true
+ );
+};
+
+MenubarNavigation.prototype.getMenuitems = function (domNode, depth) {
+ var nodes = [];
+
+ var initMenu = this.initMenu.bind(this);
+ var menuitemGroups = this.menuitemGroups;
+ var popups = this.popups;
+
+ function findMenuitems(node) {
+ var role, flag;
+
+ while (node) {
+ flag = true;
+ role = node.getAttribute('role');
+
+ if (role) {
+ role = role.trim().toLowerCase();
+ }
+
+ switch (role) {
+ case 'menu':
+ node.tabIndex = -1;
+ initMenu(node, depth + 1);
+ flag = false;
+ break;
+
+ case 'menuitem':
+ if (node.getAttribute('aria-haspopup') === 'true') {
+ popups.push(node);
+ }
+ nodes.push(node);
+ break;
+
+ default:
+ break;
+ }
+
+ if (
+ flag &&
+ node.firstElementChild &&
+ node.firstElementChild.tagName !== 'svg'
+ ) {
+ findMenuitems(node.firstElementChild);
+ }
+
+ node = node.nextElementSibling;
+ }
+ }
+
+ findMenuitems(domNode.firstElementChild);
+
+ return nodes;
+};
+
+MenubarNavigation.prototype.initMenu = function (menu, depth) {
+ var menuitems, menuitem, role, nextElement;
+
+ var menuId = this.getMenuId(menu);
+
+ menuitems = this.getMenuitems(menu, depth);
+ this.menuOrientation[menuId] = this.getMenuOrientation(menu);
+
+ this.isPopup[menuId] = menu.getAttribute('role') === 'menu' && depth === 1;
+ this.isPopout[menuId] = menu.getAttribute('role') === 'menu' && depth > 1;
+
+ this.menuitemGroups[menuId] = [];
+ this.firstChars[menuId] = [];
+ this.firstMenuitem[menuId] = null;
+ this.lastMenuitem[menuId] = null;
+
+ for (var i = 0; i < menuitems.length; i++) {
+ menuitem = menuitems[i];
+ role = menuitem.getAttribute('role');
+
+ if (role.indexOf('menuitem') < 0) {
+ continue;
+ }
+
+ menuitem.tabIndex = -1;
+ this.menuitemGroups[menuId].push(menuitem);
+ this.firstChars[menuId].push(menuitem.textContent.trim().toLowerCase()[0]);
+
+ menuitem.addEventListener('keydown', this.handleKeydown.bind(this));
+ menuitem.addEventListener('click', this.handleMenuitemClick.bind(this));
+
+ menuitem.addEventListener(
+ 'mouseover',
+ this.handleMenuitemMouseover.bind(this)
+ );
+
+ if (!this.firstMenuitem[menuId]) {
+ if (this.hasPopup(menuitem)) {
+ menuitem.tabIndex = 0;
+ }
+ this.firstMenuitem[menuId] = menuitem;
+ }
+ this.lastMenuitem[menuId] = menuitem;
+ }
+};
+
+MenubarNavigation.prototype.setFocusToMenuitem = function (
+ menuId,
+ newMenuitem
+) {
+ this.closePopupAll(newMenuitem);
+
+ if (this.menuitemGroups[menuId]) {
+ this.menuitemGroups[menuId].forEach(function (item) {
+ if (item === newMenuitem) {
+ item.tabIndex = 0;
+ newMenuitem.focus();
+ } else {
+ item.tabIndex = -1;
+ }
+ });
+ }
+};
+
+MenubarNavigation.prototype.setFocusToFirstMenuitem = function (
+ menuId,
+ currentMenuitem
+) {
+ this.setFocusToMenuitem(menuId, this.firstMenuitem[menuId]);
+};
+
+MenubarNavigation.prototype.setFocusToLastMenuitem = function (
+ menuId,
+ currentMenuitem
+) {
+ this.setFocusToMenuitem(menuId, this.lastMenuitem[menuId]);
+};
+
+MenubarNavigation.prototype.setFocusToPreviousMenuitem = function (
+ menuId,
+ currentMenuitem
+) {
+ var newMenuitem, index;
+
+ if (currentMenuitem === this.firstMenuitem[menuId]) {
+ newMenuitem = this.lastMenuitem[menuId];
+ } else {
+ index = this.menuitemGroups[menuId].indexOf(currentMenuitem);
+ newMenuitem = this.menuitemGroups[menuId][index - 1];
+ }
+
+ this.setFocusToMenuitem(menuId, newMenuitem);
+
+ return newMenuitem;
+};
+
+MenubarNavigation.prototype.setFocusToNextMenuitem = function (
+ menuId,
+ currentMenuitem
+) {
+ var newMenuitem, index;
+
+ if (currentMenuitem === this.lastMenuitem[menuId]) {
+ newMenuitem = this.firstMenuitem[menuId];
+ } else {
+ index = this.menuitemGroups[menuId].indexOf(currentMenuitem);
+ newMenuitem = this.menuitemGroups[menuId][index + 1];
+ }
+ this.setFocusToMenuitem(menuId, newMenuitem);
+
+ return newMenuitem;
+};
+
+MenubarNavigation.prototype.setFocusByFirstCharacter = function (
+ menuId,
+ currentMenuitem,
+ char
+) {
+ var start, index;
+
+ char = char.toLowerCase();
+
+ // Get start index for search based on position of currentItem
+ start = this.menuitemGroups[menuId].indexOf(currentMenuitem) + 1;
+ if (start >= this.menuitemGroups[menuId].length) {
+ start = 0;
+ }
+
+ // Check remaining slots in the menu
+ index = this.getIndexFirstChars(menuId, start, char);
+
+ // If not found in remaining slots, check from beginning
+ if (index === -1) {
+ index = this.getIndexFirstChars(menuId, 0, char);
+ }
+
+ // If match was found...
+ if (index > -1) {
+ this.setFocusToMenuitem(menuId, this.menuitemGroups[menuId][index]);
+ }
+};
+
+// Utilities
+
+MenubarNavigation.prototype.getIndexFirstChars = function (
+ menuId,
+ startIndex,
+ char
+) {
+ for (var i = startIndex; i < this.firstChars[menuId].length; i++) {
+ if (char === this.firstChars[menuId][i]) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+MenubarNavigation.prototype.isPrintableCharacter = function (str) {
+ return str.length === 1 && str.match(/\S/);
+};
+
+MenubarNavigation.prototype.getIdFromAriaLabel = function (node) {
+ var id = node.getAttribute('aria-label');
+ if (id) {
+ id = id.trim().toLowerCase().replace(' ', '-').replace('/', '-');
+ }
+ return id;
+};
+
+MenubarNavigation.prototype.getMenuOrientation = function (node) {
+ var orientation = node.getAttribute('aria-orientation');
+
+ if (!orientation) {
+ var role = node.getAttribute('role');
+
+ switch (role) {
+ case 'menubar':
+ orientation = 'horizontal';
+ break;
+
+ case 'menu':
+ orientation = 'vertical';
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return orientation;
+};
+
+MenubarNavigation.prototype.getMenuId = function (node) {
+ var id = false;
+ var role = node.getAttribute('role');
+
+ while (node && role !== 'menu' && role !== 'menubar') {
+ node = node.parentNode;
+ if (node) {
+ role = node.getAttribute('role');
+ }
+ }
+
+ if (node) {
+ id = role + '-' + this.getIdFromAriaLabel(node);
+ }
+
+ return id;
+};
+
+MenubarNavigation.prototype.getMenu = function (menuitem) {
+ var id = false;
+ var menu = menuitem;
+ var role = menuitem.getAttribute('role');
+
+ while (menu && role !== 'menu' && role !== 'menubar') {
+ menu = menu.parentNode;
+ if (menu) {
+ role = menu.getAttribute('role');
+ }
+ }
+
+ return menu;
+};
+
+// Popup menu methods
+
+MenubarNavigation.prototype.isAnyPopupOpen = function () {
+ for (var i = 0; i < this.popups.length; i++) {
+ if (this.popups[i].getAttribute('aria-expanded') === 'true') {
+ return true;
+ }
+ }
+ return false;
+};
+
+MenubarNavigation.prototype.openPopup = function (menuId, menuitem) {
+ // set aria-expanded attribute
+ var popupMenu = menuitem.nextElementSibling;
+
+ var rect = menuitem.getBoundingClientRect();
+
+ // Set CSS properties
+ if (this.isPopup[menuId]) {
+ popupMenu.parentNode.style.position = 'relative';
+ popupMenu.style.display = 'block';
+ popupMenu.style.position = 'absolute';
+ popupMenu.style.left = rect.width + 6 + 'px';
+ popupMenu.style.top = '0px';
+ popupMenu.style.zIndex = 100;
+ } else {
+ popupMenu.style.display = 'block';
+ popupMenu.style.position = 'absolute';
+ popupMenu.style.left = '0px';
+ popupMenu.style.top = rect.height + 8 + 'px';
+ popupMenu.style.zIndex = 100;
+ }
+
+ menuitem.setAttribute('aria-expanded', 'true');
+
+ return this.getMenuId(popupMenu);
+};
+
+MenubarNavigation.prototype.closePopout = function (menuitem) {
+ var menu,
+ menuId = this.getMenuId(menuitem),
+ cmi = menuitem;
+
+ while (this.isPopup[menuId] || this.isPopout[menuId]) {
+ menu = this.getMenu(cmi);
+ cmi = menu.previousElementSibling;
+ menuId = this.getMenuId(cmi);
+ cmi.setAttribute('aria-expanded', 'false');
+ menu.style.display = 'none';
+ }
+ cmi.focus();
+ return cmi;
+};
+
+MenubarNavigation.prototype.closePopup = function (menuitem) {
+ var menu,
+ menuId = this.getMenuId(menuitem),
+ cmi = menuitem;
+
+ if (this.isMenubar(menuId)) {
+ if (this.isOpen(menuitem)) {
+ menuitem.setAttribute('aria-expanded', 'false');
+ menuitem.nextElementSibling.style.display = 'none';
+ }
+ } else {
+ menu = this.getMenu(menuitem);
+ cmi = menu.previousElementSibling;
+ cmi.setAttribute('aria-expanded', 'false');
+ cmi.focus();
+ menu.style.display = 'none';
+ }
+
+ return cmi;
+};
+
+MenubarNavigation.prototype.doesNotContain = function (popup, menuitem) {
+ if (menuitem) {
+ return !popup.nextElementSibling.contains(menuitem);
+ }
+ return true;
+};
+
+MenubarNavigation.prototype.closePopupAll = function (menuitem) {
+ if (typeof menuitem !== 'object') {
+ menuitem = false;
+ }
+ for (var i = 0; i < this.popups.length; i++) {
+ var popup = this.popups[i];
+ if (this.doesNotContain(popup, menuitem) && this.isOpen(popup)) {
+ var cmi = popup.nextElementSibling;
+ if (cmi) {
+ popup.setAttribute('aria-expanded', 'false');
+ cmi.style.display = 'none';
+ }
+ }
+ }
+};
+
+MenubarNavigation.prototype.hasPopup = function (menuitem) {
+ return menuitem.getAttribute('aria-haspopup') === 'true';
+};
+
+MenubarNavigation.prototype.isOpen = function (menuitem) {
+ return menuitem.getAttribute('aria-expanded') === 'true';
+};
+
+MenubarNavigation.prototype.isMenubar = function (menuId) {
+ return !this.isPopup[menuId] && !this.isPopout[menuId];
+};
+
+MenubarNavigation.prototype.isMenuHorizontal = function (menuitem) {
+ return this.menuOrientation[menuitem] === 'horizontal';
+};
+
+MenubarNavigation.prototype.hasFocus = function () {
+ return this.domNode.classList.contains('focus');
+};
+
+// Menu event handlers
+
+MenubarNavigation.prototype.handleMenubarFocusin = function (event) {
+ // if the menubar or any of its menus has focus, add styling hook for hover
+ this.domNode.classList.add('focus');
+};
+
+MenubarNavigation.prototype.handleMenubarFocusout = function (event) {
+ // remove styling hook for hover on menubar item
+ this.domNode.classList.remove('focus');
+};
+
+MenubarNavigation.prototype.handleKeydown = function (event) {
+ var tgt = event.currentTarget,
+ key = event.key,
+ flag = false,
+ menuId = this.getMenuId(tgt),
+ id,
+ popupMenuId,
+ mi,
+ role,
+ option,
+ value;
+
+ var isAnyPopupOpen = this.isAnyPopupOpen();
+
+ switch (key) {
+ case ' ':
+ case 'Enter':
+ if (this.hasPopup(tgt)) {
+ this.openPopups = true;
+ popupMenuId = this.openPopup(menuId, tgt);
+ this.setFocusToFirstMenuitem(popupMenuId);
+ } else {
+ if (tgt.href !== '#') {
+ this.closePopupAll();
+ window.location.href = tgt.href;
+ }
+ }
+ flag = true;
+ break;
+
+ case 'Esc':
+ case 'Escape':
+ this.openPopups = false;
+ this.closePopup(tgt);
+ flag = true;
+ break;
+
+ case 'Up':
+ case 'ArrowUp':
+ if (this.isMenuHorizontal(menuId)) {
+ if (this.hasPopup(tgt)) {
+ this.openPopups = true;
+ popupMenuId = this.openPopup(menuId, tgt);
+ this.setFocusToLastMenuitem(popupMenuId);
+ }
+ } else {
+ this.setFocusToPreviousMenuitem(menuId, tgt);
+ }
+ flag = true;
+ break;
+
+ case 'ArrowDown':
+ case 'Down':
+ if (this.isMenuHorizontal(menuId)) {
+ if (this.hasPopup(tgt)) {
+ this.openPopups = true;
+ popupMenuId = this.openPopup(menuId, tgt);
+ this.setFocusToFirstMenuitem(popupMenuId);
+ }
+ } else {
+ this.setFocusToNextMenuitem(menuId, tgt);
+ }
+ flag = true;
+ break;
+
+ case 'Left':
+ case 'ArrowLeft':
+ if (this.isMenuHorizontal(menuId)) {
+ mi = this.setFocusToPreviousMenuitem(menuId, tgt);
+ if (isAnyPopupOpen) {
+ this.openPopup(menuId, mi);
+ }
+ } else {
+ if (this.isPopout[menuId]) {
+ mi = this.closePopup(tgt);
+ id = this.getMenuId(mi);
+ mi = this.setFocusToMenuitem(id, mi);
+ } else {
+ mi = this.closePopup(tgt);
+ id = this.getMenuId(mi);
+ mi = this.setFocusToPreviousMenuitem(id, mi);
+ this.openPopup(id, mi);
+ }
+ }
+ flag = true;
+ break;
+
+ case 'Right':
+ case 'ArrowRight':
+ if (this.isMenuHorizontal(menuId)) {
+ mi = this.setFocusToNextMenuitem(menuId, tgt);
+ if (isAnyPopupOpen) {
+ this.openPopup(menuId, mi);
+ }
+ } else {
+ if (this.hasPopup(tgt)) {
+ popupMenuId = this.openPopup(menuId, tgt);
+ this.setFocusToFirstMenuitem(popupMenuId);
+ } else {
+ mi = this.closePopout(tgt);
+ id = this.getMenuId(mi);
+ mi = this.setFocusToNextMenuitem(id, mi);
+ this.openPopup(id, mi);
+ }
+ }
+ flag = true;
+ break;
+
+ case 'Home':
+ case 'PageUp':
+ this.setFocusToFirstMenuitem(menuId, tgt);
+ flag = true;
+ break;
+
+ case 'End':
+ case 'PageDown':
+ this.setFocusToLastMenuitem(menuId, tgt);
+ flag = true;
+ break;
+
+ case 'Tab':
+ this.openPopups = false;
+ this.closePopup(tgt);
+ break;
+
+ default:
+ if (this.isPrintableCharacter(key)) {
+ this.setFocusByFirstCharacter(menuId, tgt, key);
+ flag = true;
+ }
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+MenubarNavigation.prototype.handleMenuitemClick = function (event) {
+ var tgt = event.currentTarget;
+ var menuId = this.getMenuId(tgt);
+
+ if (this.hasPopup(tgt)) {
+ if (this.isOpen(tgt)) {
+ this.closePopup(tgt);
+ } else {
+ this.closePopupAll(tgt);
+ this.openPopup(menuId, tgt);
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ }
+};
+
+MenubarNavigation.prototype.handleMenuitemMouseover = function (event) {
+ var tgt = event.currentTarget;
+ var menuId = this.getMenuId(tgt);
+
+ if (this.hasFocus()) {
+ this.setFocusToMenuitem(menuId, tgt);
+ }
+
+ if (this.isAnyPopupOpen() || this.hasFocus()) {
+ this.closePopupAll(tgt);
+ if (this.hasPopup(tgt)) {
+ this.openPopup(menuId, tgt);
+ }
+ }
+};
+
+MenubarNavigation.prototype.handleBackgroundMousedown = function (event) {
+ if (!this.domNode.contains(event.target)) {
+ this.closePopupAll();
+ }
+};
+
+// Initialize menubar editor
+
+window.addEventListener('load', function () {
+ var menubarNavs = document.querySelectorAll('.menubar-navigation');
+ for (var i = 0; i < menubarNavs.length; i++) {
+ var menubarNav = new MenubarNavigation(menubarNavs[i]);
+ }
+});
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/js/style-manager.js b/build/tests/menubar-editor/reference/2020-11-19_152059/js/style-manager.js
new file mode 100644
index 000000000..06e35d7c4
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/js/style-manager.js
@@ -0,0 +1,158 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: TextStyling.js
+ *
+ * Desc: Styling functions for changing the style of an item
+ */
+
+'use strict';
+
+var StyleManager = function (node) {
+ this.node = node;
+ this.fontSize = 'medium';
+};
+
+StyleManager.prototype.setFontFamily = function (value) {
+ this.node.style.fontFamily = value;
+};
+
+StyleManager.prototype.setTextDecoration = function (value) {
+ this.node.style.textDecoration = value;
+};
+
+StyleManager.prototype.setTextAlign = function (value) {
+ this.node.style.textAlign = value;
+};
+
+StyleManager.prototype.setFontSize = function (value) {
+ this.fontSize = value;
+ this.node.style.fontSize = value;
+};
+
+StyleManager.prototype.setColor = function (value) {
+ this.node.style.color = value;
+};
+
+StyleManager.prototype.setBold = function (flag) {
+ if (flag) {
+ this.node.style.fontWeight = 'bold';
+ } else {
+ this.node.style.fontWeight = 'normal';
+ }
+};
+
+StyleManager.prototype.setItalic = function (flag) {
+ if (flag) {
+ this.node.style.fontStyle = 'italic';
+ } else {
+ this.node.style.fontStyle = 'normal';
+ }
+};
+
+StyleManager.prototype.fontSmaller = function () {
+ switch (this.fontSize) {
+ case 'small':
+ this.setFontSize('x-small');
+ break;
+
+ case 'medium':
+ this.setFontSize('small');
+ break;
+
+ case 'large':
+ this.setFontSize('medium');
+ break;
+
+ case 'x-large':
+ this.setFontSize('large');
+ break;
+
+ default:
+ break;
+ } // end switch
+};
+
+StyleManager.prototype.fontLarger = function () {
+ switch (this.fontSize) {
+ case 'x-small':
+ this.setFontSize('small');
+ break;
+
+ case 'small':
+ this.setFontSize('medium');
+ break;
+
+ case 'medium':
+ this.setFontSize('large');
+ break;
+
+ case 'large':
+ this.setFontSize('x-large');
+ break;
+
+ default:
+ break;
+ } // end switch
+};
+
+StyleManager.prototype.isMinFontSize = function () {
+ return this.fontSize === 'x-small';
+};
+
+StyleManager.prototype.isMaxFontSize = function () {
+ return this.fontSize === 'x-large';
+};
+
+StyleManager.prototype.getFontSize = function () {
+ return this.fontSize;
+};
+
+StyleManager.prototype.setOption = function (option, value) {
+ option = option.toLowerCase();
+ if (typeof value === 'string') {
+ value = value.toLowerCase();
+ }
+
+ switch (option) {
+ case 'font-bold':
+ this.setBold(value);
+ break;
+
+ case 'font-color':
+ this.setColor(value);
+ break;
+
+ case 'font-family':
+ this.setFontFamily(value);
+ break;
+
+ case 'font-smaller':
+ this.fontSmaller();
+ break;
+
+ case 'font-larger':
+ this.fontLarger();
+ break;
+
+ case 'font-size':
+ this.setFontSize(value);
+ break;
+
+ case 'font-italic':
+ this.setItalic(value);
+ break;
+
+ case 'text-align':
+ this.setTextAlign(value);
+ break;
+
+ case 'text-decoration':
+ this.setTextDecoration(value);
+ break;
+
+ default:
+ break;
+ } // end switch
+};
diff --git a/build/tests/menubar-editor/reference/2020-11-19_152059/menubar-editor.html b/build/tests/menubar-editor/reference/2020-11-19_152059/menubar-editor.html
new file mode 100644
index 000000000..8f65589a1
--- /dev/null
+++ b/build/tests/menubar-editor/reference/2020-11-19_152059/menubar-editor.html
@@ -0,0 +1,822 @@
+
+
+
+
+ Editor Menubar Example | WAI-ARIA Authoring Practices 1.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Editor Menubar Example
+
+ The following example demonstrates using the
+ menubar design pattern
+ to provide access to sets of actions.
+ Each item in the below menubar identifies a category of text formatting actions that can be executed from its submenu.
+ The submenus also demonstrate menuitemradio and menuitemcheckbox elements.
+
Users of assistive technologies can identify which format settings are selected because they are represented by menu item radio and menu item checkbox elements that have a checked state.
+
Disabled menu items are demonstrated in the font size menu, which includes two disabled menuitems.
+
To help communicate that the arrow keys are available for directional navigation within the menubar and its submenus, a border is added to the menubar container when focus is within the menubar.
+
To support operating system high contrast settings, focus is highlighted by adding and removing a border around the menu item with focus.
+
The down arrow and checked icons are made compatible with operating system high contrast settings and hidden from screen readers by using the CSS content property to render images.
+
+ Like desktop menubars, submenus open on mouse hover over a parent item in the menubar only if another submenu is already open.
+ That is, if all submenus are closed, a click on a parent menu item is required to display a submenu.
+ Minimizing automatic popups makes exploring with a screen magnifier easier.
+
+
+ In general, moving focus in response to mouse hover is avoided in accessible widgets; it causes unexpected context changes for keyboard users.
+ However, like desktop menubars, there are two conditions in this example menubar where focus moves in response to hover in order to help maintain context for users who use both keyboard and mouse:
+
+
After a parent menu item in the menubar has been activated and the user hovers over a different parent item in the menubar, focus will follow hover.
+
When a submenu is open and the user hovers over an item in the submenu, focus follows hover.
+
+
+
+
+
+
+
Keyboard Support
+
Menubar
+
+
+
+
Key
+
Function
+
+
+
+
+
+ Space Enter
+
+
Opens submenu and moves focus to first item in the submenu.
+
+
+
+ Escape
+
+
+ If a submenu is open, closes it. Otherwise, does nothing.
+
+
+
+
+ Right Arrow
+
+
+
+
Moves focus to the next item in the menubar.
+
If focus is on the last item, moves focus to the first item.
+
+
+
+
+
+ Left Arrow
+
+
+
+
Moves focus to the previous item in the menubar.
+
If focus is on the first item, moves focus to the last item.
+
+
+
+
+
+ Down Arrow
+
+
Opens submenu and moves focus to first item in the submenu.
+
+
+
+ Up Arrow
+
+
Opens submenu and moves focus to last item in the submenu.
+
+
+
+ Home
+
+
Moves focus to first item in the menubar.
+
+
+
+ End
+
+
Moves focus to last item in the menubar.
+
+
+
+ Character
+
+
+
+
Moves focus to next item in the menubar having a name that starts with the typed character.
+
If none of the items have a name starting with the typed character, focus does not move.
+
+
+
+
+
+
+
Submenu
+
+
+
+
Key
+
Function
+
+
+
+
+
+ Space Enter
+
+
Activates menu item, causing action to be executed, e.g., bold text, change font.
+
+
+
+ Escape
+
+
+
+
Closes submenu.
+
Moves focus to parent menubar item.
+
+
+
+
+
+ Right Arrow
+
+
+
+
Closes submenu.
+
Moves focus to next item in the menubar.
+
Opens submenu of newly focused menubar item, keeping focus on that parent menubar item.
+
+
+
+
+
+ Left Arrow
+
+
+
+
Closes submenu.
+
Moves focus to previous item in the menubar.
+
Opens submenu of newly focused menubar item, keeping focus on that parent menubar item.
+
+
+
+
+
+ Down Arrow
+
+
+
+
Moves focus to the next item in the submenu.
+
If focus is on the last item, moves focus to the first item.
+
+
+
+
+
+ Up Arrow
+
+
+
+
Moves focus to previous item in the submenu.
+
If focus is on the first item, moves focus to the last item.
+
+
+
+
+
+ Home
+
+
Moves focus to the first item in the submenu.
+
+
+
+ End
+
+
Moves focus to the last item in the submenu.
+
+
+
+ Character
+
+
+
+
Moves focus to the next item having a name that starts with the typed character.
+
If none of the items have a name starting with the typed character, focus does not move.
+
+
+
+
+
+
+
+
+
Role, Property, State, and Tabindex Attributes
+
Menubar
+
+
+
+
Role
+
Attribute
+
Element
+
Usage
+
+
+
+
+
+ menubar
+
+
+
+ ul
+
+
+
+
Identifies the element as a menubar container for a set of menuitem elements.
+
Is not focusable because focus is managed using roving tabindex.
+
+
+
+
+
+
+ aria-label="string"
+
+
+
+ ul
+
+
+
+
+ Defines an accessible name for the menubar.
+
+
Helps assistive technology users understand the purpose of the menubar and
+ distinguish it from any other menubars or similar elements on the page.
+
+
+
+
+
+ menuitem
+
+
+
+ span
+
+
+
+
Identifies the element as a menu item within the menubar.
+
Accessible name comes from the text content.
+
+
+
+
+
+
+ tabindex="-1"
+
+
+ span
+
+
+ Makes the menuitem element keyboard focusable but
+ not part of the Tab sequence of the page.
+
+
+
+
+
+ tabindex="0"
+
+
+ span
+
+
+
+
+ Makes the menuitem element keyboard focusable and
+ part of the tab sequence of the page.
+
+
+ Only one menuitem in the menubar has tabindex="0".
+
+
+ When the page loads, the first item in the menubar has tabindex="0".
+
Identifies the element as a menu container for a set of menu items.
+
Is not focusable because focus is managed using roving tabindex.
+
+
+
+
+
+
+ aria-label="string"
+
+
+ ul
+
+
+ Defines an accessible name for the menu.
+
+
+
+
+ menuitem
+
+
+
+ li
+
+
+
+
Identifies the element as an item in the submenu.
+
Accessible name comes from the text content.
+
+
+
+
+
+
+ tabindex="-1"
+
+
+ li
+
+
+ Makes the item focusable but not part of the page tab sequence.
+
+
+
+
+
+ aria-disabled="false"
+
+
+ li
+
+
+ Used on the font size "Smaller" and "Larger" options to indicate they are active.
+
+
+
+
+
+ aria-disabled="true"
+
+
+ li
+
+
+ Used on the font size "Smaller" and "Larger" options to indicate one of the options is not active because the largest or smallest font has been selected.
+
+
+
+
+ menuitemcheckbox
+
+
+
+ li
+
+
+
+
Identifies the element as a menuitemcheckbox.
+
Accessible name comes from the text content.
+
+
+
+
+
+
+ tabindex="-1"
+
+
+ li
+
+
+ Makes the menuitemcheckbox focusable but not part of the page tab sequence.
+
+
+
+
+
+ aria-checked="true"
+
+
+ li
+
+
+
+
+ Indicates that the menuitemcheckbox is checked.
+
+
+ The visual appearance of the selected state is synchronized with the aria-checked value using CSS attribute selectors.
+
+
+
+
+
+
+
+ aria-checked="false"
+
+
+ li
+
+
+
+
+ Indicates that the menuitemcheckbox is NOT checked.
+
+
+ The visual appearance of the selected state is synchronized with the aria-checked value using CSS attribute selectors.
+
+
+
+
+
+
+ separator
+
+
+
+ li
+
+
+
+
Identifies the element as a visual separator between groups of items within a menu, such as groups of menuitemradio or menuitemcheckbox elements.
+
Is not focusable but may be perceivable by a screen reader user when using a reading cursor that does not depend on focus.
+
+
+
+
+
+ group
+
+
+
+ ul
+
+
+
+
+ Identifies the element as a container for a set of menuitemradio elements.
+
+
+ Enables browsers to compute values of aria-setsize and aria-posinset.
+
+
+
+
+
+
+
+ aria-label="string"
+
+
+ ul
+
+
+ Provides an accessible name for the group of menu items.
+
+
+
+
+ menuitemradio
+
+
+
+ li
+
+
+
+
+ Identifies the element as a menuitemradio element.
+
+
+ When all items in a submenu are members of the same radio group,
+ the group is defined by the menu element; a group element is not necessary.
+
+
+ Accessible name is computed from the text content.
+
+
+
+
+
+
+
+ tabindex="-1"
+
+
+ li
+
+
+ Makes the menuitemradio focusable but not part of the page tab sequence.
+
+
+
+
+
+ aria-checked="true"
+
+
+ li
+
+
+
+
+ Indicates the menuitemradio is checked.
+
+
+ The visual appearance of the selected state is synchronized with the aria-checked value using CSS attribute selectors.
+
+
+
+
+
+
+
+ aria-checked="false"
+
+
+ li
+
+
+
+
+ Indicates that the menuitemradio is NOT checked.
+
+
+ The visual appearance of the selected state is synchronized with the aria-checked value using CSS attribute selectors.
+