combobox.spec.js 31 KB

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