listbox.spec.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. import { beVisible, beHidden, haveAttribute, haveClasses, notHaveClasses, 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(haveAttribute('orientation', 'horizontal'))
  581. .should(beVisible())
  582. .should(haveFocus())
  583. get('[option="1"]')
  584. .should(haveClasses(['active']))
  585. get('[options]')
  586. .type('{rightarrow}')
  587. get('[option="2"]')
  588. .should(haveClasses(['active']))
  589. get('[options]')
  590. .type('{rightarrow}')
  591. get('[option="4"]')
  592. .should(haveClasses(['active']))
  593. get('[options]')
  594. .type('{leftarrow}')
  595. get('[option="2"]')
  596. .should(haveClasses(['active']))
  597. },
  598. )
  599. test('search',
  600. [html`
  601. <div
  602. x-data="{ active: null, people: [
  603. { id: 1, name: 'Wade Cooper' },
  604. { id: 2, name: 'Arlene Mccoy' },
  605. { id: 3, name: 'Devon Webb', disabled: true },
  606. { id: 4, name: 'Tom Cook' },
  607. { id: 5, name: 'Tanya Fox', disabled: true },
  608. { id: 6, name: 'Hellen Schmidt' },
  609. { id: 7, name: 'Caroline Schultz' },
  610. { id: 8, name: 'Mason Heaney' },
  611. { id: 9, name: 'Claudie Smitham' },
  612. { id: 10, name: 'Emil Schaefer' },
  613. ]}"
  614. x-listbox
  615. x-model="active"
  616. >
  617. <label x-listbox:label>Assigned to</label>
  618. <button x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  619. <ul x-listbox:options options>
  620. <template x-for="person in people" :key="person.id">
  621. <li
  622. :option="person.id"
  623. x-listbox:option
  624. :value="person"
  625. :disabled="person.disabled"
  626. :class="{
  627. 'selected': $listboxOption.isSelected,
  628. 'active': $listboxOption.isActive,
  629. }"
  630. >
  631. <span x-text="person.name"></span>
  632. </li>
  633. </template>
  634. </ul>
  635. </div>
  636. `],
  637. ({ get, wait }) => {
  638. get('.active').should(notExist())
  639. get('button').click()
  640. get('[options]')
  641. .type('ar')
  642. get('[option="2"]')
  643. .should(haveClasses(['active']))
  644. wait(500)
  645. get('[options]')
  646. .type('ma')
  647. get('[option="8"]')
  648. .should(haveClasses(['active']))
  649. },
  650. )
  651. // @todo make sure $listboxOption.isSelected works in this case (aria-selected and the values are working correctly but the class is not applied)
  652. test('changing value manually changes internal state',
  653. [html`
  654. <div
  655. x-data="{ active: null, people: [
  656. { id: 1, name: 'Wade Cooper' },
  657. { id: 2, name: 'Arlene Mccoy' },
  658. { id: 3, name: 'Devon Webb', disabled: true },
  659. { id: 4, name: 'Tom Cook' },
  660. { id: 5, name: 'Tanya Fox', disabled: true },
  661. { id: 6, name: 'Hellen Schmidt' },
  662. { id: 7, name: 'Caroline Schultz' },
  663. { id: 8, name: 'Mason Heaney' },
  664. { id: 9, name: 'Claudie Smitham' },
  665. { id: 10, name: 'Emil Schaefer' },
  666. ]}"
  667. x-listbox
  668. x-model="active"
  669. >
  670. <label x-listbox:label>Assigned to</label>
  671. <button toggle x-listbox:button x-text="$listbox.selected ? $listbox.selected : 'Select Person'"></button>
  672. <button select-tim @click="active = 4">Select Tim</button>
  673. <ul x-listbox:options options>
  674. <template x-for="person in people" :key="person.id">
  675. <li
  676. :option="person.id"
  677. x-listbox:option
  678. :value="person.id"
  679. :disabled="person.disabled"
  680. :class="{
  681. 'selected': $listboxOption.isSelected,
  682. 'active': $listboxOption.isActive,
  683. }"
  684. >
  685. <span x-text="person.name"></span>
  686. </li>
  687. </template>
  688. </ul>
  689. </div>
  690. `],
  691. ({ get }) => {
  692. get('[toggle]').click()
  693. get('[option="2"]')
  694. .click()
  695. .should(haveClasses(['selected']))
  696. get('[select-tim]').click()
  697. get('[option="4"]').should(haveClasses(['selected']))
  698. get('[option="1"]').should(notHaveClasses(['selected']))
  699. get('[toggle]').should(haveText('4'))
  700. },
  701. )
  702. test('has accessibility attributes',
  703. [html`
  704. <div
  705. x-data="{ active: null, people: [
  706. { id: 1, name: 'Wade Cooper' },
  707. { id: 2, name: 'Arlene Mccoy' },
  708. { id: 3, name: 'Devon Webb', disabled: true },
  709. { id: 4, name: 'Tom Cook' },
  710. { id: 5, name: 'Tanya Fox', disabled: true },
  711. { id: 6, name: 'Hellen Schmidt' },
  712. { id: 7, name: 'Caroline Schultz' },
  713. { id: 8, name: 'Mason Heaney' },
  714. { id: 9, name: 'Claudie Smitham' },
  715. { id: 10, name: 'Emil Schaefer' },
  716. ]}"
  717. x-listbox
  718. x-model="active"
  719. >
  720. <label x-listbox:label>Assigned to</label>
  721. <button x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  722. <ul x-listbox:options options>
  723. <template x-for="person in people" :key="person.id">
  724. <li
  725. :option="person.id"
  726. x-listbox:option
  727. :value="person"
  728. :disabled="person.disabled"
  729. :class="{
  730. 'selected': $listboxOption.isSelected,
  731. 'active': $listboxOption.isActive,
  732. }"
  733. >
  734. <span x-text="person.name"></span>
  735. </li>
  736. </template>
  737. </ul>
  738. </div>
  739. `],
  740. ({ get }) => {
  741. get('button')
  742. .should(haveAttribute('aria-haspopup', 'true'))
  743. .should(haveAttribute('aria-labelledby', 'alpine-listbox-label-1'))
  744. .should(haveAttribute('aria-expanded', 'false'))
  745. .should(notHaveAttribute('aria-controls'))
  746. .should(haveAttribute('id', 'alpine-listbox-button-1'))
  747. .click()
  748. .should(haveAttribute('aria-expanded', 'true'))
  749. .should(haveAttribute('aria-controls', 'alpine-listbox-options-1'))
  750. get('[options]')
  751. .should(haveAttribute('aria-orientation', 'vertical'))
  752. .should(haveAttribute('role', 'listbox'))
  753. .should(haveAttribute('id', 'alpine-listbox-options-1'))
  754. .should(haveAttribute('aria-labelledby', 'alpine-listbox-button-1'))
  755. .should(notHaveAttribute('aria-activedescendant'))
  756. .should(haveAttribute('tabindex', '0'))
  757. .should(haveAttribute('aria-activedescendant', 'alpine-listbox-option-1'))
  758. get('[option="1"]')
  759. .should(haveAttribute('role', 'option'))
  760. .should(haveAttribute('id', 'alpine-listbox-option-1'))
  761. .should(haveAttribute('tabindex', '-1'))
  762. .should(haveAttribute('aria-selected', 'false'))
  763. get('[option="2"]')
  764. .should(haveAttribute('role', 'option'))
  765. .should(haveAttribute('id', 'alpine-listbox-option-2'))
  766. .should(haveAttribute('tabindex', '-1'))
  767. .should(haveAttribute('aria-selected', 'false'))
  768. get('[options]')
  769. .type('{downarrow}')
  770. .should(haveAttribute('aria-activedescendant', 'alpine-listbox-option-2'))
  771. get('[option="2"]')
  772. .click()
  773. .should(haveAttribute('aria-selected', 'true'))
  774. },
  775. )
  776. test('"static" prop',
  777. [html`
  778. <div
  779. x-data="{ active: null, show: false, people: [
  780. { id: 1, name: 'Wade Cooper' },
  781. { id: 2, name: 'Arlene Mccoy' },
  782. { id: 3, name: 'Devon Webb' },
  783. { id: 4, name: 'Tom Cook' },
  784. { id: 5, name: 'Tanya Fox', disabled: true },
  785. { id: 6, name: 'Hellen Schmidt' },
  786. { id: 7, name: 'Caroline Schultz' },
  787. { id: 8, name: 'Mason Heaney' },
  788. { id: 9, name: 'Claudie Smitham' },
  789. { id: 10, name: 'Emil Schaefer' },
  790. ]}"
  791. x-listbox
  792. x-model="active"
  793. >
  794. <label x-listbox:label>Assigned to</label>
  795. <button normal-toggle x-listbox:button x-text="active ? active.name : 'Select Person'"></button>
  796. <button real-toggle @click="show = ! show">Toggle</button>
  797. <ul x-listbox:options x-show="show" static>
  798. <template x-for="person in people" :key="person.id">
  799. <li
  800. :option="person.id"
  801. x-listbox:option
  802. :value="person"
  803. :disabled="person.disabled"
  804. >
  805. <span x-text="person.name"></span>
  806. </li>
  807. </template>
  808. </ul>
  809. </div>
  810. `],
  811. ({ get }) => {
  812. get('ul').should(notBeVisible())
  813. get('[normal-toggle]')
  814. .should(haveText('Select Person'))
  815. .click()
  816. get('ul').should(notBeVisible())
  817. get('[real-toggle]').click()
  818. get('ul').should(beVisible())
  819. get('[option="2"]').click()
  820. get('ul').should(beVisible())
  821. get('[normal-toggle]').should(haveText('Arlene Mccoy'))
  822. },
  823. )
  824. // test "by" attribute