shadcn-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +40 -0
  3. data/CHANGELOG.md +54 -0
  4. data/CLAUDE.md +463 -0
  5. data/PROGRESS.md +485 -0
  6. data/README.md +1483 -0
  7. data/Rakefile +29 -0
  8. data/__tests__/controllers/__snapshots__/calendar_controller.test.js.snap +13 -0
  9. data/__tests__/controllers/__snapshots__/popover_controller.test.js.snap +46 -0
  10. data/__tests__/controllers/__snapshots__/sheet_controller.test.js.snap +111 -0
  11. data/__tests__/controllers/__snapshots__/tabs_controller.test.js.snap +27 -0
  12. data/__tests__/controllers/accordion_controller.test.js +904 -0
  13. data/__tests__/controllers/calendar_controller.test.js +1370 -0
  14. data/__tests__/controllers/carousel_controller.test.js +912 -0
  15. data/__tests__/controllers/checkbox_controller.test.js +454 -0
  16. data/__tests__/controllers/collapsible_controller.test.js +407 -0
  17. data/__tests__/controllers/combobox_controller.test.js +966 -0
  18. data/__tests__/controllers/context_menu_controller.test.js +627 -0
  19. data/__tests__/controllers/date_picker_controller.test.js +636 -0
  20. data/__tests__/controllers/dialog_controller.test.js +878 -0
  21. data/__tests__/controllers/drawer_controller.test.js +995 -0
  22. data/__tests__/controllers/menubar_controller.test.js +736 -0
  23. data/__tests__/controllers/navigation_menu_controller.test.js +598 -0
  24. data/__tests__/controllers/popover_controller.test.js +1007 -0
  25. data/__tests__/controllers/radio_group_controller.test.js +640 -0
  26. data/__tests__/controllers/resizable_controller.test.js +680 -0
  27. data/__tests__/controllers/select_controller.test.js +674 -0
  28. data/__tests__/controllers/sheet_controller.test.js +986 -0
  29. data/__tests__/controllers/slider_controller.test.js +1036 -0
  30. data/__tests__/controllers/switch_controller.test.js +424 -0
  31. data/__tests__/controllers/tabs_controller.test.js +907 -0
  32. data/__tests__/controllers/toggle_group_controller.test.js +839 -0
  33. data/__tests__/controllers/tooltip_controller.test.js +808 -0
  34. data/__tests__/helpers/stimulus-test-helper.js +203 -0
  35. data/app/assets/config/manifest.js +1 -0
  36. data/app/assets/javascripts/shadcn/controllers/accordion_controller.d.ts +53 -0
  37. data/app/assets/javascripts/shadcn/controllers/accordion_controller.js +140 -0
  38. data/app/assets/javascripts/shadcn/controllers/avatar_controller.d.ts +22 -0
  39. data/app/assets/javascripts/shadcn/controllers/avatar_controller.js +26 -0
  40. data/app/assets/javascripts/shadcn/controllers/calendar_controller.js +592 -0
  41. data/app/assets/javascripts/shadcn/controllers/carousel_controller.js +263 -0
  42. data/app/assets/javascripts/shadcn/controllers/checkbox_controller.d.ts +31 -0
  43. data/app/assets/javascripts/shadcn/controllers/checkbox_controller.js +48 -0
  44. data/app/assets/javascripts/shadcn/controllers/collapsible_controller.d.ts +43 -0
  45. data/app/assets/javascripts/shadcn/controllers/collapsible_controller.js +73 -0
  46. data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +234 -0
  47. data/app/assets/javascripts/shadcn/controllers/command_controller.js +141 -0
  48. data/app/assets/javascripts/shadcn/controllers/command_dialog_controller.js +162 -0
  49. data/app/assets/javascripts/shadcn/controllers/context_menu_controller.js +202 -0
  50. data/app/assets/javascripts/shadcn/controllers/date_picker_controller.js +282 -0
  51. data/app/assets/javascripts/shadcn/controllers/dialog_controller.d.ts +67 -0
  52. data/app/assets/javascripts/shadcn/controllers/dialog_controller.js +187 -0
  53. data/app/assets/javascripts/shadcn/controllers/drawer_controller.d.ts +58 -0
  54. data/app/assets/javascripts/shadcn/controllers/drawer_controller.js +112 -0
  55. data/app/assets/javascripts/shadcn/controllers/dropdown_controller.d.ts +83 -0
  56. data/app/assets/javascripts/shadcn/controllers/dropdown_controller.js +225 -0
  57. data/app/assets/javascripts/shadcn/controllers/hover_card_controller.d.ts +59 -0
  58. data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +143 -0
  59. data/app/assets/javascripts/shadcn/controllers/input_otp_controller.d.ts +44 -0
  60. data/app/assets/javascripts/shadcn/controllers/input_otp_controller.js +206 -0
  61. data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +323 -0
  62. data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +251 -0
  63. data/app/assets/javascripts/shadcn/controllers/popover_controller.d.ts +56 -0
  64. data/app/assets/javascripts/shadcn/controllers/popover_controller.js +141 -0
  65. data/app/assets/javascripts/shadcn/controllers/radio_group_controller.d.ts +47 -0
  66. data/app/assets/javascripts/shadcn/controllers/radio_group_controller.js +108 -0
  67. data/app/assets/javascripts/shadcn/controllers/resizable_controller.js +272 -0
  68. data/app/assets/javascripts/shadcn/controllers/scroll_area_controller.d.ts +44 -0
  69. data/app/assets/javascripts/shadcn/controllers/scroll_area_controller.js +74 -0
  70. data/app/assets/javascripts/shadcn/controllers/select_controller.d.ts +84 -0
  71. data/app/assets/javascripts/shadcn/controllers/select_controller.js +222 -0
  72. data/app/assets/javascripts/shadcn/controllers/sheet_controller.d.ts +60 -0
  73. data/app/assets/javascripts/shadcn/controllers/sheet_controller.js +151 -0
  74. data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +148 -0
  75. data/app/assets/javascripts/shadcn/controllers/slider_controller.d.ts +102 -0
  76. data/app/assets/javascripts/shadcn/controllers/slider_controller.js +364 -0
  77. data/app/assets/javascripts/shadcn/controllers/switch_controller.d.ts +46 -0
  78. data/app/assets/javascripts/shadcn/controllers/switch_controller.js +78 -0
  79. data/app/assets/javascripts/shadcn/controllers/tabs_controller.d.ts +51 -0
  80. data/app/assets/javascripts/shadcn/controllers/tabs_controller.js +126 -0
  81. data/app/assets/javascripts/shadcn/controllers/toast_controller.d.ts +37 -0
  82. data/app/assets/javascripts/shadcn/controllers/toast_controller.js +58 -0
  83. data/app/assets/javascripts/shadcn/controllers/toggle_controller.d.ts +27 -0
  84. data/app/assets/javascripts/shadcn/controllers/toggle_controller.js +42 -0
  85. data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.d.ts +44 -0
  86. data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.js +68 -0
  87. data/app/assets/javascripts/shadcn/controllers/tooltip_controller.d.ts +56 -0
  88. data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +117 -0
  89. data/app/assets/javascripts/shadcn/index.d.ts +74 -0
  90. data/app/assets/javascripts/shadcn/index.js +133 -0
  91. data/app/assets/stylesheets/.keep +0 -0
  92. data/app/assets/stylesheets/shadcn/base.css +445 -0
  93. data/app/assets/stylesheets/shadcn/components.css +513 -0
  94. data/app/assets/stylesheets/shadcn/index.css +18 -0
  95. data/app/assets/stylesheets/shadcn/themes/gray.css +68 -0
  96. data/app/assets/stylesheets/shadcn/themes/slate.css +68 -0
  97. data/app/assets/stylesheets/shadcn/themes/stone.css +68 -0
  98. data/app/assets/stylesheets/shadcn/themes/zinc.css +68 -0
  99. data/app/components/shadcn/accordion_component.rb +63 -0
  100. data/app/components/shadcn/accordion_content_component.rb +29 -0
  101. data/app/components/shadcn/accordion_item_component.rb +40 -0
  102. data/app/components/shadcn/accordion_trigger_component.rb +49 -0
  103. data/app/components/shadcn/alert_component.rb +75 -0
  104. data/app/components/shadcn/alert_description_component.rb +12 -0
  105. data/app/components/shadcn/alert_dialog_action_component.rb +24 -0
  106. data/app/components/shadcn/alert_dialog_cancel_component.rb +24 -0
  107. data/app/components/shadcn/alert_dialog_component.rb +71 -0
  108. data/app/components/shadcn/alert_dialog_content_component.rb +57 -0
  109. data/app/components/shadcn/alert_dialog_description_component.rb +12 -0
  110. data/app/components/shadcn/alert_dialog_footer_component.rb +19 -0
  111. data/app/components/shadcn/alert_dialog_header_component.rb +19 -0
  112. data/app/components/shadcn/alert_dialog_title_component.rb +12 -0
  113. data/app/components/shadcn/alert_title_component.rb +12 -0
  114. data/app/components/shadcn/aspect_ratio_component.rb +49 -0
  115. data/app/components/shadcn/avatar_component.rb +107 -0
  116. data/app/components/shadcn/avatar_fallback_component.rb +17 -0
  117. data/app/components/shadcn/badge_component.rb +49 -0
  118. data/app/components/shadcn/base_component.rb +100 -0
  119. data/app/components/shadcn/breadcrumb_component.rb +70 -0
  120. data/app/components/shadcn/breadcrumb_item_component.rb +50 -0
  121. data/app/components/shadcn/button_component.rb +141 -0
  122. data/app/components/shadcn/button_group_component.rb +69 -0
  123. data/app/components/shadcn/calendar_component.rb +337 -0
  124. data/app/components/shadcn/card_action_component.rb +10 -0
  125. data/app/components/shadcn/card_component.rb +63 -0
  126. data/app/components/shadcn/card_content_component.rb +19 -0
  127. data/app/components/shadcn/card_description_component.rb +12 -0
  128. data/app/components/shadcn/card_footer_component.rb +12 -0
  129. data/app/components/shadcn/card_header_component.rb +24 -0
  130. data/app/components/shadcn/card_title_component.rb +18 -0
  131. data/app/components/shadcn/carousel_component.rb +275 -0
  132. data/app/components/shadcn/checkbox_component.rb +103 -0
  133. data/app/components/shadcn/collapsible_component.rb +66 -0
  134. data/app/components/shadcn/collapsible_content_component.rb +28 -0
  135. data/app/components/shadcn/combobox_component.rb +322 -0
  136. data/app/components/shadcn/command_component.rb +52 -0
  137. data/app/components/shadcn/command_dialog_component.rb +76 -0
  138. data/app/components/shadcn/command_empty_component.rb +12 -0
  139. data/app/components/shadcn/command_group_component.rb +34 -0
  140. data/app/components/shadcn/command_input_component.rb +59 -0
  141. data/app/components/shadcn/command_item_component.rb +48 -0
  142. data/app/components/shadcn/command_list_component.rb +38 -0
  143. data/app/components/shadcn/command_separator_component.rb +12 -0
  144. data/app/components/shadcn/command_shortcut_component.rb +12 -0
  145. data/app/components/shadcn/context_menu_component.rb +64 -0
  146. data/app/components/shadcn/context_menu_content_component.rb +44 -0
  147. data/app/components/shadcn/context_menu_item_component.rb +63 -0
  148. data/app/components/shadcn/context_menu_label_component.rb +18 -0
  149. data/app/components/shadcn/context_menu_separator_component.rb +12 -0
  150. data/app/components/shadcn/context_menu_shortcut_component.rb +12 -0
  151. data/app/components/shadcn/date_picker_component.rb +368 -0
  152. data/app/components/shadcn/dialog_component.rb +77 -0
  153. data/app/components/shadcn/dialog_content_component.rb +91 -0
  154. data/app/components/shadcn/dialog_description_component.rb +12 -0
  155. data/app/components/shadcn/dialog_footer_component.rb +12 -0
  156. data/app/components/shadcn/dialog_header_component.rb +19 -0
  157. data/app/components/shadcn/dialog_title_component.rb +12 -0
  158. data/app/components/shadcn/drawer_component.rb +72 -0
  159. data/app/components/shadcn/drawer_content_component.rb +76 -0
  160. data/app/components/shadcn/drawer_description_component.rb +12 -0
  161. data/app/components/shadcn/drawer_footer_component.rb +12 -0
  162. data/app/components/shadcn/drawer_header_component.rb +19 -0
  163. data/app/components/shadcn/drawer_title_component.rb +12 -0
  164. data/app/components/shadcn/dropdown_menu_component.rb +75 -0
  165. data/app/components/shadcn/dropdown_menu_content_component.rb +49 -0
  166. data/app/components/shadcn/dropdown_menu_group_component.rb +10 -0
  167. data/app/components/shadcn/dropdown_menu_item_component.rb +63 -0
  168. data/app/components/shadcn/dropdown_menu_label_component.rb +18 -0
  169. data/app/components/shadcn/dropdown_menu_separator_component.rb +12 -0
  170. data/app/components/shadcn/dropdown_menu_shortcut_component.rb +12 -0
  171. data/app/components/shadcn/empty_component.rb +48 -0
  172. data/app/components/shadcn/empty_content_component.rb +12 -0
  173. data/app/components/shadcn/empty_description_component.rb +12 -0
  174. data/app/components/shadcn/empty_header_component.rb +29 -0
  175. data/app/components/shadcn/empty_media_component.rb +21 -0
  176. data/app/components/shadcn/empty_title_component.rb +12 -0
  177. data/app/components/shadcn/field_component.rb +113 -0
  178. data/app/components/shadcn/hover_card_component.rb +64 -0
  179. data/app/components/shadcn/hover_card_content_component.rb +36 -0
  180. data/app/components/shadcn/input_component.rb +108 -0
  181. data/app/components/shadcn/input_group_component.rb +70 -0
  182. data/app/components/shadcn/input_otp_component.rb +183 -0
  183. data/app/components/shadcn/item_actions_component.rb +12 -0
  184. data/app/components/shadcn/item_component.rb +98 -0
  185. data/app/components/shadcn/item_content_component.rb +24 -0
  186. data/app/components/shadcn/item_description_component.rb +12 -0
  187. data/app/components/shadcn/item_footer_component.rb +12 -0
  188. data/app/components/shadcn/item_group_component.rb +24 -0
  189. data/app/components/shadcn/item_header_component.rb +12 -0
  190. data/app/components/shadcn/item_media_component.rb +22 -0
  191. data/app/components/shadcn/item_separator_component.rb +12 -0
  192. data/app/components/shadcn/item_title_component.rb +12 -0
  193. data/app/components/shadcn/kbd_component.rb +36 -0
  194. data/app/components/shadcn/label_component.rb +49 -0
  195. data/app/components/shadcn/menubar_checkbox_item_component.rb +76 -0
  196. data/app/components/shadcn/menubar_component.rb +56 -0
  197. data/app/components/shadcn/menubar_content_component.rb +64 -0
  198. data/app/components/shadcn/menubar_item_component.rb +65 -0
  199. data/app/components/shadcn/menubar_label_component.rb +27 -0
  200. data/app/components/shadcn/menubar_menu_component.rb +34 -0
  201. data/app/components/shadcn/menubar_radio_group_component.rb +42 -0
  202. data/app/components/shadcn/menubar_radio_item_component.rb +76 -0
  203. data/app/components/shadcn/menubar_separator_component.rb +22 -0
  204. data/app/components/shadcn/menubar_shortcut_component.rb +21 -0
  205. data/app/components/shadcn/menubar_sub_component.rb +38 -0
  206. data/app/components/shadcn/menubar_sub_content_component.rb +45 -0
  207. data/app/components/shadcn/menubar_sub_trigger_component.rb +59 -0
  208. data/app/components/shadcn/menubar_trigger_component.rb +31 -0
  209. data/app/components/shadcn/native_select_component.rb +150 -0
  210. data/app/components/shadcn/navigation_menu_component.rb +76 -0
  211. data/app/components/shadcn/navigation_menu_content_component.rb +30 -0
  212. data/app/components/shadcn/navigation_menu_item_component.rb +39 -0
  213. data/app/components/shadcn/navigation_menu_link_component.rb +38 -0
  214. data/app/components/shadcn/navigation_menu_list_component.rb +29 -0
  215. data/app/components/shadcn/navigation_menu_trigger_component.rb +59 -0
  216. data/app/components/shadcn/pagination_component.rb +195 -0
  217. data/app/components/shadcn/pagination_content_component.rb +47 -0
  218. data/app/components/shadcn/pagination_ellipsis_component.rb +30 -0
  219. data/app/components/shadcn/pagination_item_component.rb +53 -0
  220. data/app/components/shadcn/pagination_next_component.rb +48 -0
  221. data/app/components/shadcn/pagination_previous_component.rb +48 -0
  222. data/app/components/shadcn/popover_component.rb +76 -0
  223. data/app/components/shadcn/popover_content_component.rb +25 -0
  224. data/app/components/shadcn/progress_component.rb +77 -0
  225. data/app/components/shadcn/radio_group_component.rb +129 -0
  226. data/app/components/shadcn/radio_group_item_component.rb +109 -0
  227. data/app/components/shadcn/resizable_handle_component.rb +98 -0
  228. data/app/components/shadcn/resizable_panel_component.rb +56 -0
  229. data/app/components/shadcn/resizable_panel_group_component.rb +94 -0
  230. data/app/components/shadcn/scroll_area_component.rb +110 -0
  231. data/app/components/shadcn/select_component.rb +151 -0
  232. data/app/components/shadcn/select_group_component.rb +32 -0
  233. data/app/components/shadcn/select_item_component.rb +59 -0
  234. data/app/components/shadcn/select_separator_component.rb +12 -0
  235. data/app/components/shadcn/separator_component.rb +54 -0
  236. data/app/components/shadcn/sheet_component.rb +82 -0
  237. data/app/components/shadcn/sheet_content_component.rb +95 -0
  238. data/app/components/shadcn/sheet_description_component.rb +12 -0
  239. data/app/components/shadcn/sheet_footer_component.rb +12 -0
  240. data/app/components/shadcn/sheet_header_component.rb +19 -0
  241. data/app/components/shadcn/sheet_title_component.rb +12 -0
  242. data/app/components/shadcn/sidebar_component.rb +180 -0
  243. data/app/components/shadcn/sidebar_content_component.rb +32 -0
  244. data/app/components/shadcn/sidebar_footer_component.rb +24 -0
  245. data/app/components/shadcn/sidebar_group_action_component.rb +26 -0
  246. data/app/components/shadcn/sidebar_group_component.rb +38 -0
  247. data/app/components/shadcn/sidebar_group_content_component.rb +32 -0
  248. data/app/components/shadcn/sidebar_group_label_component.rb +25 -0
  249. data/app/components/shadcn/sidebar_header_component.rb +24 -0
  250. data/app/components/shadcn/sidebar_inset_component.rb +25 -0
  251. data/app/components/shadcn/sidebar_menu_action_component.rb +37 -0
  252. data/app/components/shadcn/sidebar_menu_badge_component.rb +25 -0
  253. data/app/components/shadcn/sidebar_menu_button_component.rb +52 -0
  254. data/app/components/shadcn/sidebar_menu_component.rb +32 -0
  255. data/app/components/shadcn/sidebar_menu_item_component.rb +41 -0
  256. data/app/components/shadcn/sidebar_menu_skeleton_component.rb +46 -0
  257. data/app/components/shadcn/sidebar_menu_sub_button_component.rb +43 -0
  258. data/app/components/shadcn/sidebar_menu_sub_component.rb +33 -0
  259. data/app/components/shadcn/sidebar_menu_sub_item_component.rb +30 -0
  260. data/app/components/shadcn/sidebar_provider_component.rb +57 -0
  261. data/app/components/shadcn/sidebar_rail_component.rb +30 -0
  262. data/app/components/shadcn/sidebar_separator_component.rb +24 -0
  263. data/app/components/shadcn/sidebar_trigger_component.rb +51 -0
  264. data/app/components/shadcn/skeleton_component.rb +29 -0
  265. data/app/components/shadcn/slider_component.rb +76 -0
  266. data/app/components/shadcn/spinner_component.rb +67 -0
  267. data/app/components/shadcn/switch_component.rb +147 -0
  268. data/app/components/shadcn/table_body_component.rb +16 -0
  269. data/app/components/shadcn/table_caption_component.rb +12 -0
  270. data/app/components/shadcn/table_cell_component.rb +12 -0
  271. data/app/components/shadcn/table_component.rb +57 -0
  272. data/app/components/shadcn/table_footer_component.rb +16 -0
  273. data/app/components/shadcn/table_head_component.rb +12 -0
  274. data/app/components/shadcn/table_header_component.rb +16 -0
  275. data/app/components/shadcn/table_row_component.rb +40 -0
  276. data/app/components/shadcn/tabs_component.rb +78 -0
  277. data/app/components/shadcn/tabs_content_component.rb +32 -0
  278. data/app/components/shadcn/tabs_list_component.rb +30 -0
  279. data/app/components/shadcn/tabs_trigger_component.rb +37 -0
  280. data/app/components/shadcn/textarea_component.rb +84 -0
  281. data/app/components/shadcn/toast_action_component.rb +18 -0
  282. data/app/components/shadcn/toast_component.rb +114 -0
  283. data/app/components/shadcn/toast_description_component.rb +12 -0
  284. data/app/components/shadcn/toast_title_component.rb +12 -0
  285. data/app/components/shadcn/toast_viewport_component.rb +12 -0
  286. data/app/components/shadcn/toggle_component.rb +77 -0
  287. data/app/components/shadcn/toggle_group_component.rb +96 -0
  288. data/app/components/shadcn/toggle_group_item_component.rb +62 -0
  289. data/app/components/shadcn/tooltip_component.rb +89 -0
  290. data/app/components/shadcn/typography_component.rb +112 -0
  291. data/babel.config.cjs +5 -0
  292. data/bin/console +11 -0
  293. data/bin/setup +8 -0
  294. data/config/importmap.rb +5 -0
  295. data/fly.toml +26 -0
  296. data/jest.config.js +19 -0
  297. data/jest.setup.js +8 -0
  298. data/lib/generators/shadcn/component/component_generator.rb +188 -0
  299. data/lib/generators/shadcn/install/install_generator.rb +140 -0
  300. data/lib/generators/shadcn/install/templates/initializer.rb.tt +35 -0
  301. data/lib/generators/shadcn/install/templates/shadcn.yml.tt +35 -0
  302. data/lib/generators/shadcn/theme/theme_generator.rb +128 -0
  303. data/lib/shadcn/rails/class_merger.rb +228 -0
  304. data/lib/shadcn/rails/configuration.rb +341 -0
  305. data/lib/shadcn/rails/engine.rb +59 -0
  306. data/lib/shadcn/rails/helpers/class_name_helper.rb +35 -0
  307. data/lib/shadcn/rails/helpers/component_helper.rb +60 -0
  308. data/lib/shadcn/rails/helpers/pagination_helper.rb +187 -0
  309. data/lib/shadcn/rails/version.rb +7 -0
  310. data/lib/shadcn/rails.rb +179 -0
  311. data/package-lock.json +7415 -0
  312. data/package.json +68 -0
  313. data/rollup.config.js +29 -0
  314. data/sig/shadcn/rails.rbs +6 -0
  315. metadata +526 -0
@@ -0,0 +1,407 @@
1
+ import { Application } from "@hotwired/stimulus"
2
+ import CollapsibleController from "../../app/assets/javascripts/shadcn/controllers/collapsible_controller.js"
3
+ import { setupController, cleanupController, click, nextFrame, wait } from '../helpers/stimulus-test-helper.js'
4
+
5
+ describe("CollapsibleController", () => {
6
+ let application
7
+ let element
8
+ let controller
9
+
10
+ afterEach(() => {
11
+ cleanupController(application)
12
+ })
13
+
14
+ describe("basic rendering and initialization", () => {
15
+ const basicHTML = `
16
+ <div data-controller="shadcn--collapsible"
17
+ data-shadcn--collapsible-open-value="false"
18
+ data-shadcn--collapsible-disabled-value="false">
19
+ <button data-shadcn--collapsible-target="trigger"
20
+ data-action="click->shadcn--collapsible#toggle">
21
+ Toggle
22
+ </button>
23
+ <div data-shadcn--collapsible-target="content" hidden>
24
+ <p>Collapsible content</p>
25
+ </div>
26
+ </div>
27
+ `
28
+
29
+ beforeEach(async () => {
30
+ const setup = await setupController(CollapsibleController, basicHTML, 'shadcn--collapsible')
31
+ application = setup.application
32
+ element = setup.element
33
+ controller = setup.controller
34
+ })
35
+
36
+ test("initializes with closed state", () => {
37
+ expect(controller.openValue).toBe(false)
38
+ })
39
+
40
+ test("initializes with disabled false", () => {
41
+ expect(controller.disabledValue).toBe(false)
42
+ })
43
+
44
+ test("sets data-state closed on element", () => {
45
+ expect(element.dataset.state).toBe("closed")
46
+ })
47
+
48
+ test("sets data-state closed on content", () => {
49
+ expect(controller.contentTarget.dataset.state).toBe("closed")
50
+ })
51
+
52
+ test("content is hidden initially", () => {
53
+ expect(controller.contentTarget.hidden).toBe(true)
54
+ })
55
+ })
56
+
57
+ describe("toggle functionality", () => {
58
+ const toggleHTML = `
59
+ <div data-controller="shadcn--collapsible"
60
+ data-shadcn--collapsible-open-value="false">
61
+ <button data-shadcn--collapsible-target="trigger"
62
+ data-action="click->shadcn--collapsible#toggle">
63
+ Toggle
64
+ </button>
65
+ <div data-shadcn--collapsible-target="content" hidden>
66
+ Content
67
+ </div>
68
+ </div>
69
+ `
70
+
71
+ beforeEach(async () => {
72
+ const setup = await setupController(CollapsibleController, toggleHTML, 'shadcn--collapsible')
73
+ application = setup.application
74
+ element = setup.element
75
+ controller = setup.controller
76
+ })
77
+
78
+ test("toggles open state when closed", async () => {
79
+ controller.toggle()
80
+ await nextFrame()
81
+
82
+ expect(controller.openValue).toBe(true)
83
+ })
84
+
85
+ test("toggles closed state when open", async () => {
86
+ controller.openValue = true
87
+ controller.toggle()
88
+ await nextFrame()
89
+
90
+ expect(controller.openValue).toBe(false)
91
+ })
92
+
93
+ test("updates element data-state on toggle", async () => {
94
+ controller.toggle()
95
+ await nextFrame()
96
+
97
+ expect(element.dataset.state).toBe("open")
98
+ })
99
+
100
+ test("updates content data-state on toggle", async () => {
101
+ controller.toggle()
102
+ await nextFrame()
103
+
104
+ expect(controller.contentTarget.dataset.state).toBe("open")
105
+ })
106
+
107
+ test("shows content when toggled open", async () => {
108
+ controller.toggle()
109
+ await nextFrame()
110
+
111
+ expect(controller.contentTarget.hidden).toBe(false)
112
+ })
113
+
114
+ test("dispatches opened event when opening", async () => {
115
+ let eventFired = false
116
+ element.addEventListener("shadcn--collapsible:opened", () => {
117
+ eventFired = true
118
+ })
119
+
120
+ controller.toggle()
121
+ await nextFrame()
122
+
123
+ expect(eventFired).toBe(true)
124
+ })
125
+
126
+ test("dispatches closed event when closing", async () => {
127
+ controller.openValue = true
128
+ controller.updateState()
129
+
130
+ let eventFired = false
131
+ element.addEventListener("shadcn--collapsible:closed", () => {
132
+ eventFired = true
133
+ })
134
+
135
+ controller.toggle()
136
+ await nextFrame()
137
+
138
+ expect(eventFired).toBe(true)
139
+ })
140
+ })
141
+
142
+ describe("open and close methods", () => {
143
+ const methodsHTML = `
144
+ <div data-controller="shadcn--collapsible"
145
+ data-shadcn--collapsible-open-value="false">
146
+ <button data-shadcn--collapsible-target="trigger">Toggle</button>
147
+ <div data-shadcn--collapsible-target="content" hidden>Content</div>
148
+ </div>
149
+ `
150
+
151
+ beforeEach(async () => {
152
+ const setup = await setupController(CollapsibleController, methodsHTML, 'shadcn--collapsible')
153
+ application = setup.application
154
+ element = setup.element
155
+ controller = setup.controller
156
+ })
157
+
158
+ test("open() sets openValue to true", async () => {
159
+ controller.open()
160
+ await nextFrame()
161
+
162
+ expect(controller.openValue).toBe(true)
163
+ })
164
+
165
+ test("open() updates state", async () => {
166
+ controller.open()
167
+ await nextFrame()
168
+
169
+ expect(element.dataset.state).toBe("open")
170
+ })
171
+
172
+ test("close() sets openValue to false", async () => {
173
+ controller.openValue = true
174
+ controller.close()
175
+ await nextFrame()
176
+
177
+ expect(controller.openValue).toBe(false)
178
+ })
179
+
180
+ test("close() updates state", async () => {
181
+ controller.openValue = true
182
+ controller.updateState()
183
+ controller.close()
184
+ await nextFrame()
185
+
186
+ expect(element.dataset.state).toBe("closed")
187
+ })
188
+ })
189
+
190
+ describe("disabled state", () => {
191
+ const disabledHTML = `
192
+ <div data-controller="shadcn--collapsible"
193
+ data-shadcn--collapsible-open-value="false"
194
+ data-shadcn--collapsible-disabled-value="true">
195
+ <button data-shadcn--collapsible-target="trigger"
196
+ data-action="click->shadcn--collapsible#toggle">Toggle</button>
197
+ <div data-shadcn--collapsible-target="content" hidden>Content</div>
198
+ </div>
199
+ `
200
+
201
+ beforeEach(async () => {
202
+ const setup = await setupController(CollapsibleController, disabledHTML, 'shadcn--collapsible')
203
+ application = setup.application
204
+ element = setup.element
205
+ controller = setup.controller
206
+ })
207
+
208
+ test("toggle does not change state when disabled", async () => {
209
+ controller.toggle()
210
+ await nextFrame()
211
+
212
+ expect(controller.openValue).toBe(false)
213
+ })
214
+
215
+ test("open does not change state when disabled", async () => {
216
+ controller.open()
217
+ await nextFrame()
218
+
219
+ expect(controller.openValue).toBe(false)
220
+ })
221
+
222
+ test("close still works when disabled", async () => {
223
+ // Force open state
224
+ controller.openValue = true
225
+ controller.disabledValue = true
226
+
227
+ controller.close()
228
+ await nextFrame()
229
+
230
+ expect(controller.openValue).toBe(false)
231
+ })
232
+ })
233
+
234
+ describe("initial open state", () => {
235
+ const openHTML = `
236
+ <div data-controller="shadcn--collapsible"
237
+ data-shadcn--collapsible-open-value="true">
238
+ <button data-shadcn--collapsible-target="trigger">Toggle</button>
239
+ <div data-shadcn--collapsible-target="content">Content</div>
240
+ </div>
241
+ `
242
+
243
+ beforeEach(async () => {
244
+ const setup = await setupController(CollapsibleController, openHTML, 'shadcn--collapsible')
245
+ application = setup.application
246
+ element = setup.element
247
+ controller = setup.controller
248
+ })
249
+
250
+ test("initializes with open state", () => {
251
+ expect(controller.openValue).toBe(true)
252
+ })
253
+
254
+ test("sets data-state open on element", () => {
255
+ expect(element.dataset.state).toBe("open")
256
+ })
257
+
258
+ test("content is visible initially", () => {
259
+ expect(controller.contentTarget.hidden).toBe(false)
260
+ })
261
+ })
262
+
263
+ describe("programmatic value change", () => {
264
+ const programmaticHTML = `
265
+ <div data-controller="shadcn--collapsible"
266
+ data-shadcn--collapsible-open-value="false">
267
+ <button data-shadcn--collapsible-target="trigger">Toggle</button>
268
+ <div data-shadcn--collapsible-target="content" hidden>Content</div>
269
+ </div>
270
+ `
271
+
272
+ beforeEach(async () => {
273
+ const setup = await setupController(CollapsibleController, programmaticHTML, 'shadcn--collapsible')
274
+ application = setup.application
275
+ element = setup.element
276
+ controller = setup.controller
277
+ })
278
+
279
+ test("openValueChanged callback updates state", async () => {
280
+ controller.openValue = true
281
+ await nextFrame()
282
+
283
+ expect(element.dataset.state).toBe("open")
284
+ })
285
+
286
+ test("changing openValue shows content", async () => {
287
+ controller.openValue = true
288
+ await nextFrame()
289
+
290
+ expect(controller.contentTarget.hidden).toBe(false)
291
+ })
292
+ })
293
+
294
+ describe("click handler", () => {
295
+ const clickHTML = `
296
+ <div data-controller="shadcn--collapsible"
297
+ data-shadcn--collapsible-open-value="false">
298
+ <button data-shadcn--collapsible-target="trigger"
299
+ data-action="click->shadcn--collapsible#toggle">Toggle</button>
300
+ <div data-shadcn--collapsible-target="content" hidden>Content</div>
301
+ </div>
302
+ `
303
+
304
+ beforeEach(async () => {
305
+ const setup = await setupController(CollapsibleController, clickHTML, 'shadcn--collapsible')
306
+ application = setup.application
307
+ element = setup.element
308
+ controller = setup.controller
309
+ })
310
+
311
+ test("toggles on trigger click", async () => {
312
+ click(controller.triggerTarget)
313
+ await nextFrame()
314
+
315
+ expect(controller.openValue).toBe(true)
316
+ })
317
+
318
+ test("toggles back on second click", async () => {
319
+ click(controller.triggerTarget)
320
+ await nextFrame()
321
+ click(controller.triggerTarget)
322
+ await nextFrame()
323
+
324
+ expect(controller.openValue).toBe(false)
325
+ })
326
+ })
327
+
328
+ describe("without content target", () => {
329
+ const noContentHTML = `
330
+ <div data-controller="shadcn--collapsible"
331
+ data-shadcn--collapsible-open-value="false">
332
+ <button data-shadcn--collapsible-target="trigger"
333
+ data-action="click->shadcn--collapsible#toggle">Toggle</button>
334
+ </div>
335
+ `
336
+
337
+ beforeEach(async () => {
338
+ const setup = await setupController(CollapsibleController, noContentHTML, 'shadcn--collapsible')
339
+ application = setup.application
340
+ element = setup.element
341
+ controller = setup.controller
342
+ })
343
+
344
+ test("works without content target", async () => {
345
+ expect(() => {
346
+ controller.toggle()
347
+ }).not.toThrow()
348
+
349
+ expect(controller.openValue).toBe(true)
350
+ })
351
+
352
+ test("updates element state without content", async () => {
353
+ controller.toggle()
354
+ await nextFrame()
355
+
356
+ expect(element.dataset.state).toBe("open")
357
+ })
358
+ })
359
+
360
+ describe("multiple toggles", () => {
361
+ const multipleHTML = `
362
+ <div data-controller="shadcn--collapsible"
363
+ data-shadcn--collapsible-open-value="false">
364
+ <button data-shadcn--collapsible-target="trigger"
365
+ data-action="click->shadcn--collapsible#toggle">Toggle</button>
366
+ <div data-shadcn--collapsible-target="content" hidden>Content</div>
367
+ </div>
368
+ `
369
+
370
+ beforeEach(async () => {
371
+ const setup = await setupController(CollapsibleController, multipleHTML, 'shadcn--collapsible')
372
+ application = setup.application
373
+ element = setup.element
374
+ controller = setup.controller
375
+ })
376
+
377
+ test("handles rapid toggles", async () => {
378
+ controller.toggle() // open
379
+ controller.toggle() // close
380
+ controller.toggle() // open
381
+ await nextFrame()
382
+
383
+ expect(controller.openValue).toBe(true)
384
+ })
385
+
386
+ test("dispatches events for each toggle", async () => {
387
+ let openedCount = 0
388
+ let closedCount = 0
389
+
390
+ element.addEventListener("shadcn--collapsible:opened", () => openedCount++)
391
+ element.addEventListener("shadcn--collapsible:closed", () => closedCount++)
392
+
393
+ // Note: The controller dispatches events from both toggle() and openValueChanged()
394
+ // so each toggle fires two events. This is the current behavior.
395
+ controller.toggle() // open
396
+ await nextFrame()
397
+ controller.toggle() // close
398
+ await nextFrame()
399
+ controller.toggle() // open
400
+ await nextFrame()
401
+
402
+ // Events fire twice per toggle (from toggle() and openValueChanged())
403
+ expect(openedCount).toBe(4) // 2 opens * 2 events each
404
+ expect(closedCount).toBe(2) // 1 close * 2 events
405
+ })
406
+ })
407
+ })