listbox.spec.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. import { beVisible, beHidden, haveAttribute, haveClasses, haveText, html, notBeVisible, notHaveAttribute, notExist, haveFocus, test} from '../../../utils'
  2. test('it works with x-model',
  3. [html`
  4. <div
  5. x-data="{ active: null, people: [
  6. { id: 1, name: 'Wade Cooper' },
  7. { id: 2, name: 'Arlene Mccoy' },
  8. { id: 3, name: 'Devon Webb' },
  9. { id: 4, name: 'Tom Cook' },
  10. { id: 5, name: 'Tanya Fox', disabled: true },
  11. { id: 6, name: 'Hellen Schmidt' },
  12. { id: 7, name: 'Caroline Schultz' },
  13. { id: 8, name: 'Mason Heaney' },
  14. { id: 9, name: 'Claudie Smitham' },
  15. { id: 10, name: 'Emil Schaefer' },
  16. ]}"
  17. x-listbox
  18. x-model="active"
  19. >
  20. <label x-listbox:label>Assigned to</label>
  21. <button x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  22. <ul x-listbox:options>
  23. <template x-for="person in people" :key="person.id">
  24. <li
  25. :option="person.id"
  26. x-listbox:option
  27. :value="person"
  28. :disabled="person.disabled"
  29. >
  30. <span x-text="person.name"></span>
  31. </li>
  32. </template>
  33. </ul>
  34. </div>
  35. `],
  36. ({ get }) => {
  37. get('ul').should(notBeVisible())
  38. get('button')
  39. .should(haveText('Select Person'))
  40. .click()
  41. get('ul').should(beVisible())
  42. get('button').click()
  43. get('ul').should(notBeVisible())
  44. get('button').click()
  45. get('[option="2"]').click()
  46. get('ul').should(notBeVisible())
  47. get('button').should(haveText('Arlene Mccoy'))
  48. },
  49. )
  50. test('it works with internal state',
  51. [html`
  52. <div
  53. x-data="{ people: [
  54. { id: 1, name: 'Wade Cooper' },
  55. { id: 2, name: 'Arlene Mccoy' },
  56. { id: 3, name: 'Devon Webb' },
  57. { id: 4, name: 'Tom Cook' },
  58. { id: 5, name: 'Tanya Fox', disabled: true },
  59. { id: 6, name: 'Hellen Schmidt' },
  60. { id: 7, name: 'Caroline Schultz' },
  61. { id: 8, name: 'Mason Heaney' },
  62. { id: 9, name: 'Claudie Smitham' },
  63. { id: 10, name: 'Emil Schaefer' },
  64. ]}"
  65. x-listbox
  66. >
  67. <label x-listbox:label>Assigned to</label>
  68. <button x-listbox:button x-text="$listbox.selected ? $listbox.selected.name : 'Select Person'"></button>
  69. <ul x-listbox:options>
  70. <template x-for="person in people" :key="person.id">
  71. <li
  72. :option="person.id"
  73. x-listbox:option
  74. :value="person"
  75. :disabled="person.disabled"
  76. >
  77. <span x-text="person.name"></span>
  78. </li>
  79. </template>
  80. </ul>
  81. </div>
  82. `],
  83. ({ get }) => {
  84. get('ul').should(notBeVisible())
  85. get('button')
  86. .should(haveText('Select Person'))
  87. .click()
  88. get('ul').should(beVisible())
  89. get('button').click()
  90. get('ul').should(notBeVisible())
  91. get('button').click()
  92. get('[option="2"]').click()
  93. get('ul').should(notBeVisible())
  94. get('button').should(haveText('Arlene Mccoy'))
  95. },
  96. )
  97. test('$listbox/$listboxOption',
  98. [html`
  99. <div
  100. x-data="{ people: [
  101. { id: 1, name: 'Wade Cooper' },
  102. { id: 2, name: 'Arlene Mccoy' },
  103. { id: 3, name: 'Devon Webb' },
  104. { id: 4, name: 'Tom Cook' },
  105. { id: 5, name: 'Tanya Fox', disabled: true },
  106. { id: 6, name: 'Hellen Schmidt' },
  107. { id: 7, name: 'Caroline Schultz' },
  108. { id: 8, name: 'Mason Heaney' },
  109. { id: 9, name: 'Claudie Smitham' },
  110. { id: 10, name: 'Emil Schaefer' },
  111. ]}"
  112. x-listbox
  113. >
  114. <label x-listbox:label>Assigned to</label>
  115. <button x-listbox:button x-text="$listbox.selected ? $listbox.selected.name : 'Select Person'"></button>
  116. <article x-text="$listbox.active?.name"></article>
  117. <ul x-listbox:options>
  118. <template x-for="person in people" :key="person.id">
  119. <li
  120. :option="person.id"
  121. x-listbox:option
  122. :value="person"
  123. :disabled="person.disabled"
  124. :class="{
  125. 'selected': $listboxOption.isSelected,
  126. 'active': $listboxOption.isActive,
  127. 'disabled': $listboxOption.isDisabled,
  128. }"
  129. >
  130. <span x-text="person.name"></span>
  131. </li>
  132. </template>
  133. </ul>
  134. </div>
  135. `],
  136. ({ get }) => {
  137. get('article').should(haveText(''))
  138. get('[option="5"]').should(haveClasses(['disabled']))
  139. get('button')
  140. .should(haveText('Select Person'))
  141. .click()
  142. get('article').should(haveText('Wade Cooper'))
  143. get('[option="1"]').should(haveClasses(['active']))
  144. get('ul').type('{downarrow}')
  145. get('article').should(haveText('Arlene Mccoy'))
  146. get('[option="2"]').should(haveClasses(['active']))
  147. get('button').should(haveText('Select Person'))
  148. get('[option="2"]').click()
  149. get('button').should(haveText('Arlene Mccoy'))
  150. get('[option="2"]').should(haveClasses(['selected']))
  151. },
  152. )
  153. test('"name" prop',
  154. [html`
  155. <div
  156. x-data="{ people: [
  157. { id: 1, name: 'Wade Cooper' },
  158. { id: 2, name: 'Arlene Mccoy' },
  159. { id: 3, name: 'Devon Webb' },
  160. { id: 4, name: 'Tom Cook' },
  161. { id: 5, name: 'Tanya Fox', disabled: true },
  162. { id: 6, name: 'Hellen Schmidt' },
  163. { id: 7, name: 'Caroline Schultz' },
  164. { id: 8, name: 'Mason Heaney' },
  165. { id: 9, name: 'Claudie Smitham' },
  166. { id: 10, name: 'Emil Schaefer' },
  167. ]}"
  168. x-listbox
  169. name="person"
  170. >
  171. <label x-listbox:label>Assigned to</label>
  172. <button x-listbox:button x-text="$listbox.selected ? $listbox.selected : 'Select Person'"></button>
  173. <ul x-listbox:options>
  174. <template x-for="person in people" :key="person.id">
  175. <li
  176. :option="person.id"
  177. x-listbox:option
  178. :value="person.id"
  179. :disabled="person.disabled"
  180. :class="{
  181. 'selected': $listboxOption.isSelected,
  182. 'active': $listboxOption.isActive,
  183. }"
  184. >
  185. <span x-text="person.name"></span>
  186. </li>
  187. </template>
  188. </ul>
  189. </div>
  190. `],
  191. ({ get }) => {
  192. get('input').should(haveAttribute('value', 'null'))
  193. get('button').click()
  194. get('input').should(haveAttribute('value', 'null'))
  195. get('[option="2"]').click()
  196. get('input').should(beHidden())
  197. .should(haveAttribute('name', 'person'))
  198. .should(haveAttribute('value', '2'))
  199. .should(haveAttribute('type', 'hidden'))
  200. get('button').click()
  201. get('[option="4"]').click()
  202. get('input').should(beHidden())
  203. .should(haveAttribute('name', 'person'))
  204. .should(haveAttribute('value', '4'))
  205. .should(haveAttribute('type', 'hidden'))
  206. },
  207. );
  208. test('"name" prop with object value',
  209. [html`
  210. <div
  211. x-data="{ people: [
  212. { id: 1, name: 'Wade Cooper' },
  213. { id: 2, name: 'Arlene Mccoy' },
  214. { id: 3, name: 'Devon Webb' },
  215. { id: 4, name: 'Tom Cook' },
  216. { id: 5, name: 'Tanya Fox', disabled: true },
  217. { id: 6, name: 'Hellen Schmidt' },
  218. { id: 7, name: 'Caroline Schultz' },
  219. { id: 8, name: 'Mason Heaney' },
  220. { id: 9, name: 'Claudie Smitham' },
  221. { id: 10, name: 'Emil Schaefer' },
  222. ]}"
  223. x-listbox
  224. name="person"
  225. >
  226. <label x-listbox:label>Assigned to</label>
  227. <button x-listbox:button x-text="$listbox.selected ? $listbox.selected.name : 'Select Person'"></button>
  228. <ul x-listbox:options>
  229. <template x-for="person in people" :key="person.id">
  230. <li
  231. :option="person.id"
  232. x-listbox:option
  233. :value="person"
  234. :disabled="person.disabled"
  235. :class="{
  236. 'selected': $listboxOption.isSelected,
  237. 'active': $listboxOption.isActive,
  238. }"
  239. >
  240. <span x-text="person.name"></span>
  241. </li>
  242. </template>
  243. </ul>
  244. </div>
  245. `],
  246. ({ get }) => {
  247. get('input[name="person"]').should(haveAttribute('value', 'null'))
  248. get('button').click()
  249. get('[name="person[id]"]').should(notExist())
  250. get('[option="2"]').click()
  251. get('input[name="person"]').should(notExist())
  252. get('[name="person[id]"]').should(beHidden())
  253. .should(haveAttribute('value', '2'))
  254. .should(haveAttribute('type', 'hidden'))
  255. get('[name="person[name]"]').should(beHidden())
  256. .should(haveAttribute('value', 'Arlene Mccoy'))
  257. .should(haveAttribute('type', 'hidden'))
  258. get('button').click()
  259. get('[option="4"]').click()
  260. get('[name="person[id]"]').should(beHidden())
  261. .should(haveAttribute('value', '4'))
  262. .should(haveAttribute('type', 'hidden'))
  263. get('[name="person[name]"]').should(beHidden())
  264. .should(haveAttribute('value', 'Tom Cook'))
  265. .should(haveAttribute('type', 'hidden'))
  266. },
  267. );
  268. // @todo: support default-value
  269. test('"default-value" prop',
  270. [html`
  271. <div
  272. x-data="{ people: [
  273. { id: 1, name: 'Wade Cooper' },
  274. { id: 2, name: 'Arlene Mccoy' },
  275. { id: 3, name: 'Devon Webb' },
  276. { id: 4, name: 'Tom Cook' },
  277. { id: 5, name: 'Tanya Fox', disabled: true },
  278. { id: 6, name: 'Hellen Schmidt' },
  279. { id: 7, name: 'Caroline Schultz' },
  280. { id: 8, name: 'Mason Heaney' },
  281. { id: 9, name: 'Claudie Smitham' },
  282. { id: 10, name: 'Emil Schaefer' },
  283. ]}"
  284. x-listbox
  285. name="person"
  286. default-value="2"
  287. >
  288. <label x-listbox:label>Assigned to</label>
  289. <button x-listbox:button x-text="$listbox.selected ? $listbox.selected : 'Select Person'"></button>
  290. <ul x-listbox:options>
  291. <template x-for="person in people" :key="person.id">
  292. <li
  293. :option="person.id"
  294. x-listbox:option
  295. :value="person.id"
  296. :disabled="person.disabled"
  297. :class="{
  298. 'selected': $listboxOption.isSelected,
  299. 'active': $listboxOption.isActive,
  300. }"
  301. >
  302. <span x-text="person.name"></span>
  303. </li>
  304. </template>
  305. </ul>
  306. </div>
  307. `],
  308. ({ get }) => {
  309. get('input').should(beHidden())
  310. .should(haveAttribute('name', 'person'))
  311. .should(haveAttribute('value', '2'))
  312. .should(haveAttribute('type', 'hidden'))
  313. },
  314. );
  315. test('"multiple" prop',
  316. [html`
  317. <div
  318. x-data="{
  319. people: [
  320. { id: 1, name: 'Wade Cooper' },
  321. { id: 2, name: 'Arlene Mccoy' },
  322. { id: 3, name: 'Devon Webb' },
  323. { id: 4, name: 'Tom Cook' },
  324. { id: 5, name: 'Tanya Fox', disabled: true },
  325. { id: 6, name: 'Hellen Schmidt' },
  326. { id: 7, name: 'Caroline Schultz' },
  327. { id: 8, name: 'Mason Heaney' },
  328. { id: 9, name: 'Claudie Smitham' },
  329. { id: 10, name: 'Emil Schaefer' },
  330. ]
  331. }"
  332. x-listbox
  333. multiple
  334. >
  335. <label x-listbox:label>Assigned to</label>
  336. <button x-listbox:button x-text="$listbox.selected ? $listbox.selected.join(',') : 'Select People'"></button>
  337. <ul x-listbox:options>
  338. <template x-for="person in people" :key="person.id">
  339. <li
  340. :option="person.id"
  341. x-listbox:option
  342. :value="person.id"
  343. :disabled="person.disabled"
  344. :class="{
  345. 'selected': $listboxOption.isSelected,
  346. 'active': $listboxOption.isActive,
  347. }"
  348. >
  349. <span x-text="person.name"></span>
  350. </li>
  351. </template>
  352. </ul>
  353. </div>
  354. `],
  355. ({ get }) => {
  356. get('button').click()
  357. get('[option="2"]').click()
  358. get('ul').should(beVisible())
  359. get('button').should(haveText('2'))
  360. get('[option="4"]').click()
  361. get('button').should(haveText('2,4'))
  362. get('ul').should(beVisible())
  363. get('[option="4"]').click()
  364. get('button').should(haveText('2'))
  365. get('ul').should(beVisible())
  366. },
  367. );
  368. test('"multiple" and "name" props together',
  369. [html`
  370. <div
  371. x-data="{
  372. people: [
  373. { id: 1, name: 'Wade Cooper' },
  374. { id: 2, name: 'Arlene Mccoy' },
  375. { id: 3, name: 'Devon Webb' },
  376. { id: 4, name: 'Tom Cook' },
  377. { id: 5, name: 'Tanya Fox', disabled: true },
  378. { id: 6, name: 'Hellen Schmidt' },
  379. { id: 7, name: 'Caroline Schultz' },
  380. { id: 8, name: 'Mason Heaney' },
  381. { id: 9, name: 'Claudie Smitham' },
  382. { id: 10, name: 'Emil Schaefer' },
  383. ]
  384. }"
  385. x-listbox
  386. multiple
  387. name="people"
  388. >
  389. <label x-listbox:label>Assigned to</label>
  390. <button x-listbox:button x-text="$listbox.selected ? $listbox.selected.map(p => p.id).join(',') : 'Select People'"></button>
  391. <ul x-listbox:options>
  392. <template x-for="person in people" :key="person.id">
  393. <li
  394. :option="person.id"
  395. x-listbox:option
  396. :value="person"
  397. :disabled="person.disabled"
  398. :class="{
  399. 'selected': $listboxOption.isSelected,
  400. 'active': $listboxOption.isActive,
  401. }"
  402. >
  403. <span x-text="person.name"></span>
  404. </li>
  405. </template>
  406. </ul>
  407. </div>
  408. `],
  409. ({ get }) => {
  410. get('input[name="people"]').should(haveAttribute('value', 'null'))
  411. get('button').click()
  412. get('[name="people[0][id]"]').should(notExist())
  413. get('[option="2"]').click()
  414. get('ul').should(beVisible())
  415. get('button').should(haveText('2'))
  416. get('input[name="people"]').should(notExist())
  417. get('[name="people[0][id]"]').should(beHidden())
  418. .should(haveAttribute('value', '2'))
  419. .should(haveAttribute('type', 'hidden'))
  420. get('[name="people[0][name]"]').should(beHidden())
  421. .should(haveAttribute('value', 'Arlene Mccoy'))
  422. .should(haveAttribute('type', 'hidden'))
  423. get('[option="4"]').click()
  424. get('[name="people[0][id]"]').should(beHidden())
  425. .should(haveAttribute('value', '2'))
  426. .should(haveAttribute('type', 'hidden'))
  427. get('[name="people[0][name]"]').should(beHidden())
  428. .should(haveAttribute('value', 'Arlene Mccoy'))
  429. .should(haveAttribute('type', 'hidden'))
  430. get('[name="people[1][id]"]').should(beHidden())
  431. .should(haveAttribute('value', '4'))
  432. .should(haveAttribute('type', 'hidden'))
  433. get('[name="people[1][name]"]').should(beHidden())
  434. .should(haveAttribute('value', 'Tom Cook'))
  435. .should(haveAttribute('type', 'hidden'))
  436. get('button').should(haveText('2,4'))
  437. get('ul').should(beVisible())
  438. get('[option="4"]').click()
  439. get('[name="people[0][id]"]').should(beHidden())
  440. .should(haveAttribute('value', '2'))
  441. .should(haveAttribute('type', 'hidden'))
  442. get('[name="people[0][name]"]').should(beHidden())
  443. .should(haveAttribute('value', 'Arlene Mccoy'))
  444. .should(haveAttribute('type', 'hidden'))
  445. get('[name="people[1][id]"]').should(notExist())
  446. get('[name="people[1][name]"]').should(notExist())
  447. get('button').should(haveText('2'))
  448. get('ul').should(beVisible())
  449. },
  450. );
  451. test('keyboard controls',
  452. [html`
  453. <div
  454. x-data="{ active: null, people: [
  455. { id: 1, name: 'Wade Cooper' },
  456. { id: 2, name: 'Arlene Mccoy' },
  457. { id: 3, name: 'Devon Webb', disabled: true },
  458. { id: 4, name: 'Tom Cook' },
  459. { id: 5, name: 'Tanya Fox', disabled: true },
  460. { id: 6, name: 'Hellen Schmidt' },
  461. { id: 7, name: 'Caroline Schultz' },
  462. { id: 8, name: 'Mason Heaney' },
  463. { id: 9, name: 'Claudie Smitham' },
  464. { id: 10, name: 'Emil Schaefer' },
  465. ]}"
  466. x-listbox
  467. x-model="active"
  468. >
  469. <label x-listbox:label>Assigned to</label>
  470. <button x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  471. <ul x-listbox:options options>
  472. <template x-for="person in people" :key="person.id">
  473. <li
  474. :option="person.id"
  475. x-listbox:option
  476. :value="person"
  477. :disabled="person.disabled"
  478. :class="{
  479. 'selected': $listboxOption.isSelected,
  480. 'active': $listboxOption.isActive,
  481. }"
  482. >
  483. <span x-text="person.name"></span>
  484. </li>
  485. </template>
  486. </ul>
  487. </div>
  488. `],
  489. ({ get }) => {
  490. get('.active').should(notExist())
  491. get('button').focus().type(' ')
  492. get('[options]')
  493. .should(beVisible())
  494. .should(haveFocus())
  495. get('[option="1"')
  496. .should(haveClasses(['active']))
  497. get('[options]')
  498. .type('{downarrow}')
  499. get('[option="2"]')
  500. .should(haveClasses(['active']))
  501. get('[options]')
  502. .type('{downarrow}')
  503. get('[option="4"]')
  504. .should(haveClasses(['active']))
  505. get('[options]')
  506. .type('{uparrow}')
  507. get('[option="2"]')
  508. .should(haveClasses(['active']))
  509. get('[options]')
  510. .type('{home}')
  511. get('[option="1"')
  512. .should(haveClasses(['active']))
  513. get('[options]')
  514. .type('{end}')
  515. get('[option="10"]')
  516. .should(haveClasses(['active']))
  517. get('[options]')
  518. .type('{pageUp}')
  519. get('[option="1"')
  520. .should(haveClasses(['active']))
  521. get('[options]')
  522. .type('{pageDown}')
  523. get('[option="10"]')
  524. .should(haveClasses(['active']))
  525. get('[options]')
  526. .tab()
  527. .should(haveFocus())
  528. .should(beVisible())
  529. .tab({ shift: true })
  530. .should(haveFocus())
  531. .should(beVisible())
  532. .type('{esc}')
  533. .should(notBeVisible())
  534. },
  535. )
  536. // @todo support horizontal prop
  537. test('"horizontal" keyboard controls',
  538. [html`
  539. <div
  540. x-data="{ active: null, people: [
  541. { id: 1, name: 'Wade Cooper' },
  542. { id: 2, name: 'Arlene Mccoy' },
  543. { id: 3, name: 'Devon Webb', disabled: true },
  544. { id: 4, name: 'Tom Cook' },
  545. { id: 5, name: 'Tanya Fox', disabled: true },
  546. { id: 6, name: 'Hellen Schmidt' },
  547. { id: 7, name: 'Caroline Schultz' },
  548. { id: 8, name: 'Mason Heaney' },
  549. { id: 9, name: 'Claudie Smitham' },
  550. { id: 10, name: 'Emil Schaefer' },
  551. ]}"
  552. x-listbox
  553. x-model="active"
  554. horizontal
  555. >
  556. <label x-listbox:label>Assigned to</label>
  557. <button x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  558. <ul x-listbox:options options>
  559. <template x-for="person in people" :key="person.id">
  560. <li
  561. :option="person.id"
  562. x-listbox:option
  563. :value="person"
  564. :disabled="person.disabled"
  565. :class="{
  566. 'selected': $listboxOption.isSelected,
  567. 'active': $listboxOption.isActive,
  568. }"
  569. >
  570. <span x-text="person.name"></span>
  571. </li>
  572. </template>
  573. </ul>
  574. </div>
  575. `],
  576. ({ get }) => {
  577. get('.active').should(notExist())
  578. get('button').focus().type(' ')
  579. get('[options]')
  580. .should(beVisible())
  581. .should(haveFocus())
  582. get('[option="1"')
  583. .should(haveClasses(['active']))
  584. get('[options]')
  585. .type('{rightarrow}')
  586. get('[option="2"]')
  587. .should(haveClasses(['active']))
  588. get('[options]')
  589. .type('{rightarrow}')
  590. get('[option="4"]')
  591. .should(haveClasses(['active']))
  592. get('[options]')
  593. .type('{leftarrow}')
  594. get('[option="2"]')
  595. .should(haveClasses(['active']))
  596. },
  597. )
  598. test('search',
  599. [html`
  600. <div
  601. x-data="{ active: null, people: [
  602. { id: 1, name: 'Wade Cooper' },
  603. { id: 2, name: 'Arlene Mccoy' },
  604. { id: 3, name: 'Devon Webb', disabled: true },
  605. { id: 4, name: 'Tom Cook' },
  606. { id: 5, name: 'Tanya Fox', disabled: true },
  607. { id: 6, name: 'Hellen Schmidt' },
  608. { id: 7, name: 'Caroline Schultz' },
  609. { id: 8, name: 'Mason Heaney' },
  610. { id: 9, name: 'Claudie Smitham' },
  611. { id: 10, name: 'Emil Schaefer' },
  612. ]}"
  613. x-listbox
  614. x-model="active"
  615. >
  616. <label x-listbox:label>Assigned to</label>
  617. <button x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  618. <ul x-listbox:options options>
  619. <template x-for="person in people" :key="person.id">
  620. <li
  621. :option="person.id"
  622. x-listbox:option
  623. :value="person"
  624. :disabled="person.disabled"
  625. :class="{
  626. 'selected': $listboxOption.isSelected,
  627. 'active': $listboxOption.isActive,
  628. }"
  629. >
  630. <span x-text="person.name"></span>
  631. </li>
  632. </template>
  633. </ul>
  634. </div>
  635. `],
  636. ({ get, wait }) => {
  637. get('.active').should(notExist())
  638. get('button').click()
  639. get('[options]')
  640. .type('ar')
  641. get('[option="2"')
  642. .should(haveClasses(['active']))
  643. wait(500)
  644. get('[options]')
  645. .type('ma')
  646. get('[option="8"]')
  647. .should(haveClasses(['active']))
  648. },
  649. )
  650. test('changing value manually changes internal state',
  651. [html`
  652. <div
  653. x-data="{ active: null, people: [
  654. { id: 1, name: 'Wade Cooper' },
  655. { id: 2, name: 'Arlene Mccoy' },
  656. { id: 3, name: 'Devon Webb', disabled: true },
  657. { id: 4, name: 'Tom Cook' },
  658. { id: 5, name: 'Tanya Fox', disabled: true },
  659. { id: 6, name: 'Hellen Schmidt' },
  660. { id: 7, name: 'Caroline Schultz' },
  661. { id: 8, name: 'Mason Heaney' },
  662. { id: 9, name: 'Claudie Smitham' },
  663. { id: 10, name: 'Emil Schaefer' },
  664. ]}"
  665. x-listbox
  666. x-model="active"
  667. >
  668. <label x-listbox:label>Assigned to</label>
  669. <button toggle x-listbox:button x-text="$listbox.selected ? $listbox.selected : 'Select Person'"></button>
  670. <button select-tim @click="active = 4">Select Tim</button>
  671. <ul x-listbox:options options>
  672. <template x-for="person in people" :key="person.id">
  673. <li
  674. :option="person.id"
  675. x-listbox:option
  676. :value="person.id"
  677. :disabled="person.disabled"
  678. :class="{
  679. 'selected': $listboxOption.isSelected,
  680. 'active': $listboxOption.isActive,
  681. }"
  682. >
  683. <span x-text="person.name"></span>
  684. </li>
  685. </template>
  686. </ul>
  687. </div>
  688. `],
  689. ({ get }) => {
  690. get('.active').should(notExist())
  691. get('[toggle]').click()
  692. get('[option="2"')
  693. .click()
  694. .should(haveClasses(['selected']))
  695. get('[select-tim]').click()
  696. get('[option="4"]').should(haveClasses(['selected']))
  697. get('[toggle]').should(haveText('4'))
  698. },
  699. )
  700. // @todo update these assertions to be accurate
  701. test('has accessibility attributes',
  702. [html`
  703. <div
  704. x-data="{ active: null, people: [
  705. { id: 1, name: 'Wade Cooper' },
  706. { id: 2, name: 'Arlene Mccoy' },
  707. { id: 3, name: 'Devon Webb', disabled: true },
  708. { id: 4, name: 'Tom Cook' },
  709. { id: 5, name: 'Tanya Fox', disabled: true },
  710. { id: 6, name: 'Hellen Schmidt' },
  711. { id: 7, name: 'Caroline Schultz' },
  712. { id: 8, name: 'Mason Heaney' },
  713. { id: 9, name: 'Claudie Smitham' },
  714. { id: 10, name: 'Emil Schaefer' },
  715. ]}"
  716. x-listbox
  717. x-model="active"
  718. >
  719. <label x-listbox:label>Assigned to</label>
  720. <button x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  721. <ul x-listbox:options options>
  722. <template x-for="person in people" :key="person.id">
  723. <li
  724. :option="person.id"
  725. x-listbox:option
  726. :value="person"
  727. :disabled="person.disabled"
  728. :class="{
  729. 'selected': $listboxOption.isSelected,
  730. 'active': $listboxOption.isActive,
  731. }"
  732. >
  733. <span x-text="person.name"></span>
  734. </li>
  735. </template>
  736. </ul>
  737. </div>
  738. `],
  739. ({ get }) => {
  740. get('button')
  741. .should(haveAttribute('aria-haspopup', 'true'))
  742. .should(haveAttribute('aria-labelledby', 'alpine-listbox-label-1'))
  743. .should(haveAttribute('aria-expanded', 'false'))
  744. .should(notHaveAttribute('aria-controls'))
  745. .should(haveAttribute('id', 'alpine-listbox-button-1'))
  746. .click()
  747. .should(haveAttribute('aria-expanded', 'true'))
  748. .should(haveAttribute('aria-controls', 'alpine-listbox-items-1'))
  749. get('[options]')
  750. .should(haveAttribute('aria-orientation', 'vertical'))
  751. .should(haveAttribute('role', 'menu'))
  752. .should(haveAttribute('id', 'alpine-listbox-items-1'))
  753. .should(haveAttribute('aria-labelledby', 'alpine-listbox-button-1'))
  754. .should(notHaveAttribute('aria-activedescendant'))
  755. .should(haveAttribute('tabindex', '0'))
  756. .type('{downarrow}')
  757. .should(haveAttribute('aria-activedescendant', 'alpine-listbox-item-1'))
  758. get('[option="1"')
  759. .should(haveAttribute('role', 'menuitem'))
  760. .should(haveAttribute('id', 'alpine-listbox-item-1'))
  761. .should(haveAttribute('tabindex', '-1'))
  762. get('[option="2"]')
  763. .should(haveAttribute('role', 'menuitem'))
  764. .should(haveAttribute('id', 'alpine-listbox-item-2'))
  765. .should(haveAttribute('tabindex', '-1'))
  766. get('[options]')
  767. .type('{downarrow}')
  768. .should(haveAttribute('aria-activedescendant', 'alpine-listbox-item-2'))
  769. },
  770. )
  771. test.only('"static" prop',
  772. [html`
  773. <div
  774. x-data="{ active: null, show: false, people: [
  775. { id: 1, name: 'Wade Cooper' },
  776. { id: 2, name: 'Arlene Mccoy' },
  777. { id: 3, name: 'Devon Webb' },
  778. { id: 4, name: 'Tom Cook' },
  779. { id: 5, name: 'Tanya Fox', disabled: true },
  780. { id: 6, name: 'Hellen Schmidt' },
  781. { id: 7, name: 'Caroline Schultz' },
  782. { id: 8, name: 'Mason Heaney' },
  783. { id: 9, name: 'Claudie Smitham' },
  784. { id: 10, name: 'Emil Schaefer' },
  785. ]}"
  786. x-listbox
  787. x-model="active"
  788. >
  789. <label x-listbox:label>Assigned to</label>
  790. <button normal-toggle x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  791. <button real-toggle @click="show = ! show">Toggle</button>
  792. <ul x-listbox:options x-show="show" static>
  793. <template x-for="person in people" :key="person.id">
  794. <li
  795. :option="person.id"
  796. x-listbox:option
  797. :value="person"
  798. :disabled="person.disabled"
  799. >
  800. <span x-text="person.name"></span>
  801. </li>
  802. </template>
  803. </ul>
  804. </div>
  805. `],
  806. ({ get }) => {
  807. get('ul').should(notBeVisible())
  808. get('[normal-toggle]')
  809. .should(haveText('Select Person'))
  810. .click()
  811. get('ul').should(notBeVisible())
  812. get('[real-toggle]').click()
  813. get('ul').should(beVisible())
  814. get('[option="2"]').click()
  815. get('ul').should(beVisible())
  816. get('[normal-toggle]').should(haveText('Arlene Mccoy'))
  817. },
  818. )
  819. // test "by" attribute