adminterface 0.0.0 → 0.2.3

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 (271) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +1 -1
  3. data/README.md +69 -0
  4. data/Rakefile +43 -0
  5. data/app/assets/config/adminterface_manifest.js +1 -0
  6. data/app/assets/stylesheets/adminterface/_base.scss +20 -0
  7. data/app/assets/stylesheets/adminterface/_print.scss +56 -0
  8. data/app/assets/stylesheets/adminterface/components/_active_admin_comment.scss +19 -0
  9. data/app/assets/stylesheets/adminterface/components/_all.scss +24 -0
  10. data/app/assets/stylesheets/adminterface/components/_aside.scss +28 -0
  11. data/app/assets/stylesheets/adminterface/components/_attributes_table.scss +19 -0
  12. data/app/assets/stylesheets/adminterface/components/_comments.scss +7 -0
  13. data/app/assets/stylesheets/adminterface/components/_divider.scss +28 -0
  14. data/app/assets/stylesheets/adminterface/components/_filters.scss +27 -0
  15. data/app/assets/stylesheets/adminterface/components/_flash.scss +4 -0
  16. data/app/assets/stylesheets/adminterface/components/_footer.scss +4 -0
  17. data/app/assets/stylesheets/adminterface/components/_form.scss +219 -0
  18. data/app/assets/stylesheets/adminterface/components/_header.scss +30 -0
  19. data/app/assets/stylesheets/adminterface/components/_index.scss +1 -0
  20. data/app/assets/stylesheets/adminterface/components/_index_as_grid.scss +5 -0
  21. data/app/assets/stylesheets/adminterface/components/_index_as_table.scss +1 -0
  22. data/app/assets/stylesheets/adminterface/components/_index_footer.scss +19 -0
  23. data/app/assets/stylesheets/adminterface/components/_modal_dialog.scss +4 -0
  24. data/app/assets/stylesheets/adminterface/components/_pagination.scss +3 -0
  25. data/app/assets/stylesheets/adminterface/components/_panel.scss +21 -0
  26. data/app/assets/stylesheets/adminterface/components/_sidebar.scss +13 -0
  27. data/app/assets/stylesheets/adminterface/components/_site_title.scss +17 -0
  28. data/app/assets/stylesheets/adminterface/components/_table_for.scss +37 -0
  29. data/app/assets/stylesheets/adminterface/components/_table_tools.scss +19 -0
  30. data/app/assets/stylesheets/adminterface/components/_thumbnail.scss +29 -0
  31. data/app/assets/stylesheets/adminterface/components/_title_bar.scss +36 -0
  32. data/app/assets/stylesheets/adminterface/components/inputs/_all.scss +2 -0
  33. data/app/assets/stylesheets/adminterface/components/inputs/_password.scss +28 -0
  34. data/app/assets/stylesheets/adminterface/components/inputs/_text.scss +30 -0
  35. data/app/assets/stylesheets/adminterface/helpers/_aa_icon.scss +1 -0
  36. data/app/assets/stylesheets/adminterface/helpers/_all.scss +2 -0
  37. data/app/assets/stylesheets/adminterface/helpers/_pointer.scss +1 -0
  38. data/app/assets/stylesheets/adminterface/layouts/_all.scss +4 -0
  39. data/app/assets/stylesheets/adminterface/layouts/_base.scss +5 -0
  40. data/app/assets/stylesheets/adminterface/layouts/_logged_in.scss +30 -0
  41. data/app/assets/stylesheets/adminterface/layouts/_logged_out.scss +14 -0
  42. data/app/assets/stylesheets/adminterface/layouts/types/_all.scss +4 -0
  43. data/app/assets/stylesheets/adminterface/layouts/types/_mixins.scss +195 -0
  44. data/app/assets/stylesheets/adminterface/layouts/types/_navigation_aside.scss +33 -0
  45. data/app/assets/stylesheets/adminterface/layouts/types/_navigation_sidebar.scss +15 -0
  46. data/app/assets/stylesheets/adminterface/layouts/types/_navigation_top.scss +33 -0
  47. data/app/assets/stylesheets/adminterface/mixins/_all.scss +3 -0
  48. data/app/assets/stylesheets/adminterface/mixins/_layouts.scss +38 -0
  49. data/app/assets/stylesheets/adminterface/mixins/_utilities.scss +39 -0
  50. data/app/assets/stylesheets/adminterface/mixins/_variables.scss +100 -0
  51. data/app/assets/stylesheets/adminterface/placeholders/_aa_icon.scss +10 -0
  52. data/app/assets/stylesheets/adminterface/placeholders/_all.scss +3 -0
  53. data/app/assets/stylesheets/adminterface/placeholders/_background.scss +5 -0
  54. data/app/assets/stylesheets/adminterface/placeholders/_typography.scss +11 -0
  55. data/app/assets/stylesheets/adminterface/vendors/_all.scss +4 -0
  56. data/app/assets/stylesheets/adminterface/vendors/bootstrap/_all.scss +6 -0
  57. data/app/assets/stylesheets/adminterface/vendors/bootstrap/_variables.scss +51 -0
  58. data/app/assets/stylesheets/adminterface/vendors/bootstrap/components/_buttons.scss +5 -0
  59. data/app/assets/stylesheets/adminterface/vendors/bootstrap/components/_dropdowns.scss +28 -0
  60. data/app/assets/stylesheets/adminterface/vendors/bootstrap/components/_navs.scss +25 -0
  61. data/app/assets/stylesheets/adminterface/vendors/bootstrap/components/form/_labels.scss +5 -0
  62. data/app/assets/stylesheets/adminterface/vendors/bootstrap/content/_tables.scss +2 -0
  63. data/app/assets/stylesheets/adminterface/vendors/bootstrap/content/_typography.scss +2 -0
  64. data/app/assets/stylesheets/adminterface/vendors/flatpickr/_all.scss +2 -0
  65. data/app/assets/stylesheets/adminterface/vendors/flatpickr/_variables.scss +7 -0
  66. data/app/assets/stylesheets/adminterface/vendors/flatpickr/main.scss +50 -0
  67. data/app/assets/stylesheets/adminterface/vendors/tom_select/_all.scss +1 -0
  68. data/app/assets/stylesheets/adminterface/vendors/tom_select/_main.scss +6 -0
  69. data/app/assets/stylesheets/adminterface/vendors/trix/_actiontext.scss +36 -0
  70. data/app/assets/stylesheets/adminterface/vendors/trix/_all.scss +3 -0
  71. data/app/assets/stylesheets/adminterface/vendors/trix/_trix_editor.scss +86 -0
  72. data/app/assets/stylesheets/adminterface/vendors/trix/_variables.scss +31 -0
  73. data/app/javascript/adminterface/base.js +14 -0
  74. data/app/javascript/adminterface/config.js +129 -0
  75. data/app/javascript/adminterface/initializers/batch_actions.js +9 -0
  76. data/app/javascript/adminterface/initializers/bootstrap.js +29 -0
  77. data/app/javascript/adminterface/initializers/confirm_dialog.js +23 -0
  78. data/app/javascript/adminterface/initializers/detached_dropdown.js +16 -0
  79. data/app/javascript/adminterface/initializers/drawer_menu_item_toggler.js +15 -0
  80. data/app/javascript/adminterface/initializers/filters.js +23 -0
  81. data/app/javascript/adminterface/initializers/has_many.js +7 -0
  82. data/app/javascript/adminterface/initializers/header_toggler.js +10 -0
  83. data/app/javascript/adminterface/initializers/index.js +2 -0
  84. data/app/javascript/adminterface/initializers/per_page.js +12 -0
  85. data/app/javascript/adminterface/initializers/plugins/flatpickr.js +24 -0
  86. data/app/javascript/adminterface/initializers/plugins/input_counter.js +21 -0
  87. data/app/javascript/adminterface/initializers/plugins/password_visibility.js +23 -0
  88. data/app/javascript/adminterface/initializers/plugins/tom_select.js +24 -0
  89. data/app/javascript/adminterface/lib/__tests__/checkbox_toggler.html +5 -0
  90. data/app/javascript/adminterface/lib/__tests__/checkbox_toggler.spec.js +110 -0
  91. data/app/javascript/adminterface/lib/__tests__/detached_dropdown.spec.js +69 -0
  92. data/app/javascript/adminterface/lib/__tests__/filters.spec.js +150 -0
  93. data/app/javascript/adminterface/lib/__tests__/has_many.html +14 -0
  94. data/app/javascript/adminterface/lib/__tests__/has_many.spec.js +224 -0
  95. data/app/javascript/adminterface/lib/__tests__/utils.spec.js +280 -0
  96. data/app/javascript/adminterface/lib/batch_actions.js +106 -0
  97. data/app/javascript/adminterface/lib/checkbox_toggler.js +55 -0
  98. data/app/javascript/adminterface/lib/confirm_dialog.js +113 -0
  99. data/app/javascript/adminterface/lib/detached_dropdown.js +34 -0
  100. data/app/javascript/adminterface/lib/filters.js +34 -0
  101. data/app/javascript/adminterface/lib/has_many.js +125 -0
  102. data/app/javascript/adminterface/lib/header_toggler.js +54 -0
  103. data/app/javascript/adminterface/lib/input.js +108 -0
  104. data/app/javascript/adminterface/lib/inputs/base/groupish.js +55 -0
  105. data/app/javascript/adminterface/lib/inputs/base/pluginish.js +63 -0
  106. data/app/javascript/adminterface/lib/inputs/base_input.js +87 -0
  107. data/app/javascript/adminterface/lib/inputs/boolean_input.js +30 -0
  108. data/app/javascript/adminterface/lib/inputs/check_boxes_input.js +14 -0
  109. data/app/javascript/adminterface/lib/inputs/choices_input.js +96 -0
  110. data/app/javascript/adminterface/lib/inputs/color_input.js +14 -0
  111. data/app/javascript/adminterface/lib/inputs/country_input.js +90 -0
  112. data/app/javascript/adminterface/lib/inputs/datalist_input.js +41 -0
  113. data/app/javascript/adminterface/lib/inputs/date_picker_input.js +11 -0
  114. data/app/javascript/adminterface/lib/inputs/datetime_picker_input.js +11 -0
  115. data/app/javascript/adminterface/lib/inputs/hidden_input.js +21 -0
  116. data/app/javascript/adminterface/lib/inputs/password_input.js +13 -0
  117. data/app/javascript/adminterface/lib/inputs/radio_input.js +13 -0
  118. data/app/javascript/adminterface/lib/inputs/select_input.js +68 -0
  119. data/app/javascript/adminterface/lib/inputs/string_input.js +13 -0
  120. data/app/javascript/adminterface/lib/inputs/switch_input.js +9 -0
  121. data/app/javascript/adminterface/lib/inputs/text_input.js +12 -0
  122. data/app/javascript/adminterface/lib/inputs/time_picker_input.js +13 -0
  123. data/app/javascript/adminterface/lib/inputs/time_zone_input.js +66 -0
  124. data/app/javascript/adminterface/lib/per_page.js +18 -0
  125. data/app/javascript/adminterface/lib/plugins/input_counter.js +94 -0
  126. data/app/javascript/adminterface/lib/plugins/password_visibility.js +70 -0
  127. data/app/javascript/adminterface/lib/table_checkbox_toggler.js +47 -0
  128. data/app/javascript/adminterface/lib/utils.js +148 -0
  129. data/app/views/active_admin/devise/confirmations/new.html.erb +19 -0
  130. data/app/views/active_admin/devise/passwords/edit.html.erb +21 -0
  131. data/app/views/active_admin/devise/passwords/new.html.erb +19 -0
  132. data/app/views/active_admin/devise/registrations/new.html.erb +24 -0
  133. data/app/views/active_admin/devise/sessions/new.html.erb +23 -0
  134. data/app/views/active_admin/devise/shared/_links.html.erb +25 -0
  135. data/app/views/active_admin/devise/shared/_omniauth.html.erb +10 -0
  136. data/app/views/active_admin/devise/unlocks/new.html.erb +19 -0
  137. data/app/views/kaminari/active_admin/_first_page.html.erb +11 -0
  138. data/app/views/kaminari/active_admin/_gap.html.erb +12 -0
  139. data/app/views/kaminari/active_admin/_last_page.html.erb +11 -0
  140. data/app/views/kaminari/active_admin/_next_page.html.erb +11 -0
  141. data/app/views/kaminari/active_admin/_page.html.erb +12 -0
  142. data/app/views/kaminari/active_admin/_paginator.html.erb +27 -0
  143. data/app/views/kaminari/active_admin/_prev_page.html.erb +11 -0
  144. data/app/views/layouts/active_admin_logged_out.html.erb +56 -0
  145. data/config/locales/active_admin.en.yml +25 -0
  146. data/lib/adminterface/callable.rb +7 -0
  147. data/lib/adminterface/configs.rb +76 -0
  148. data/lib/adminterface/data/base.rb +11 -0
  149. data/lib/adminterface/data/countries.rb +44 -0
  150. data/lib/adminterface/data/time_zones.rb +31 -0
  151. data/lib/adminterface/data.rb +6 -0
  152. data/lib/adminterface/encryption/encryptor.rb +26 -0
  153. data/lib/adminterface/engine.rb +22 -0
  154. data/lib/adminterface/extensions/base_controller.rb +59 -0
  155. data/lib/adminterface/extensions/batch_actions/resource_extension.rb +64 -0
  156. data/lib/adminterface/extensions/batch_actions/views/batch_action_selector.rb +48 -0
  157. data/lib/adminterface/extensions/batch_actions/views/selection_cells.rb +39 -0
  158. data/lib/adminterface/extensions/dsl.rb +13 -0
  159. data/lib/adminterface/extensions/dynamic_settings_node.rb +26 -0
  160. data/lib/adminterface/extensions/filters/forms.rb +76 -0
  161. data/lib/adminterface/extensions/form_builder.rb +165 -0
  162. data/lib/adminterface/extensions/inputs/base/active_admin_config.rb +21 -0
  163. data/lib/adminterface/extensions/inputs/base/choices.rb +33 -0
  164. data/lib/adminterface/extensions/inputs/base/groupish.rb +50 -0
  165. data/lib/adminterface/extensions/inputs/base/html.rb +15 -0
  166. data/lib/adminterface/extensions/inputs/base/labelling.rb +15 -0
  167. data/lib/adminterface/extensions/inputs/base/pluginish.rb +56 -0
  168. data/lib/adminterface/extensions/inputs/base/stringish.rb +19 -0
  169. data/lib/adminterface/extensions/inputs/base/wrapping.rb +25 -0
  170. data/lib/adminterface/extensions/inputs/base.rb +19 -0
  171. data/lib/adminterface/extensions/inputs/boolean_input.rb +42 -0
  172. data/lib/adminterface/extensions/inputs/check_boxes_input.rb +34 -0
  173. data/lib/adminterface/extensions/inputs/color_input.rb +29 -0
  174. data/lib/adminterface/extensions/inputs/country_input.rb +33 -0
  175. data/lib/adminterface/extensions/inputs/datalist_input.rb +26 -0
  176. data/lib/adminterface/extensions/inputs/date_picker_input.rb +30 -0
  177. data/lib/adminterface/extensions/inputs/date_select_input.rb +7 -0
  178. data/lib/adminterface/extensions/inputs/datetime_picker_input.rb +19 -0
  179. data/lib/adminterface/extensions/inputs/datetime_select_input.rb +7 -0
  180. data/lib/adminterface/extensions/inputs/email_input.rb +23 -0
  181. data/lib/adminterface/extensions/inputs/file_input.rb +19 -0
  182. data/lib/adminterface/extensions/inputs/filters/base/search_method_select.rb +37 -0
  183. data/lib/adminterface/extensions/inputs/filters/base.rb +3 -0
  184. data/lib/adminterface/extensions/inputs/filters/boolean_input.rb +4 -0
  185. data/lib/adminterface/extensions/inputs/filters/check_boxes_input.rb +12 -0
  186. data/lib/adminterface/extensions/inputs/filters/date_picker_input.rb +4 -0
  187. data/lib/adminterface/extensions/inputs/filters/date_range_input.rb +50 -0
  188. data/lib/adminterface/extensions/inputs/filters/select_input.rb +4 -0
  189. data/lib/adminterface/extensions/inputs/number_input.rb +23 -0
  190. data/lib/adminterface/extensions/inputs/password_input.rb +23 -0
  191. data/lib/adminterface/extensions/inputs/phone_input.rb +23 -0
  192. data/lib/adminterface/extensions/inputs/radio_input.rb +41 -0
  193. data/lib/adminterface/extensions/inputs/range_input.rb +22 -0
  194. data/lib/adminterface/extensions/inputs/rich_text_area_input.rb +21 -0
  195. data/lib/adminterface/extensions/inputs/search_input.rb +23 -0
  196. data/lib/adminterface/extensions/inputs/select_input.rb +33 -0
  197. data/lib/adminterface/extensions/inputs/string_input.rb +19 -0
  198. data/lib/adminterface/extensions/inputs/switch_input.rb +11 -0
  199. data/lib/adminterface/extensions/inputs/text_input.rb +27 -0
  200. data/lib/adminterface/extensions/inputs/time_picker_input.rb +19 -0
  201. data/lib/adminterface/extensions/inputs/time_select_input.rb +7 -0
  202. data/lib/adminterface/extensions/inputs/time_zone_input.rb +33 -0
  203. data/lib/adminterface/extensions/inputs/url_input.rb +23 -0
  204. data/lib/adminterface/extensions/menu_item.rb +16 -0
  205. data/lib/adminterface/extensions/namespace_settings.rb +30 -0
  206. data/lib/adminterface/extensions/orm/active_record/comments/views/active_admin_comments.rb +122 -0
  207. data/lib/adminterface/extensions/resource/action_items.rb +117 -0
  208. data/lib/adminterface/extensions/sidebar_section.rb +35 -0
  209. data/lib/adminterface/extensions/view_helpers/alert_helper.rb +38 -0
  210. data/lib/adminterface/extensions/view_helpers/component_helper.rb +23 -0
  211. data/lib/adminterface/extensions/views/action_items.rb +39 -0
  212. data/lib/adminterface/extensions/views/columns.rb +59 -0
  213. data/lib/adminterface/extensions/views/components/active_admin_form.rb +47 -0
  214. data/lib/adminterface/extensions/views/components/attributes_table.rb +28 -0
  215. data/lib/adminterface/extensions/views/components/blank_slate.rb +19 -0
  216. data/lib/adminterface/extensions/views/components/dropdown_menu.rb +58 -0
  217. data/lib/adminterface/extensions/views/components/icon.rb +26 -0
  218. data/lib/adminterface/extensions/views/components/index_list.rb +43 -0
  219. data/lib/adminterface/extensions/views/components/menu.rb +18 -0
  220. data/lib/adminterface/extensions/views/components/menu_item.rb +69 -0
  221. data/lib/adminterface/extensions/views/components/paginated_collection.rb +52 -0
  222. data/lib/adminterface/extensions/views/components/panel.rb +71 -0
  223. data/lib/adminterface/extensions/views/components/scopes.rb +53 -0
  224. data/lib/adminterface/extensions/views/components/sidebar_section.rb +39 -0
  225. data/lib/adminterface/extensions/views/components/site_title.rb +36 -0
  226. data/lib/adminterface/extensions/views/components/status_tag.rb +26 -0
  227. data/lib/adminterface/extensions/views/components/table_for.rb +23 -0
  228. data/lib/adminterface/extensions/views/components/tabs.rb +101 -0
  229. data/lib/adminterface/extensions/views/footer.rb +27 -0
  230. data/lib/adminterface/extensions/views/header.rb +60 -0
  231. data/lib/adminterface/extensions/views/index_as_grid.rb +53 -0
  232. data/lib/adminterface/extensions/views/index_as_table.rb +77 -0
  233. data/lib/adminterface/extensions/views/pages/base.rb +165 -0
  234. data/lib/adminterface/extensions/views/pages/form.rb +27 -0
  235. data/lib/adminterface/extensions/views/pages/index.rb +136 -0
  236. data/lib/adminterface/extensions/views/pages/page.rb +18 -0
  237. data/lib/adminterface/extensions/views/title_bar.rb +74 -0
  238. data/lib/adminterface/fixtures/components.yml +73 -0
  239. data/lib/adminterface/fixtures/css_classes.yml +107 -0
  240. data/lib/adminterface/initializers/batch_actions.rb +15 -0
  241. data/lib/adminterface/initializers/comments.rb +13 -0
  242. data/lib/adminterface/initializers/configurations.rb +38 -0
  243. data/lib/adminterface/initializers/formtastic.rb +58 -0
  244. data/lib/adminterface/initializers/resource.rb +20 -0
  245. data/lib/adminterface/initializers/view_helpers.rb +14 -0
  246. data/lib/adminterface/initializers/views.rb +39 -0
  247. data/lib/adminterface/license.rb +18 -0
  248. data/lib/adminterface/licensing/base.rb +99 -0
  249. data/lib/adminterface/licensing/commercial.rb +7 -0
  250. data/lib/adminterface/licensing/notice.rb +54 -0
  251. data/lib/adminterface/licensing/personal.rb +13 -0
  252. data/lib/adminterface/public.pem +9 -0
  253. data/lib/adminterface/version.rb +3 -0
  254. data/lib/adminterface.rb +55 -0
  255. data/lib/generators/adminterface/comments/comments_generator.rb +33 -0
  256. data/lib/generators/adminterface/comments/templates/README +13 -0
  257. data/lib/generators/adminterface/comments/templates/active_admin_comment_action_text.rb +18 -0
  258. data/lib/generators/adminterface/comments/templates/migrations/drop_body_for_active_admin_comments.rb.erb +28 -0
  259. data/lib/generators/adminterface/configs/configs_generator.rb +19 -0
  260. data/lib/generators/adminterface/configs/templates/README +33 -0
  261. data/lib/generators/adminterface/install/install_generator.rb +39 -0
  262. data/lib/generators/adminterface/install/templates/dashboard.rb +32 -0
  263. data/lib/generators/adminterface/views/views_generator.rb +15 -0
  264. data/lib/generators/adminterface/webpacker/templates/README +27 -0
  265. data/lib/generators/adminterface/webpacker/templates/active_admin.js +12 -0
  266. data/lib/generators/adminterface/webpacker/templates/active_admin.scss +8 -0
  267. data/lib/generators/adminterface/webpacker/templates/print.scss +3 -0
  268. data/lib/generators/adminterface/webpacker/webpacker_generator.rb +69 -0
  269. data/lib/tasks/adminterface_tasks.rake +4 -0
  270. metadata +291 -10
  271. data/lib/name.rb +0 -1
@@ -0,0 +1,224 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import Sortable from 'sortablejs'
4
+ import HasManyClass from '../has_many'
5
+
6
+ jest.mock('sortablejs')
7
+
8
+ describe('HasMany', () => {
9
+ const html = fs.readFileSync(path.resolve(__dirname, './has_many.html'))
10
+ let element
11
+
12
+ const options = {
13
+ list: '.has-many-list',
14
+ item: 'fieldset.has_many_fields',
15
+ addLink: 'a.button.has_many_add',
16
+ removeLink: 'a.button.has_many_remove',
17
+ destroyInput: 'input[name$="[_destroy]"]'
18
+ }
19
+
20
+ beforeEach(() => {
21
+ document.documentElement.innerHTML = html
22
+ element = document.querySelector('div')
23
+ global.adminterface = {
24
+ addObserver: jest.fn()
25
+ }
26
+ })
27
+
28
+ afterEach(() => {
29
+ jest.restoreAllMocks()
30
+ })
31
+
32
+ test('constructor initiates correctly', () => {
33
+ const spyOnBind = jest.spyOn(HasManyClass.prototype, '_bind')
34
+ spyOnBind.mockImplementation(() => {})
35
+
36
+ const HasMany = new HasManyClass(element)
37
+ expect(HasMany.element).toStrictEqual(element)
38
+
39
+ expect(spyOnBind).toHaveBeenCalledTimes(1)
40
+ })
41
+
42
+ test('bind calls bindEvents, initSortable and admininterface', () => {
43
+ const spyOnBindEvents = jest.spyOn(HasManyClass.prototype, '_bindEvents')
44
+ spyOnBindEvents.mockImplementation(() => {})
45
+
46
+ const spyOnInitSortable = jest.spyOn(HasManyClass.prototype, '_initSortable')
47
+ spyOnInitSortable.mockImplementation(() => {})
48
+
49
+ const HasMany = new HasManyClass(element)
50
+
51
+ expect(spyOnBindEvents).toHaveBeenCalledWith(element)
52
+ expect(spyOnInitSortable).toHaveBeenCalledTimes(1)
53
+ expect(global.adminterface.addObserver).toHaveBeenCalledTimes(1)
54
+ expect(global.adminterface.addObserver).toHaveBeenCalledWith(
55
+ element,
56
+ HasMany,
57
+ HasMany.constructor.name,
58
+ []
59
+ )
60
+ })
61
+
62
+ test('initSortable assigns new instance of Sortable to sortable property', () => {
63
+ const spyOnBindEvents = jest.spyOn(HasManyClass.prototype, '_bindEvents')
64
+ spyOnBindEvents.mockImplementation(() => {})
65
+
66
+ const HasMany = new HasManyClass(element)
67
+
68
+ const MockSortableArgument = {
69
+ handle: '.handle',
70
+ animation: 150,
71
+ ghostClass: 'sortable-placeholder',
72
+ dragClass: 'sortable-drag',
73
+ onUpdate: expect.any(Function)
74
+ }
75
+
76
+ expect(Sortable).toHaveBeenCalledTimes(1)
77
+ expect(Sortable).toHaveBeenCalledWith(
78
+ document.querySelector(HasMany.options.list),
79
+ MockSortableArgument
80
+ )
81
+ const sortable = new Sortable(
82
+ document.querySelector(HasMany.options.list),
83
+ MockSortableArgument
84
+ )
85
+ expect(HasMany.sortable.toString()).toStrictEqual(sortable.toString())
86
+ })
87
+
88
+ test('recomputePosition changes sortableInput value', () => {
89
+ const query = 'input[name$="[address]"]'
90
+ const sortableInput = element.querySelector(query)
91
+
92
+ expect(sortableInput.value.length).not.toEqual(0)
93
+
94
+ const HasMany = new HasManyClass(element)
95
+ HasMany._recomputePosition()
96
+
97
+ expect(sortableInput.value.length).toEqual(0)
98
+ })
99
+
100
+ test('bindDestroyEvent called with element and change event added', () => {
101
+ const spyOnBindDestroyEvent = jest.spyOn(
102
+ HasManyClass.prototype,
103
+ '_bindDestroyEvent'
104
+ )
105
+ const destroyInput = document.querySelector(options.destroyInput)
106
+ destroyInput.addEventListener = jest.fn()
107
+
108
+ new HasManyClass(element) // eslint-disable-line no-new
109
+
110
+ expect(spyOnBindDestroyEvent).toHaveBeenCalledTimes(1)
111
+ expect(spyOnBindDestroyEvent).toHaveBeenCalledWith(destroyInput)
112
+
113
+ expect(destroyInput.addEventListener).toHaveBeenCalledTimes(1)
114
+ expect(destroyInput.addEventListener).toHaveBeenCalledWith(
115
+ 'change',
116
+ expect.any(Function)
117
+ )
118
+ })
119
+
120
+ test('bindRemoveEvent called with element and click event added', () => {
121
+ const spyOnBindRemoveEvent = jest.spyOn(
122
+ HasManyClass.prototype,
123
+ '_bindRemoveEvent'
124
+ )
125
+ const removeLinks = document.querySelector(options.removeLink)
126
+ removeLinks.addEventListener = jest.fn()
127
+
128
+ new HasManyClass(element) // eslint-disable-line no-new
129
+
130
+ expect(spyOnBindRemoveEvent).toHaveBeenCalledTimes(1)
131
+ expect(spyOnBindRemoveEvent).toHaveBeenCalledWith(removeLinks)
132
+
133
+ expect(removeLinks.addEventListener).toHaveBeenCalledTimes(1)
134
+ expect(removeLinks.addEventListener).toHaveBeenCalledWith(
135
+ 'click',
136
+ expect.any(Function)
137
+ )
138
+ })
139
+
140
+ test('bindRemoveEventCallBack', () => {
141
+ const mockEvent = {
142
+ preventDefault: () => {},
143
+ target: document.body.querySelector('fieldset')
144
+ }
145
+ const spyOnRecomputePosition = jest.spyOn(
146
+ HasManyClass.prototype,
147
+ '_recomputePosition'
148
+ )
149
+ const HasMany = new HasManyClass(element)
150
+ const spyOnDispatch = jest.spyOn(HasMany.element, 'dispatchEvent')
151
+
152
+ const fieldSet = mockEvent.target.closest(HasMany.options.item)
153
+ fieldSet.remove = jest.fn()
154
+
155
+ const parent = HasMany.element
156
+ const removeBeforeEvent = HasMany.events.removeBefore
157
+ removeBeforeEvent.detail = { fieldSet, parent }
158
+ const removeAfterEvent = HasMany.events.removeAfter
159
+ removeAfterEvent.detail = { fieldSet, parent }
160
+
161
+ const output = HasMany._bindRemoveEventCallBack(mockEvent)
162
+
163
+ expect(spyOnRecomputePosition).toHaveBeenCalledTimes(1)
164
+ expect(spyOnDispatch).toHaveBeenCalledTimes(2)
165
+ expect(spyOnDispatch.mock.calls).toMatchObject([
166
+ [removeBeforeEvent],
167
+ [removeAfterEvent]
168
+ ])
169
+ expect(fieldSet.remove).toHaveBeenCalledTimes(1)
170
+ expect(output).toBeTruthy()
171
+ })
172
+
173
+ test('bindAddEvent called with correctly element and click event added', () => {
174
+ const spyOnBindAddEvent = jest.spyOn(
175
+ HasManyClass.prototype,
176
+ '_bindAddEvent'
177
+ )
178
+
179
+ const addLinks = document.querySelector(options.addLink)
180
+ addLinks.addEventListener = jest.fn()
181
+
182
+ new HasManyClass(element) // eslint-disable-line no-new
183
+
184
+ expect(spyOnBindAddEvent).toHaveBeenCalledTimes(1)
185
+ expect(spyOnBindAddEvent).toHaveBeenCalledWith(addLinks)
186
+
187
+ expect(addLinks.addEventListener).toHaveBeenCalledTimes(1)
188
+ expect(addLinks.addEventListener).toHaveBeenCalledWith(
189
+ 'click',
190
+ expect.any(Function)
191
+ )
192
+ })
193
+
194
+ test('bindAddEventCallBack', () => {
195
+ const mockEvent = {
196
+ preventDefault: () => {},
197
+ target: document.body.querySelector(options.addLink)
198
+ }
199
+ const HasMany = new HasManyClass(element)
200
+ const spyOnDispatchEvent = jest.spyOn(HasMany.element, 'dispatchEvent')
201
+ const $list = HasMany.element.querySelector(HasMany.options.list)
202
+ const spyOnListAppendChild = jest.spyOn($list, 'appendChild')
203
+ const spyOnBindEvents = jest.spyOn(HasMany, '_bindEvents')
204
+ const spyOnRecomputePosition = jest.spyOn(HasMany, '_recomputePosition')
205
+ const beforeAdd = HasMany.events.addBefore
206
+ const parent = HasMany.element
207
+ const datasetHTML = document.createElement('div')
208
+
209
+ const output = HasMany._bindAddEventCallBack(mockEvent)
210
+
211
+ expect(spyOnDispatchEvent).toHaveBeenCalledTimes(2)
212
+ expect(spyOnDispatchEvent.mock.calls).toMatchObject([
213
+ [beforeAdd, [parent]],
214
+ [HasMany.events.addAfter]
215
+ ])
216
+
217
+ expect(spyOnListAppendChild).toHaveBeenCalledTimes(1)
218
+ expect(spyOnListAppendChild).toHaveBeenCalledWith(datasetHTML)
219
+ expect(spyOnBindEvents).toHaveBeenCalledTimes(1)
220
+ expect(spyOnBindEvents).toHaveBeenCalledWith(datasetHTML)
221
+ expect(spyOnRecomputePosition).toHaveBeenCalledTimes(1)
222
+ expect(output).toBeTruthy()
223
+ })
224
+ })
@@ -0,0 +1,280 @@
1
+ import {
2
+ hasTurbolinks,
3
+ turbolinksVisit,
4
+ queryString,
5
+ queryStringToParams,
6
+ toHTMLAttrString,
7
+ toQueryString,
8
+ serializeArray,
9
+ serializeObject,
10
+ cookieSet,
11
+ cookieGet,
12
+ getObjectValue,
13
+ deepMergeObject,
14
+ toSnakeCase,
15
+ toHTMLCssString,
16
+ setObjectValue,
17
+ camalize
18
+ } from '../utils'
19
+
20
+ describe('utils', () => {
21
+ let windowSpy
22
+
23
+ beforeEach(() => {
24
+ windowSpy = jest.spyOn(window, 'window', 'get')
25
+ Object.defineProperty(document, 'cookie', {
26
+ writable: true,
27
+ value: ''
28
+ })
29
+ global.Turbolinks = {
30
+ supported: true,
31
+ visit: path => path
32
+ }
33
+ })
34
+
35
+ afterEach(() => {
36
+ windowSpy.mockRestore()
37
+ })
38
+
39
+ const windowSpyMockImplement = (URL) => {
40
+ return windowSpy.mockImplementation(() => ({
41
+ location: {
42
+ search: URL
43
+ }
44
+ }))
45
+ }
46
+
47
+ test('hasTurbolinks returns boolean value correctly', () => {
48
+ expect(hasTurbolinks()).toBeTruthy()
49
+
50
+ global.Turbolinks.supported = false
51
+ expect(hasTurbolinks()).toBeFalsy()
52
+ })
53
+
54
+ test('turbolinksVisit triggers Turbolinks api correctly', () => {
55
+ const spyOnTurbolinks = jest.spyOn(global.Turbolinks, 'visit')
56
+
57
+ const params = [
58
+ { name: 'Richard', value: 1 },
59
+ { name: 'Nelson', value: 2 }
60
+ ]
61
+ const path = window.location.pathname
62
+ turbolinksVisit(params)
63
+
64
+ expect(spyOnTurbolinks).toHaveBeenCalledTimes(1)
65
+ expect(spyOnTurbolinks).toHaveBeenCalledWith(`${path}?Richard=1&Nelson=2`)
66
+ })
67
+
68
+ test('queryString returns correctly', () => {
69
+ const exampleURL = '?email=someone@example.com'
70
+
71
+ windowSpyMockImplement(exampleURL)
72
+ expect(queryString()).toEqual(exampleURL.replace(/^\?/, ''))
73
+ })
74
+
75
+ test('queryStringToParams returns correctly', () => {
76
+ const emailParam = 'email=someone@example.com'
77
+ const searchParam = 'q=Nelson+Richard'
78
+ const mockQueryString = `?${emailParam}&${searchParam}`
79
+
80
+ windowSpyMockImplement(mockQueryString)
81
+
82
+ const result = [
83
+ { name: 'email', value: 'someone@example.com' },
84
+ { name: 'q', value: 'Nelson Richard' }
85
+ ]
86
+
87
+ expect(queryStringToParams()).toEqual(result)
88
+ })
89
+
90
+ test('toHTMLAttrString returns correctly', () => {
91
+ const mockObject = {
92
+ name: 'Codey',
93
+ info: {
94
+ email: 'codey@example.com',
95
+ address: 'Tokyo'
96
+ }
97
+ }
98
+ const mockStringifyObject = JSON.stringify(mockObject.info)
99
+
100
+ const result = `name='${mockObject.name}' info='${mockStringifyObject}'`
101
+
102
+ expect(toHTMLAttrString(mockObject)).toEqual(result)
103
+ })
104
+
105
+ test('toQueryString returns query string correctly', () => {
106
+ const params = [
107
+ { name: 'Richard', value: 1 },
108
+ { name: 'Nelson', value: 2 }
109
+ ]
110
+ expect(toQueryString(params)).toEqual('Richard=1&Nelson=2')
111
+ })
112
+
113
+ test('serializeArray and serializeObject returns correctly', () => {
114
+ const elements = [
115
+ {
116
+ name: 'Test1',
117
+ disabled: false,
118
+ type: 'file'
119
+ },
120
+ {
121
+ name: 'Test2',
122
+ disabled: false,
123
+ type: 'select-multiple',
124
+ options: [
125
+ {
126
+ selected: true,
127
+ value: 'select-multiple value1'
128
+ },
129
+ {
130
+ selected: true,
131
+ value: 'select-multiple value2'
132
+ }
133
+ ]
134
+ },
135
+ {
136
+ name: 'Test3',
137
+ disabled: false,
138
+ type: 'checkbox',
139
+ checked: true,
140
+ value: 'checkbox value'
141
+ }
142
+ ]
143
+ const form = { elements }
144
+
145
+ const resultSerializeArray = [
146
+ {
147
+ name: elements[1].name,
148
+ value: elements[1].options[0].value
149
+ },
150
+ {
151
+ name: elements[1].name,
152
+ value: elements[1].options[1].value
153
+ },
154
+ {
155
+ name: elements[2].name,
156
+ value: elements[2].value
157
+ }
158
+ ]
159
+ expect(serializeArray(form)).toEqual(resultSerializeArray)
160
+
161
+ const resultSerializeObject = {
162
+ [resultSerializeArray[1].name]: resultSerializeArray[1].value,
163
+ [resultSerializeArray[2].name]: resultSerializeArray[2].value
164
+ }
165
+ expect(serializeObject(form)).toEqual(resultSerializeObject)
166
+ })
167
+
168
+ test('cookieSet returns correctly', () => {
169
+ const expireInSec = 10
170
+ const name = 'Codey'
171
+ const value = 'Du'
172
+
173
+ const expireAt = new Date()
174
+ expireAt.setTime(expireAt.getTime() + expireInSec * 1000)
175
+
176
+ const result = `${name}=${value};expires=${expireAt.toGMTString()};path=/`
177
+ cookieSet(name, value, expireInSec)
178
+ expect(document.cookie).toEqual(result)
179
+ })
180
+
181
+ test('cookieGet returns correctly', () => {
182
+ const name = 'Codey'
183
+ const expires = '2012'
184
+ const path = '/'
185
+ document.cookie = `name=${name};expires=${expires};path=${path}`
186
+
187
+ expect(cookieGet('name')).toEqual(name)
188
+ expect(cookieGet('expires')).toEqual(expires)
189
+ expect(cookieGet('path')).toEqual(path)
190
+ expect(cookieGet('something')).toBeNull()
191
+ })
192
+
193
+ test('getObjectValue returns correctly', () => {
194
+ const path = 'name.gender.address'
195
+ const data = {
196
+ name: 'Codey',
197
+ gender: 'male',
198
+ address: 'Tokyo'
199
+ }
200
+
201
+ expect(getObjectValue(data, path)).toEqual(data.name)
202
+ })
203
+
204
+ test('deepMergeObject returns correctly', () => {
205
+ const targetObject = {
206
+ name: 'Anonymous',
207
+ gender: 'Female',
208
+ hair: {
209
+ color: 'brown',
210
+ cut: 'long'
211
+ },
212
+ eyes: 'blue',
213
+ family: ['mom', 'dad']
214
+ }
215
+
216
+ const sourceObject = {
217
+ name: 'Codey Du',
218
+ gender: 'Male',
219
+ hair: {
220
+ cut: 'short'
221
+ },
222
+ family: ['wife', 'kids', 'dog']
223
+ }
224
+
225
+ const resultObject = {
226
+ name: 'Codey Du',
227
+ gender: 'Male',
228
+ hair: {
229
+ color: 'brown',
230
+ cut: 'short'
231
+ },
232
+ eyes: 'blue',
233
+ family: ['wife', 'kids', 'dog']
234
+ }
235
+
236
+ expect(deepMergeObject(targetObject, sourceObject)).toEqual(resultObject)
237
+ })
238
+
239
+ test('toSnakeCase returns correctly', () => {
240
+ const testString = 'AToZOneTwoThree123 Codey is in Tokyo 2021'
241
+ const resultString = 'a_to_z_one_two_three123_codey_is_in_tokyo_2021'
242
+
243
+ expect(toSnakeCase(testString)).toEqual(resultString)
244
+ })
245
+
246
+ test('toHTMLCssString returns correctly', () => {
247
+ const testStringPound = '#div: margin;'
248
+ const resultStringPound = 'div: margin;'
249
+
250
+ const testStringDot = '.h1: font-size'
251
+ const resultStringDot = 'h1: font-size'
252
+
253
+ expect(toHTMLCssString(testStringPound)).toEqual(resultStringPound)
254
+ expect(toHTMLCssString(testStringDot)).toEqual(resultStringDot)
255
+ })
256
+
257
+ test('setObjectValue returns correctly', () => {
258
+ const path = 'info.personal.gender'
259
+ const obj = {}
260
+ const value = 'male'
261
+
262
+ const result = {
263
+ info: {
264
+ personal: {
265
+ gender: value
266
+ }
267
+ }
268
+ }
269
+
270
+ setObjectValue(obj, path, value)
271
+ expect(obj).toEqual(result)
272
+ })
273
+
274
+ test('camalize returns correctly', () => {
275
+ const testString = 'Happy Camal casing 123'
276
+ const resultString = 'happyCamalCasing123'
277
+
278
+ expect(camalize(testString)).toEqual(resultString)
279
+ })
280
+ })
@@ -0,0 +1,106 @@
1
+ /* global Event, adminterface */
2
+ import ConfirmDialog from './confirm_dialog'
3
+ import CheckboxToggler from './checkbox_toggler'
4
+ import TableCheckboxToggler from './table_checkbox_toggler'
5
+
6
+ class BatchActions {
7
+ constructor (element) {
8
+ this.element = element
9
+ this.modal = null
10
+ this.events = {
11
+ confirm: new Event('confirm:complete')
12
+ }
13
+ this.options = {
14
+ batchActionSelector: '.batch_actions_selector',
15
+ collection: '.paginated_collection',
16
+ toggler: '#collection_selection_toggle_all'
17
+ }
18
+
19
+ this._bind()
20
+ }
21
+
22
+ _bindLinks () {
23
+ const $elements = this.element.querySelectorAll(`${this.options.batchActionSelector} li a`)
24
+ const _self = this
25
+
26
+ $elements.forEach((el) => {
27
+ el.addEventListener('click', (e) => {
28
+ const el = e.target
29
+ const dialogOptions = JSON.parse(el.dataset.aaConfirmDialog || '{}')
30
+ let message
31
+
32
+ e.stopPropagation()
33
+ e.preventDefault()
34
+
35
+ if ((message = el.dataset.confirm)) {
36
+ this.modal = new ConfirmDialog(message, JSON.parse(el.dataset.inputs), dialogOptions, function (inputs) {
37
+ const event = _self.events.confirm
38
+ event.detail = { inputs }
39
+ el.dispatchEvent(event)
40
+ })
41
+ }
42
+ })
43
+ })
44
+ }
45
+
46
+ _bindConfirm () {
47
+ const $elements = this.element.querySelectorAll(`${this.options.batchActionSelector} li a`)
48
+
49
+ $elements.forEach((el) => {
50
+ el.addEventListener(this.events.confirm.type, function (e) {
51
+ let value
52
+ const $batchActionInputs = document.getElementById('batch_action_inputs')
53
+ const inputs = (e.detail || {}).inputs
54
+
55
+ e.stopPropagation()
56
+ e.preventDefault()
57
+
58
+ if ((value = JSON.stringify(inputs))) {
59
+ $batchActionInputs.removeAttribute('disabled')
60
+ $batchActionInputs.value = value
61
+ } else {
62
+ $batchActionInputs.setAttribute('disabled', 'disabled')
63
+ }
64
+
65
+ document.getElementById('batch_action').value = e.target.dataset.action
66
+ document.getElementById('collection_selection').submit()
67
+ })
68
+ })
69
+ }
70
+
71
+ _bindSelector () {
72
+ const $indexTable = this.element.querySelector(`${this.options.collection} table.index_table`)
73
+ const $batchActionSelector = this.element.querySelector(this.options.batchActionSelector)
74
+ const $toggleAll = this.element.querySelector(this.options.toggler)
75
+ const $checkboxes = this.element.querySelectorAll(`${this.options.collection} input[type="checkbox"]`)
76
+ const $toggler = this.element.querySelector(`${this.options.batchActionSelector} .dropdown-toggle`)
77
+
78
+ if ($batchActionSelector && $toggleAll) {
79
+ if ($indexTable) {
80
+ this.tableCheckboxToggler = new TableCheckboxToggler(`${this.options.collection} table.index_table`)
81
+ } else {
82
+ this.checkboxToggler = new CheckboxToggler(this.options.collection)
83
+ }
84
+ }
85
+
86
+ $checkboxes.forEach((el) => {
87
+ el.addEventListener('change', (_e) => {
88
+ if (this.element.querySelectorAll(`${this.options.collection} input[type="checkbox"]:checked`).length) {
89
+ $toggler.classList.remove('disabled')
90
+ } else {
91
+ $toggler.classList.add('disabled')
92
+ }
93
+ })
94
+ })
95
+ }
96
+
97
+ _bind () {
98
+ this._bindLinks()
99
+ this._bindConfirm()
100
+ this._bindSelector()
101
+
102
+ adminterface.addObserver(this.element, this, this.constructor.name)
103
+ }
104
+ }
105
+
106
+ export default BatchActions
@@ -0,0 +1,55 @@
1
+ class CheckboxToggler {
2
+ constructor (container, options) {
3
+ this.options = options
4
+ this.container = container
5
+ this._init()
6
+ this._bind()
7
+ }
8
+
9
+ _init () {
10
+ if (!this.container) {
11
+ throw new Error('Container element not found')
12
+ } else {
13
+ this.$container = document.querySelector(this.container)
14
+ }
15
+
16
+ if (!this.$container.querySelector('.toggle_all')) {
17
+ throw new Error('"toggle all" checkbox not found')
18
+ } else {
19
+ this.$toggleAllCheckbox = this.$container.querySelector('.toggle_all')
20
+ }
21
+
22
+ this.$checkboxes = this.$container.querySelectorAll('input[type="checkbox"]:not(.toggle_all)')
23
+ }
24
+
25
+ _bind () {
26
+ this.$checkboxes.forEach((el) => {
27
+ el.addEventListener('change', (e) => {
28
+ this._didChangeCheckbox(e.target)
29
+ })
30
+ })
31
+
32
+ this.$toggleAllCheckbox.addEventListener('change', (_e) => {
33
+ this._didChangeToggleAllCheckbox()
34
+ })
35
+ }
36
+
37
+ _didChangeCheckbox (_checkbox) {
38
+ const numChecked = [...this.$checkboxes].filter(el => el.checked).length
39
+ const allChecked = numChecked === this.$checkboxes.length
40
+ const someChecked = (numChecked > 0) && (numChecked < this.$checkboxes.length)
41
+
42
+ this.$toggleAllCheckbox.checked = allChecked
43
+ this.$toggleAllCheckbox.indeterminate = someChecked
44
+ }
45
+
46
+ _didChangeToggleAllCheckbox () {
47
+ const setting = this.$toggleAllCheckbox.checked
48
+ this.$checkboxes.forEach((el) => {
49
+ el.checked = setting
50
+ })
51
+ return setting
52
+ }
53
+ }
54
+
55
+ export default CheckboxToggler