radio.spec.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. import { haveAttribute, haveFocus, html, haveClasses, notHaveClasses, test, haveText, notExist, beHidden, } from '../../../utils'
  2. test('it works using x-model',
  3. [html`
  4. <main x-data="{ active: null, access: [
  5. {
  6. id: 'access-1',
  7. name: 'Public access',
  8. description: 'This project would be available to anyone who has the link',
  9. disabled: false,
  10. },
  11. {
  12. id: 'access-2',
  13. name: 'Private to Project Members',
  14. description: 'Only members of this project would be able to access',
  15. disabled: false,
  16. },
  17. {
  18. id: 'access-3',
  19. name: 'Private to you',
  20. description: 'You are the only one able to access this project',
  21. disabled: true,
  22. },
  23. {
  24. id: 'access-4',
  25. name: 'Private to you',
  26. description: 'You are the only one able to access this project',
  27. disabled: false,
  28. },
  29. ]}">
  30. <div x-radio x-model="active">
  31. <fieldset>
  32. <legend>
  33. <h2 x-radio:label>Privacy setting</h2>
  34. </legend>
  35. <div>
  36. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  37. <div :option="id" x-radio:option :value="id" :disabled="disabled">
  38. <span x-radio:label x-text="name"></span>
  39. <span x-radio:description x-text="description"></span>
  40. </div>
  41. </template>
  42. </div>
  43. </fieldset>
  44. </div>
  45. <input x-model="active" type="hidden">
  46. </main>
  47. `],
  48. ({ get }) => {
  49. get('input').should(haveAttribute('value', ''))
  50. get('[option="access-2"]').click()
  51. get('input').should(haveAttribute('value', 'access-2'))
  52. get('[option="access-4"]').click()
  53. get('input').should(haveAttribute('value', 'access-4'))
  54. },
  55. )
  56. test('it works without x-model/with default-value',
  57. [html`
  58. <main x-data="{ access: [
  59. {
  60. id: 'access-1',
  61. name: 'Public access',
  62. description: 'This project would be available to anyone who has the link',
  63. disabled: false,
  64. },
  65. {
  66. id: 'access-2',
  67. name: 'Private to Project Members',
  68. description: 'Only members of this project would be able to access',
  69. disabled: false,
  70. },
  71. {
  72. id: 'access-3',
  73. name: 'Private to you',
  74. description: 'You are the only one able to access this project',
  75. disabled: true,
  76. },
  77. {
  78. id: 'access-4',
  79. name: 'Private to you',
  80. description: 'You are the only one able to access this project',
  81. disabled: false,
  82. },
  83. ]}">
  84. <div x-radio default-value="access-4">
  85. <fieldset>
  86. <legend>
  87. <h2 x-radio:label>Privacy setting</h2>
  88. </legend>
  89. <div>
  90. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  91. <div :option="id" x-radio:option :value="id" :disabled="disabled">
  92. <span x-radio:label x-text="name"></span>
  93. <span x-radio:description x-text="description"></span>
  94. </div>
  95. </template>
  96. </div>
  97. </fieldset>
  98. </div>
  99. </main>
  100. `],
  101. ({ get }) => {
  102. get('[option="access-4"]').should(haveAttribute('aria-checked', 'true'))
  103. get('[option="access-2"]').click()
  104. get('[option="access-2"]').should(haveAttribute('aria-checked', 'true'))
  105. },
  106. )
  107. test('cannot select any option when the group is disabled',
  108. [html`
  109. <main x-data="{ active: null, access: [
  110. {
  111. id: 'access-1',
  112. name: 'Public access',
  113. description: 'This project would be available to anyone who has the link',
  114. disabled: false,
  115. },
  116. {
  117. id: 'access-2',
  118. name: 'Private to Project Members',
  119. description: 'Only members of this project would be able to access',
  120. disabled: false,
  121. },
  122. {
  123. id: 'access-3',
  124. name: 'Private to you',
  125. description: 'You are the only one able to access this project',
  126. disabled: true,
  127. },
  128. {
  129. id: 'access-4',
  130. name: 'Private to you',
  131. description: 'You are the only one able to access this project',
  132. disabled: false,
  133. },
  134. ]}">
  135. <div x-radio x-model="active" disabled>
  136. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  137. <div :option="id" x-radio:option :value="id" :disabled="disabled">
  138. <span x-radio:label x-text="name"></span>
  139. <span x-radio:description x-text="description"></span>
  140. </div>
  141. </template>
  142. </div>
  143. <input x-model="active" type="hidden">
  144. </main>
  145. `],
  146. ({ get }) => {
  147. get('input').should(haveAttribute('value', ''))
  148. get('[option="access-1"]').click()
  149. get('input').should(haveAttribute('value', ''))
  150. },
  151. )
  152. test('cannot select a disabled option',
  153. [html`
  154. <main x-data="{ active: null, access: [
  155. {
  156. id: 'access-1',
  157. name: 'Public access',
  158. description: 'This project would be available to anyone who has the link',
  159. disabled: false,
  160. },
  161. {
  162. id: 'access-2',
  163. name: 'Private to Project Members',
  164. description: 'Only members of this project would be able to access',
  165. disabled: false,
  166. },
  167. {
  168. id: 'access-3',
  169. name: 'Private to you',
  170. description: 'You are the only one able to access this project',
  171. disabled: true,
  172. },
  173. {
  174. id: 'access-4',
  175. name: 'Private to you',
  176. description: 'You are the only one able to access this project',
  177. disabled: false,
  178. },
  179. ]}">
  180. <div x-radio x-model="active">
  181. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  182. <div :option="id" x-radio:option :value="id" :disabled="disabled">
  183. <span x-radio:label x-text="name"></span>
  184. <span x-radio:description x-text="description"></span>
  185. </div>
  186. </template>
  187. </div>
  188. <input x-model="active" type="hidden">
  189. </main>
  190. `],
  191. ({ get }) => {
  192. get('input').should(haveAttribute('value', ''))
  193. get('[option="access-3"]').click()
  194. get('input').should(haveAttribute('value', ''))
  195. },
  196. )
  197. test('keyboard navigation works',
  198. [html`
  199. <main x-data="{ active: null, access: [
  200. {
  201. id: 'access-1',
  202. name: 'Public access',
  203. description: 'This project would be available to anyone who has the link',
  204. disabled: false,
  205. },
  206. {
  207. id: 'access-2',
  208. name: 'Private to Project Members',
  209. description: 'Only members of this project would be able to access',
  210. disabled: false,
  211. },
  212. {
  213. id: 'access-3',
  214. name: 'Private to you',
  215. description: 'You are the only one able to access this project',
  216. disabled: true,
  217. },
  218. {
  219. id: 'access-4',
  220. name: 'Private to you',
  221. description: 'You are the only one able to access this project',
  222. disabled: false,
  223. },
  224. ]}">
  225. <div x-radio x-model="active">
  226. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  227. <div :option="id" x-radio:option :value="id" :disabled="disabled">
  228. <span x-radio:label x-text="name"></span>
  229. <span x-radio:description x-text="description"></span>
  230. </div>
  231. </template>
  232. </div>
  233. <input x-model="active" type="hidden">
  234. </main>
  235. `],
  236. ({ get }) => {
  237. get('input').should(haveAttribute('value', ''))
  238. get('[option="access-1"]').focus().type('{downarrow}')
  239. get('[option="access-2"]').should(haveFocus()).type('{downarrow}')
  240. get('[option="access-4"]').should(haveFocus()).type('{downarrow}')
  241. get('[option="access-1"]').should(haveFocus())
  242. },
  243. )
  244. test('has accessibility attributes',
  245. [html`
  246. <main x-data="{ active: null, access: [
  247. {
  248. id: 'access-1',
  249. name: 'Public access',
  250. description: 'This project would be available to anyone who has the link',
  251. disabled: false,
  252. },
  253. {
  254. id: 'access-2',
  255. name: 'Private to Project Members',
  256. description: 'Only members of this project would be able to access',
  257. disabled: false,
  258. },
  259. {
  260. id: 'access-3',
  261. name: 'Private to you',
  262. description: 'You are the only one able to access this project',
  263. disabled: true,
  264. },
  265. {
  266. id: 'access-4',
  267. name: 'Private to you',
  268. description: 'You are the only one able to access this project',
  269. disabled: false,
  270. },
  271. ]}">
  272. <div x-radio group x-model="active">
  273. <fieldset>
  274. <legend>
  275. <h2 x-radio:label>Privacy setting</h2>
  276. <p x-radio:description>Some description</p>
  277. </legend>
  278. <div>
  279. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  280. <div :option="id" x-radio:option :value="id" :disabled="disabled">
  281. <span :label="id" x-radio:label x-text="name"></span>
  282. <span :description="id" x-radio:description x-text="description"></span>
  283. </div>
  284. </template>
  285. </div>
  286. </fieldset>
  287. </div>
  288. </main>
  289. `],
  290. ({ get }) => {
  291. get('[group]').should(haveAttribute('role', 'radiogroup'))
  292. .should(haveAttribute('aria-labelledby', 'alpine-radio-label-1'))
  293. .should(haveAttribute('aria-describedby', 'alpine-radio-description-1'))
  294. get('h2').should(haveAttribute('id', 'alpine-radio-label-1'))
  295. get('p').should(haveAttribute('id', 'alpine-radio-description-1'))
  296. get('[option="access-1"]')
  297. .should(haveAttribute('tabindex', 0))
  298. for (i in 4) {
  299. get(`[option="access-${i}"]`)
  300. .should(haveAttribute('role', 'radio'))
  301. .should(haveAttribute('aria-disabled', 'false'))
  302. .should(haveAttribute('aria-labelledby', `alpine-radio-label-${i + 1}`))
  303. .should(haveAttribute('aria-describedby', `alpine-radio-description-${i + 1}`))
  304. get(`[label="access-${i}"]`)
  305. .should(haveAttribute('id', `alpine-radio-label-${i + 1}`))
  306. get(`[description="access-${i}"]`)
  307. .should(haveAttribute('id', `alpine-radio-description-${i + 1}`))
  308. }
  309. get('[option="access-1"]')
  310. .click()
  311. .should(haveAttribute('aria-checked', 'true'))
  312. },
  313. )
  314. test('$radioOption.isActive, $radioOption.isChecked, $radioOption.isDisabled work',
  315. [html`
  316. <main x-data="{ access: [
  317. {
  318. id: 'access-1',
  319. name: 'Public access',
  320. description: 'This project would be available to anyone who has the link',
  321. disabled: false,
  322. },
  323. {
  324. id: 'access-2',
  325. name: 'Private to Project Members',
  326. description: 'Only members of this project would be able to access',
  327. disabled: false,
  328. },
  329. {
  330. id: 'access-3',
  331. name: 'Private to you',
  332. description: 'You are the only one able to access this project',
  333. disabled: true,
  334. },
  335. {
  336. id: 'access-4',
  337. name: 'Private to you',
  338. description: 'You are the only one able to access this project',
  339. disabled: false,
  340. },
  341. ]}">
  342. <div x-radio group>
  343. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  344. <div
  345. :option="id"
  346. x-radio:option
  347. :value="id"
  348. :disabled="disabled"
  349. :class="{
  350. 'active': $radioOption.isActive,
  351. 'checked': $radioOption.isChecked,
  352. 'disabled': $radioOption.isDisabled,
  353. }"
  354. >
  355. <span :label="id" x-radio:label x-text="name"></span>
  356. <span :description="id" x-radio:description x-text="description"></span>
  357. </div>
  358. </template>
  359. </div>
  360. </main>
  361. `],
  362. ({ get }) => {
  363. get('[option="access-1"]')
  364. .should(notHaveClasses(['active', 'checked', 'disabled']))
  365. .focus()
  366. .should(haveClasses(['active']))
  367. .should(notHaveClasses(['checked']))
  368. .type(' ')
  369. .should(haveClasses(['active', 'checked']))
  370. .type('{downarrow}')
  371. get('[option="access-2"]')
  372. .should(haveClasses(['active', 'checked']))
  373. get('[option="access-3"]')
  374. .should(haveClasses(['disabled']))
  375. },
  376. )
  377. test('can bind objects to the value',
  378. [html`
  379. <main x-data="{ active: null, access: [
  380. {
  381. id: 'access-1',
  382. name: 'Public access',
  383. description: 'This project would be available to anyone who has the link',
  384. disabled: false,
  385. },
  386. {
  387. id: 'access-2',
  388. name: 'Private to Project Members',
  389. description: 'Only members of this project would be able to access',
  390. disabled: false,
  391. },
  392. {
  393. id: 'access-3',
  394. name: 'Private to you',
  395. description: 'You are the only one able to access this project',
  396. disabled: true,
  397. },
  398. {
  399. id: 'access-4',
  400. name: 'Private to you',
  401. description: 'You are the only one able to access this project',
  402. disabled: false,
  403. },
  404. ]}">
  405. <div x-radio group x-model="active">
  406. <template x-for="(option, i) in access" :key="option.id">
  407. <div
  408. :option="option.id"
  409. x-radio:option
  410. :value="option"
  411. :disabled="option.disabled"
  412. >
  413. <span :label="option.id" x-radio:label x-text="option.name"></span>
  414. <span :description="option.id" x-radio:description x-text="option.description"></span>
  415. </div>
  416. </template>
  417. </div>
  418. <article x-text="JSON.stringify(active)"></article>
  419. </main>
  420. `],
  421. ({ get }) => {
  422. get('[option="access-2"]').click()
  423. get('article')
  424. .should(haveText(JSON.stringify({
  425. id: 'access-2',
  426. name: 'Private to Project Members',
  427. description: 'Only members of this project would be able to access',
  428. disabled: false,
  429. })))
  430. },
  431. )
  432. test('name prop',
  433. [html`
  434. <main
  435. x-data="{
  436. active: null,
  437. access: [
  438. {
  439. id: 'access-1',
  440. name: 'Public access',
  441. description: 'This project would be available to anyone who has the link',
  442. disabled: false,
  443. },
  444. {
  445. id: 'access-2',
  446. name: 'Private to Project Members',
  447. description: 'Only members of this project would be able to access',
  448. disabled: false,
  449. },
  450. {
  451. id: 'access-3',
  452. name: 'Private to you',
  453. description: 'You are the only one able to access this project',
  454. disabled: true,
  455. },
  456. {
  457. id: 'access-4',
  458. name: 'Private to you',
  459. description: 'You are the only one able to access this project',
  460. disabled: false,
  461. },
  462. ]
  463. }
  464. ">
  465. <div x-radio group x-model="active" name="access">
  466. <template x-for="({ id, name, description, disabled }, i) in access" :key="id">
  467. <div
  468. :option="id"
  469. x-radio:option
  470. :value="id"
  471. :disabled="disabled"
  472. >
  473. <span :label="id" x-radio:label x-text="name"></span>
  474. <span :description="id" x-radio:description x-text="description"></span>
  475. </div>
  476. </template>
  477. </div>
  478. </main>
  479. `],
  480. ({ get }) => {
  481. get('input').should(notExist())
  482. get('[option="access-2"]').click()
  483. get('input').should(beHidden())
  484. .should(haveAttribute('name', 'access'))
  485. .should(haveAttribute('value', 'access-2'))
  486. .should(haveAttribute('type', 'hidden'))
  487. get('[option="access-4"]').click()
  488. get('input').should(beHidden())
  489. .should(haveAttribute('name', 'access'))
  490. .should(haveAttribute('value', 'access-4'))
  491. .should(haveAttribute('type', 'hidden'))
  492. },
  493. )