|
12 | 12 |
|
13 | 13 | import {act, fireEvent, mockClickDefault, pointerMap, render, within} from '@react-spectrum/test-utils-internal';
|
14 | 14 | import {AriaMenuTests} from './AriaMenu.test-util';
|
15 |
| -import {Button, Collection, Header, Keyboard, Menu, MenuContext, MenuItem, MenuSection, MenuTrigger, Popover, Separator, SubmenuTrigger, Text} from '..'; |
| 15 | +import {Button, Collection, Dialog, Header, Heading, Input, Keyboard, Label, Menu, MenuContext, MenuItem, MenuSection, MenuTrigger, Popover, Separator, SubmenuTrigger, Text, TextField} from '..'; |
16 | 16 | import React, {useState} from 'react';
|
17 | 17 | import {Selection, SelectionMode} from '@react-types/shared';
|
| 18 | +import {SubDialogTrigger} from '../src/Menu'; |
18 | 19 | import {UNSTABLE_PortalProvider} from '@react-aria/overlays';
|
19 | 20 | import {User} from '@react-aria/test-utils';
|
20 | 21 | import userEvent from '@testing-library/user-event';
|
@@ -1095,6 +1096,97 @@ describe('Menu', () => {
|
1095 | 1096 | });
|
1096 | 1097 | });
|
1097 | 1098 |
|
| 1099 | + describe('Subdialog', function () { |
| 1100 | + it('should contain focus for subdialogs', async () => { |
| 1101 | + let onAction = jest.fn(); |
| 1102 | + let {getByRole, getAllByRole} = render( |
| 1103 | + <MenuTrigger> |
| 1104 | + <Button aria-label="Menu">☰</Button> |
| 1105 | + <Popover> |
| 1106 | + <Menu onAction={onAction}> |
| 1107 | + <MenuItem id="open">Open</MenuItem> |
| 1108 | + <MenuItem id="rename">Rename…</MenuItem> |
| 1109 | + <MenuItem id="duplicate">Duplicate</MenuItem> |
| 1110 | + <SubDialogTrigger> |
| 1111 | + <MenuItem id="share">Share…</MenuItem> |
| 1112 | + <Popover> |
| 1113 | + <Dialog> |
| 1114 | + {({close}) => ( |
| 1115 | + <form style={{display: 'flex', flexDirection: 'column'}}> |
| 1116 | + <Heading slot="title">Sign up</Heading> |
| 1117 | + <TextField> |
| 1118 | + <Label>First Name: </Label> |
| 1119 | + <Input /> |
| 1120 | + </TextField> |
| 1121 | + <TextField> |
| 1122 | + <Label>Last Name: </Label> |
| 1123 | + <Input /> |
| 1124 | + </TextField> |
| 1125 | + <Button onPress={close}> |
| 1126 | + Submit |
| 1127 | + </Button> |
| 1128 | + </form> |
| 1129 | + )} |
| 1130 | + </Dialog> |
| 1131 | + </Popover> |
| 1132 | + </SubDialogTrigger> |
| 1133 | + <MenuItem id="delete">Delete…</MenuItem> |
| 1134 | + </Menu> |
| 1135 | + </Popover> |
| 1136 | + </MenuTrigger> |
| 1137 | + ); |
| 1138 | + |
| 1139 | + let button = getByRole('button'); |
| 1140 | + expect(button).not.toHaveAttribute('data-pressed'); |
| 1141 | + |
| 1142 | + await user.click(button); |
| 1143 | + expect(button).toHaveAttribute('data-pressed'); |
| 1144 | + |
| 1145 | + let menu = getAllByRole('menu')[0]; |
| 1146 | + expect(getAllByRole('menuitem')).toHaveLength(5); |
| 1147 | + |
| 1148 | + let popover = menu.closest('.react-aria-Popover'); |
| 1149 | + expect(popover).toBeInTheDocument(); |
| 1150 | + expect(popover).toHaveAttribute('data-trigger', 'MenuTrigger'); |
| 1151 | + |
| 1152 | + let triggerItem = getAllByRole('menuitem')[3]; |
| 1153 | + expect(triggerItem).toHaveTextContent('Share…'); |
| 1154 | + expect(triggerItem).toHaveAttribute('aria-haspopup', 'dialog'); |
| 1155 | + expect(triggerItem).toHaveAttribute('aria-expanded', 'false'); |
| 1156 | + // TODO: should this have a different data attribute aka has-subdialog? |
| 1157 | + expect(triggerItem).toHaveAttribute('data-has-submenu', 'true'); |
| 1158 | + expect(triggerItem).not.toHaveAttribute('data-open'); |
| 1159 | + |
| 1160 | + // Open the subdialog |
| 1161 | + await user.pointer({target: triggerItem}); |
| 1162 | + act(() => {jest.runAllTimers();}); |
| 1163 | + expect(triggerItem).toHaveAttribute('data-hovered', 'true'); |
| 1164 | + expect(triggerItem).toHaveAttribute('aria-expanded', 'true'); |
| 1165 | + expect(triggerItem).toHaveAttribute('data-open', 'true'); |
| 1166 | + let subdialog = getAllByRole('dialog')[0]; |
| 1167 | + expect(subdialog).toBeInTheDocument(); |
| 1168 | + |
| 1169 | + let subdialogPopover = subdialog.closest('.react-aria-Popover') as HTMLElement; |
| 1170 | + expect(subdialogPopover).toBeInTheDocument(); |
| 1171 | + expect(subdialogPopover).toHaveAttribute('data-trigger', 'SubDialogTrigger'); |
| 1172 | + |
| 1173 | + let inputs = within(subdialogPopover).getAllByRole('textbox'); |
| 1174 | + let buttons = within(subdialogPopover).getAllByRole('button'); |
| 1175 | + await user.click(inputs[0]); |
| 1176 | + expect(document.activeElement).toBe(inputs[0]); |
| 1177 | + await user.tab(); |
| 1178 | + expect(document.activeElement).toBe(inputs[1]); |
| 1179 | + await user.tab(); |
| 1180 | + expect(document.activeElement).toBe(buttons[0]); |
| 1181 | + await user.tab(); |
| 1182 | + expect(document.activeElement).toBe(inputs[0]); |
| 1183 | + }); |
| 1184 | + |
| 1185 | + // TODO: add more tests |
| 1186 | + // nested subdialogs |
| 1187 | + // test ESC |
| 1188 | + }); |
| 1189 | + |
1098 | 1190 | describe('portalContainer', () => {
|
1099 | 1191 | function InfoMenu(props) {
|
1100 | 1192 | return (
|
|
0 commit comments