pageflow 17.1.0 → 17.2.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 (302) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +319 -345
  3. data/README.md +3 -4
  4. data/admins/pageflow/accounts.rb +6 -6
  5. data/admins/pageflow/entry.rb +10 -8
  6. data/admins/pageflow/membership.rb +11 -11
  7. data/admins/pageflow/translations.rb +5 -5
  8. data/app/assets/images/pageflow/admin/icons/comment.svg +1 -0
  9. data/app/assets/javascripts/pageflow/dist/ui.js +1350 -630
  10. data/app/assets/javascripts/pageflow/editor/vendor.js +0 -1
  11. data/app/assets/javascripts/pageflow/ui.js +0 -1
  12. data/app/assets/stylesheets/pageflow/admin/entry_comments_indicator.scss +45 -0
  13. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +8 -1
  14. data/app/assets/stylesheets/pageflow/admin.scss +1 -0
  15. data/app/assets/stylesheets/pageflow/animations/spin.scss +9 -0
  16. data/app/assets/stylesheets/pageflow/animations.scss +1 -0
  17. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +21 -5
  18. data/app/assets/stylesheets/pageflow/editor/base.scss +9 -1
  19. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +13 -1
  20. data/app/assets/stylesheets/pageflow/editor/edit_configuration_view.scss +5 -0
  21. data/app/assets/stylesheets/pageflow/editor/file_folders.scss +127 -0
  22. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +0 -1
  23. data/app/assets/stylesheets/pageflow/editor/file_meta_data_overlay.scss +45 -0
  24. data/app/assets/stylesheets/pageflow/editor/file_preview.scss +138 -0
  25. data/app/assets/stylesheets/pageflow/editor/file_references.scss +68 -0
  26. data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +1 -1
  27. data/app/assets/stylesheets/pageflow/editor/file_stage_icons.scss +17 -0
  28. data/app/assets/stylesheets/pageflow/editor/file_stages.scss +25 -32
  29. data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +31 -48
  30. data/app/assets/stylesheets/pageflow/editor/file_type_pills.scss +73 -0
  31. data/app/assets/stylesheets/pageflow/editor/files.scss +230 -50
  32. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +142 -14
  33. data/app/assets/stylesheets/pageflow/editor/folder_breadcrumb.scss +40 -0
  34. data/app/assets/stylesheets/pageflow/editor/info_box.scss +12 -0
  35. data/app/assets/stylesheets/pageflow/editor/inputs/edit_defaults_button.scss +15 -0
  36. data/app/assets/stylesheets/pageflow/editor/inputs.scss +1 -0
  37. data/app/assets/stylesheets/pageflow/editor/list.scss +88 -38
  38. data/app/assets/stylesheets/pageflow/editor/manage_files.scss +9 -0
  39. data/app/assets/stylesheets/pageflow/editor/menu.scss +14 -0
  40. data/app/assets/stylesheets/pageflow/editor/move_to_folder_dialog.scss +83 -0
  41. data/app/assets/stylesheets/pageflow/mixins/pageflow.scss +9 -5
  42. data/app/assets/stylesheets/pageflow/ui/color_picker.scss +230 -0
  43. data/app/assets/stylesheets/pageflow/ui/forms.scss +34 -4
  44. data/app/assets/stylesheets/pageflow/ui/functions.scss +12 -2
  45. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +9 -16
  46. data/app/assets/stylesheets/pageflow/ui/input/text_area_input.scss +15 -0
  47. data/app/assets/stylesheets/pageflow/ui/overrides.scss +8 -0
  48. data/app/assets/stylesheets/pageflow/ui/properties.scss +29 -0
  49. data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -3
  50. data/app/assets/stylesheets/pageflow/ui.scss +4 -2
  51. data/app/controllers/pageflow/editor/entries_controller.rb +1 -0
  52. data/app/controllers/pageflow/editor/file_folders_controller.rb +80 -0
  53. data/app/controllers/pageflow/editor/file_import_controller.rb +1 -0
  54. data/app/controllers/pageflow/editor/files_controller.rb +15 -6
  55. data/app/controllers/pageflow/editor/oembeds_controller.rb +21 -0
  56. data/app/controllers/pageflow/entries_controller.rb +17 -13
  57. data/app/controllers/pageflow/review/comment_thread_reads_controller.rb +30 -0
  58. data/app/controllers/pageflow/review/comment_threads_controller.rb +60 -0
  59. data/app/controllers/pageflow/review/comments_controller.rb +53 -0
  60. data/app/controllers/pageflow/revisions_controller.rb +4 -1
  61. data/app/helpers/pageflow/admin/entries_helper.rb +40 -0
  62. data/app/helpers/pageflow/admin/widgets_helper.rb +2 -2
  63. data/app/helpers/pageflow/common_entry_seed_helper.rb +5 -3
  64. data/app/helpers/pageflow/editor/config_helper.rb +3 -1
  65. data/app/helpers/pageflow/entries_helper.rb +24 -42
  66. data/app/helpers/pageflow/entry_json_seed_helper.rb +4 -4
  67. data/app/helpers/pageflow/public_i18n_helper.rb +9 -8
  68. data/app/helpers/pageflow/structured_data_helper.rb +4 -1
  69. data/app/helpers/pageflow/themes_helper.rb +1 -3
  70. data/app/helpers/pageflow/widgets_helper.rb +2 -1
  71. data/app/inputs/pageflow_permalink_input.rb +6 -2
  72. data/app/models/concerns/pageflow/auto_generated_perma_id.rb +11 -4
  73. data/app/models/concerns/pageflow/reusable_file.rb +1 -1
  74. data/app/models/concerns/pageflow/serialization_blacklist.rb +2 -4
  75. data/app/models/concerns/pageflow/translated_attributes.rb +101 -0
  76. data/app/models/concerns/pageflow/uploadable_file.rb +1 -1
  77. data/app/models/pageflow/comment.rb +30 -0
  78. data/app/models/pageflow/comment_thread.rb +65 -0
  79. data/app/models/pageflow/comment_thread_read.rb +22 -0
  80. data/app/models/pageflow/customized_theme.rb +3 -2
  81. data/app/models/pageflow/draft_entry.rb +20 -8
  82. data/app/models/pageflow/entry.rb +2 -0
  83. data/app/models/pageflow/entry_at_revision.rb +9 -1
  84. data/app/models/pageflow/entry_comment_summary.rb +114 -0
  85. data/app/models/pageflow/file_folder.rb +74 -0
  86. data/app/models/pageflow/file_reuse.rb +3 -2
  87. data/app/models/pageflow/file_usage.rb +8 -0
  88. data/app/models/pageflow/image_file.rb +24 -20
  89. data/app/models/pageflow/image_file_url_templates.rb +3 -3
  90. data/app/models/pageflow/legal_info.rb +38 -0
  91. data/app/models/pageflow/nested_revision_component_copy.rb +1 -1
  92. data/app/models/pageflow/permalink.rb +1 -0
  93. data/app/models/pageflow/revision.rb +6 -2
  94. data/app/models/pageflow/site.rb +10 -0
  95. data/app/models/pageflow/used_file.rb +8 -2
  96. data/app/models/pageflow/widget.rb +2 -2
  97. data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +0 -1
  98. data/app/views/pageflow/editor/config/_seeds.json.jbuilder +4 -2
  99. data/app/views/pageflow/editor/entries/_entry.json.jbuilder +2 -1
  100. data/app/views/pageflow/editor/entries/seed.json.erb +1 -0
  101. data/app/views/pageflow/editor/file_folders/_file_folder.json.jbuilder +5 -0
  102. data/app/views/pageflow/editor/file_folders/create.json.jbuilder +1 -0
  103. data/app/views/pageflow/editor/file_folders/index.json.jbuilder +3 -0
  104. data/app/views/pageflow/editor/files/_file.json.jbuilder +2 -1
  105. data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +3 -0
  106. data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +3 -0
  107. data/app/views/pageflow/files/_file.json.jbuilder +2 -0
  108. data/app/views/pageflow/image_files/_image_file.json.jbuilder +0 -1
  109. data/app/views/pageflow/review/comment_threads/_comment_thread.json.jbuilder +20 -0
  110. data/app/views/pageflow/review/comment_threads/create.json.jbuilder +2 -0
  111. data/app/views/pageflow/review/comment_threads/index.json.jbuilder +13 -0
  112. data/app/views/pageflow/review/comments/_comment.json.jbuilder +13 -0
  113. data/app/views/pageflow/review/comments/create.json.jbuilder +1 -0
  114. data/app/views/pageflow/structured_data/_entry.json.jbuilder +2 -3
  115. data/app/views/pageflow/video_files/_video_file.json.jbuilder +0 -1
  116. data/config/locales/de.yml +152 -14
  117. data/config/locales/en.yml +150 -14
  118. data/config/routes.rb +14 -0
  119. data/db/migrate/20140418225525_setup_schema.rb +2 -2
  120. data/db/migrate/20251015000000_add_structured_data_type_name_to_revisions.rb +5 -0
  121. data/db/migrate/20251029142849_convert_legacy_social_embeds_to_unified_social_embed.rb +38 -0
  122. data/db/migrate/20251119083347_fix_main_storyline_positions.rb +16 -0
  123. data/db/migrate/20251119083348_ensure_scrolled_entries_have_excursion_storyline.rb +15 -0
  124. data/db/migrate/20260303000000_add_layout_version_to_revisions.rb +6 -0
  125. data/db/migrate/20260323000000_create_comment_threads_and_comments.rb +30 -0
  126. data/db/migrate/20260414000000_add_subject_range_to_comment_threads.rb +5 -0
  127. data/db/migrate/20260415000000_add_section_perma_id_to_comment_threads.rb +5 -0
  128. data/db/migrate/20260727000000_add_quote_to_comments.rb +5 -0
  129. data/db/migrate/20260728000000_create_file_folders.rb +17 -0
  130. data/db/migrate/20260805000000_add_attribute_translations_to_sites.rb +5 -0
  131. data/db/migrate/20260806000000_add_edited_at_to_comments.rb +5 -0
  132. data/db/migrate/20260810000000_create_pageflow_scrolled_lottie_files.rb +24 -0
  133. data/db/migrate/20260817000000_create_comment_thread_reads.rb +16 -0
  134. data/db/migrate/20260819000000_add_unread_comments_since_at_to_users.rb +18 -0
  135. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +8665 -3890
  136. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +979 -3571
  137. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +20199 -28120
  138. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +18652 -26584
  139. data/entry_types/scrolled/app/assets/stylesheets/pageflow_scrolled/ui.scss +2 -0
  140. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +33 -3
  141. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +2 -3
  142. data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +21 -1
  143. data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +3 -3
  144. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +7 -5
  145. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +5 -2
  146. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +13 -5
  147. data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +28 -13
  148. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +26 -11
  149. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +2 -2
  150. data/entry_types/scrolled/app/helpers/pageflow_scrolled/sprockets_helper.rb +16 -0
  151. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +174 -3
  152. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +49 -7
  153. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file.rb +6 -0
  154. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file_url_templates.rb +16 -0
  155. data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +1 -0
  156. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +13 -12
  157. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +30 -10
  158. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +9 -0
  159. data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +1 -1
  160. data/entry_types/scrolled/config/configuration_schemas/pageflow-scrolled.json +414 -0
  161. data/entry_types/scrolled/config/locales/de.yml +512 -65
  162. data/entry_types/scrolled/config/locales/en.yml +518 -52
  163. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +2 -2
  164. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +51 -6
  165. data/entry_types/scrolled/lib/pageflow_scrolled/configuration_schemas.rb +91 -0
  166. data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +2 -1
  167. data/entry_types/scrolled/lib/pageflow_scrolled/entry_file_reference_locations.rb +71 -0
  168. data/entry_types/scrolled/lib/pageflow_scrolled/entry_structured_data_types/faq_page.rb +58 -0
  169. data/entry_types/scrolled/lib/pageflow_scrolled/file_reference_locations.rb +69 -0
  170. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +205 -22
  171. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +2 -4
  172. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +56 -36
  173. data/entry_types/scrolled/lib/pageflow_scrolled/theme_options_default_scale.rb +35 -0
  174. data/entry_types/scrolled/lib/pageflow_scrolled.rb +13 -0
  175. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +19 -1
  176. data/entry_types/scrolled/package/config/configurationSchemas.js +70 -0
  177. data/entry_types/scrolled/package/config/rollup/configurationSchemas.js +19 -0
  178. data/entry_types/scrolled/package/config/webpack.js +72 -0
  179. data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -1
  180. data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1181 -915
  181. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.css +1 -0
  182. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.js +219 -0
  183. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.css +1 -0
  184. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.js +244 -0
  185. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.css +1 -0
  186. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.js +823 -0
  187. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +71 -21
  188. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.css +1 -0
  189. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.js +277 -0
  190. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.css +1 -0
  191. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.js +244 -0
  192. data/entry_types/scrolled/package/contentElements-editor.js +1915 -1097
  193. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  194. data/entry_types/scrolled/package/contentElements-frontend.js +3093 -1679
  195. data/entry_types/scrolled/package/editor.css +1 -0
  196. data/entry_types/scrolled/package/editor.js +8667 -4077
  197. data/entry_types/scrolled/package/entryState.js +1600 -0
  198. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-20c600de.js +1979 -0
  199. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-58189220.js +2538 -0
  200. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-9f76033e.js → PhonePlatformContext-035a99fa.js} +7 -12
  201. data/entry_types/scrolled/package/frontend/PhonePlatformContext-9da43797.js +33 -0
  202. data/entry_types/scrolled/package/frontend/Placeholder.module-bf0966b4.js +5267 -0
  203. data/entry_types/scrolled/package/frontend/Placeholder.module-d22a9335.js +4180 -0
  204. data/entry_types/scrolled/package/frontend/ThemeIcon-ab834849.js +754 -0
  205. data/entry_types/scrolled/package/frontend/ThemeIcon-f6cb3a8f.js +773 -0
  206. data/entry_types/scrolled/package/frontend/{ToggleFullscreenCornerButton-788e9cdb.js → ToggleFullscreenCornerButton-30fd1124.js} +6 -6
  207. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-61c25224.js +72 -0
  208. data/entry_types/scrolled/package/frontend/Viewer-1074fd59.js +147 -0
  209. data/entry_types/scrolled/package/frontend/Viewer-1482ca34.js +480 -0
  210. data/entry_types/scrolled/package/frontend/Viewer-70504231.js +210 -0
  211. data/entry_types/scrolled/package/frontend/{Viewer-0345ce57.js → Viewer-cc8d4072.js} +84 -92
  212. data/entry_types/scrolled/package/frontend/Wavesurfer-656e3c36.js +302 -0
  213. data/entry_types/scrolled/package/frontend/Wavesurfer-b753b838.js +385 -0
  214. data/entry_types/scrolled/package/frontend/commenting.css +1 -0
  215. data/entry_types/scrolled/package/frontend/extensions-167ae143.js +1455 -0
  216. data/entry_types/scrolled/package/frontend/extensions-2318f92b.js +5918 -0
  217. data/entry_types/scrolled/package/frontend/extensions-a461fd13.js +4890 -0
  218. data/entry_types/scrolled/package/frontend/extensions-c98269fd.js +1982 -0
  219. data/entry_types/scrolled/package/frontend/i18n-493cd2a6.js +93 -0
  220. data/entry_types/scrolled/package/frontend/i18n-e06cac14.js +100 -0
  221. data/entry_types/scrolled/package/frontend/{index-eb670c2f.js → index-2e5546f7.js} +36 -44
  222. data/entry_types/scrolled/package/frontend/index-de2b418d.js +130 -0
  223. data/entry_types/scrolled/package/frontend/index.css +1 -1
  224. data/entry_types/scrolled/package/frontend/index.js +1870 -875
  225. data/entry_types/scrolled/package/frontend/inlineEditing.css +1 -0
  226. data/entry_types/scrolled/package/frontend/useContentElementEditorState-a084912e.js +25 -0
  227. data/entry_types/scrolled/package/frontend/useDelayedBoolean-a387d85b.js +24 -0
  228. data/entry_types/scrolled/package/frontend/{usePhonePlatform-c2ba875d.js → usePhonePlatform-4ecb96d4.js} +1 -1
  229. data/entry_types/scrolled/package/frontend/usePhonePlatform-ce9ff08d.js +8 -0
  230. data/entry_types/scrolled/package/frontend-server.js +20 -209
  231. data/entry_types/scrolled/package/package.json +5 -3
  232. data/entry_types/scrolled/package/review.css +1 -0
  233. data/entry_types/scrolled/package/review.js +3306 -0
  234. data/entry_types/scrolled/package/testHelpers.js +836 -359
  235. data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
  236. data/entry_types/scrolled/package/widgets/consentBar.js +269 -290
  237. data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
  238. data/entry_types/scrolled/package/widgets/defaultNavigation.js +911 -352
  239. data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -1
  240. data/entry_types/scrolled/package/widgets/excursionSheet.js +87 -124
  241. data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
  242. data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +62 -29
  243. data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +32 -13
  244. data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -1
  245. data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +56 -100
  246. data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
  247. data/entry_types/scrolled/package/widgets/textInlineFileRights.js +33 -28
  248. data/entry_types/scrolled/package/widgets-server.js +1 -0
  249. data/entry_types/scrolled/spec/factories/lottie_files.rb +29 -0
  250. data/entry_types/scrolled/spec/fixtures/animation.lottie +0 -0
  251. data/entry_types/scrolled/spec/fixtures/font.woff +0 -0
  252. data/entry_types/scrolled/spec/fixtures/font.woff2 +0 -0
  253. data/lib/generators/pageflow/resque/resque_generator.rb +0 -2
  254. data/lib/pageflow/configuration.rb +49 -7
  255. data/lib/pageflow/deprecation.rb +19 -0
  256. data/lib/pageflow/engine.rb +1 -1
  257. data/lib/pageflow/entries_controller_env_helper.rb +32 -2
  258. data/lib/pageflow/entry_export_import/entry_serialization.rb +4 -4
  259. data/lib/pageflow/entry_export_import/page_type_versions.rb +2 -2
  260. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +10 -0
  261. data/lib/pageflow/entry_export_import/revision_serialization.rb +3 -0
  262. data/lib/pageflow/entry_structured_data_types.rb +43 -0
  263. data/lib/pageflow/entry_type_configuration.rb +1 -0
  264. data/lib/pageflow/file_type.rb +2 -2
  265. data/lib/pageflow/file_types.rb +1 -1
  266. data/lib/pageflow/oembed_providers.rb +42 -0
  267. data/lib/pageflow/primary_domain_entry_redirect.rb +6 -0
  268. data/lib/pageflow/rails_version.rb +13 -2
  269. data/lib/pageflow/themes.rb +41 -0
  270. data/lib/pageflow/user_mixin.rb +12 -0
  271. data/lib/pageflow/version.rb +1 -1
  272. data/lib/pageflow.rb +1 -0
  273. data/package/config/eslint-rules/documented-in-toc.js +125 -0
  274. data/package/config/jest/index.js +0 -1
  275. data/package/config/webpack.js +0 -1
  276. data/package/config/webpack5.js +0 -1
  277. data/package/editor.js +4378 -1897
  278. data/package/frontend.js +647 -886
  279. data/package/package.json +2 -1
  280. data/package/review.js +384 -0
  281. data/package/testHelpers.js +248 -188
  282. data/package/ui.js +1346 -630
  283. data/package/vendor/stubs/wysihtml5.js +2 -0
  284. data/spec/factories/comment_thread_reads.rb +10 -0
  285. data/spec/factories/comment_threads.rb +10 -0
  286. data/spec/factories/comments.rb +9 -0
  287. data/spec/factories/file_folders.rb +8 -0
  288. data/spec/factories/published_entries.rb +2 -0
  289. metadata +188 -67
  290. data/app/assets/stylesheets/pageflow/editor/select_button.scss +0 -51
  291. data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +0 -55
  292. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-51914be7.js +0 -5439
  293. data/entry_types/scrolled/package/frontend/ThemeIcon-81f2f066.js +0 -1932
  294. data/entry_types/scrolled/package/frontend/Viewer-cdc549cc.js +0 -147
  295. data/entry_types/scrolled/package/frontend/Wavesurfer-1cdc3925.js +0 -313
  296. data/entry_types/scrolled/package/frontend/components-96660ffd.js +0 -3803
  297. data/entry_types/scrolled/package/frontend/inherits-539844a6.js +0 -99
  298. data/entry_types/scrolled/package/frontend/useContentElementEditorState-77fe6c79.js +0 -51
  299. data/entry_types/scrolled/package/widgets/excursionDialog.css +0 -1
  300. data/entry_types/scrolled/package/widgets/excursionDialog.js +0 -109
  301. data/lib/generators/pageflow/resque/templates/resque_enqueue_after_commit_patch.rb +0 -25
  302. data/package/vendor/jquery.minicolors.js +0 -1108
@@ -1,59 +1,351 @@
1
- import React, { useEffect, useState, useContext } from 'react';
1
+ import { ConfigurationEditorTabView } from 'pageflow/ui';
2
+ import { FileInputView } from 'pageflow/editor';
3
+ import { render, act } from '@testing-library/react';
4
+ import { renderBackboneView, ConfigurationEditor } from 'pageflow/testHelpers';
5
+ export { renderBackboneView as renderReactBasedBackboneView } from 'pageflow/testHelpers';
6
+ import { EditContentElementView, editor } from 'pageflow-scrolled/editor';
7
+ import React, { useEffect, useState } from 'react';
2
8
  import { DndProvider } from 'react-dnd';
3
9
  import { HTML5Backend } from 'react-dnd-html5-backend';
4
10
  import BackboneEvents from 'backbone-events-standalone';
5
- import { render, act } from '@testing-library/react';
6
- import { RootProviders, useEntryStateDispatch, ContentElementLifecycleContext, ContentElementEditorStateContext, PhonePlatformContext, ContentElementAttributesProvider, ContentElementEditorCommandEmitterContext } from 'pageflow-scrolled/frontend';
7
- import { renderHook } from '@testing-library/react-hooks/dom';
11
+ import { PhonePlatformContext, RootProviders, useEntryStateDispatch, ContentElementLifecycleContext, ContentElementEditorCommandEmitterContext, ContentElementEditorStateContext, MainStorylineActivity, ContentElementAttributesProvider, ContentElementViewTimelineContext } from 'pageflow-scrolled/frontend';
8
12
  import { Consent } from 'pageflow/frontend';
13
+ import { renderHook } from '@testing-library/react-hooks/dom';
9
14
 
10
- function _defineProperty(obj, key, value) {
11
- if (key in obj) {
12
- Object.defineProperty(obj, key, {
13
- value: value,
14
- enumerable: true,
15
- configurable: true,
16
- writable: true
17
- });
15
+ function styleInject(css, ref) {
16
+ if ( ref === void 0 ) ref = {};
17
+ var insertAt = ref.insertAt;
18
+
19
+ if (!css || typeof document === 'undefined') { return; }
20
+
21
+ var head = document.head || document.getElementsByTagName('head')[0];
22
+ var style = document.createElement('style');
23
+ style.type = 'text/css';
24
+
25
+ if (insertAt === 'top') {
26
+ if (head.firstChild) {
27
+ head.insertBefore(style, head.firstChild);
28
+ } else {
29
+ head.appendChild(style);
30
+ }
18
31
  } else {
19
- obj[key] = value;
32
+ head.appendChild(style);
20
33
  }
21
34
 
22
- return obj;
35
+ if (style.styleSheet) {
36
+ style.styleSheet.cssText = css;
37
+ } else {
38
+ style.appendChild(document.createTextNode(css));
39
+ }
23
40
  }
24
41
 
25
- function ownKeys(object, enumerableOnly) {
26
- var keys = Object.keys(object);
42
+ var css = ".ContentElementBox-module_properties__1ljc9 {\n --content-element-box-box-shadow:\n var(--content-element-box-shadow, 0 0 #000),\n 0 0 0 1px var(--content-element-box-outline-color, transparent);\n}\n\n.ContentElementBox-module_wrapper__3wZgP {\n overflow: hidden;\n border-radius: var(--content-element-box-border-radius,\n var(--theme-content-element-box-border-radius));\n box-shadow: var(--content-element-box-box-shadow);\n}\n\n.ContentElementBox-module_full__AfWPr {\n border-radius: var(--content-element-box-border-radius,\n var(--theme-content-element-full-box-border-radius,\n var(--theme-content-element-box-border-radius)));\n}\n\n.ContentElementBox-module_positioned__3R1dq {\n position: relative;\n height: 100%;\n}\n";
43
+ var styles = {"properties":"ContentElementBox-module_properties__1ljc9","wrapper":"ContentElementBox-module_wrapper__3wZgP","full":"ContentElementBox-module_full__AfWPr","positioned":"ContentElementBox-module_positioned__3R1dq"};
44
+ styleInject(css);
27
45
 
28
- if (Object.getOwnPropertySymbols) {
29
- var symbols = Object.getOwnPropertySymbols(object);
30
- if (enumerableOnly) symbols = symbols.filter(function (sym) {
31
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
32
- });
33
- keys.push.apply(keys, symbols);
34
- }
46
+ function getElement(subject) {
47
+ var _subject$el;
48
+ return (_subject$el = subject === null || subject === void 0 ? void 0 : subject.el) !== null && _subject$el !== void 0 ? _subject$el : subject;
49
+ }
35
50
 
36
- return keys;
51
+ /**
52
+ * Assert that the subject contains a `ContentElementBox` and, optionally,
53
+ * that the box has specific theme styles applied. Registered via
54
+ * {@link useContentElementMatchers}.
55
+ *
56
+ * The subject is a DOM element, e.g. the `container` returned by
57
+ * {@link renderInContentElement}. Negate with `.not` to assert that no
58
+ * box is present.
59
+ *
60
+ * @param {Object} [options]
61
+ * @param {string} [options.boxShadow] - Expected box shadow theme scale, e.g. `'md'`.
62
+ * @param {string} [options.borderRadius] - Expected border radius theme scale, e.g. `'circle'`.
63
+ * @param {string} [options.outlineColor] - Expected outline color.
64
+ *
65
+ * @example
66
+ * expect(container).toContainContentElementBox();
67
+ * expect(container).toContainContentElementBox({borderRadius: 'circle', boxShadow: 'md'});
68
+ * expect(container).not.toContainContentElementBox();
69
+ */
70
+ function toContainContentElementBox(subject, options = {}) {
71
+ const wrapper = getElement(subject).querySelector(`.${styles.wrapper}`);
72
+ if (!wrapper) {
73
+ return {
74
+ pass: false,
75
+ message: () => 'expected element to contain a content element box, but found none'
76
+ };
77
+ }
78
+ const mismatches = boxPropertyMismatches(wrapper, options);
79
+ return {
80
+ pass: mismatches.length === 0,
81
+ message: () => mismatches.length ? `expected content element box to have ${mismatches.join(', ')}` : `expected element not to contain a content element box${describeOptions(options)}`
82
+ };
83
+ }
84
+ const boxProperties = {
85
+ boxShadow: {
86
+ customProperty: '--content-element-box-shadow',
87
+ expectedValue: value => `var(--theme-content-element-box-shadow-${value})`
88
+ },
89
+ borderRadius: {
90
+ customProperty: '--content-element-box-border-radius',
91
+ expectedValue: value => `var(--theme-content-element-box-border-radius-${value})`
92
+ },
93
+ outlineColor: {
94
+ customProperty: '--content-element-box-outline-color',
95
+ expectedValue: value => value
96
+ }
97
+ };
98
+ function boxPropertyMismatches(wrapper, options) {
99
+ return Object.keys(options).reduce((mismatches, name) => {
100
+ const property = boxProperties[name];
101
+ if (property) {
102
+ const expected = property.expectedValue(options[name]);
103
+ const actual = wrapper.style.getPropertyValue(property.customProperty);
104
+ if (actual !== expected) {
105
+ mismatches.push(`${name} ${JSON.stringify(options[name])} (found ${JSON.stringify(actual)})`);
106
+ }
107
+ }
108
+ return mismatches;
109
+ }, []);
110
+ }
111
+ function describeOptions(options) {
112
+ const names = Object.keys(options).filter(name => boxProperties[name]);
113
+ return names.length ? ` with ${names.join(', ')}` : '';
37
114
  }
38
115
 
39
- function _objectSpread2(target) {
40
- for (var i = 1; i < arguments.length; i++) {
41
- var source = arguments[i] != null ? arguments[i] : {};
116
+ var css$1 = ".FitViewport-module_container__-awVj {\n width: 100%;\n margin: 0 auto;\n max-width: var(\n --theme-fit-viewport,\n calc(100 * var(--vh) / var(--fit-viewport-aspect-ratio) * var(--fit-viewport-scale, 1))\n );\n}\n\n.FitViewport-module_content__1_K5a {\n position: relative;\n}\n\n.FitViewport-module_content__1_K5a > div:first-child {\n padding-top: calc(100% * var(--fit-viewport-aspect-ratio));\n}\n\n.FitViewport-module_inner__3psd1 {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n}\n\n@media print {\n .FitViewport-module_container__-awVj {\n page-break-inside: avoid;\n }\n}\n";
117
+ var styles$1 = {"container":"FitViewport-module_container__-awVj","content":"FitViewport-module_content__1_K5a","inner":"FitViewport-module_inner__3psd1"};
118
+ styleInject(css$1);
42
119
 
43
- if (i % 2) {
44
- ownKeys(Object(source), true).forEach(function (key) {
45
- _defineProperty(target, key, source[key]);
46
- });
47
- } else if (Object.getOwnPropertyDescriptors) {
48
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
49
- } else {
50
- ownKeys(Object(source)).forEach(function (key) {
51
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
52
- });
120
+ /**
121
+ * Assert that the subject contains a `FitViewport` and, optionally, that
122
+ * it has a specific aspect ratio. Registered via
123
+ * {@link useContentElementMatchers}.
124
+ *
125
+ * The subject is a DOM element, e.g. the `container` returned by
126
+ * {@link renderInContentElement}. Negate with `.not` to assert that no
127
+ * `FitViewport` is present.
128
+ *
129
+ * @param {Object} [options]
130
+ * @param {string} [options.aspectRatio] -
131
+ * Expected aspect ratio: a theme scale name (e.g. `'square'`) or a raw
132
+ * ratio value (e.g. `'0.75'`).
133
+ *
134
+ * @example
135
+ * expect(container).toContainFitViewport();
136
+ * expect(container).toContainFitViewport({aspectRatio: 'square'});
137
+ */
138
+ function toContainFitViewport(subject, {
139
+ aspectRatio
140
+ } = {}) {
141
+ const container = getElement(subject).querySelector(`.${styles$1.container}`);
142
+ if (!container) {
143
+ return {
144
+ pass: false,
145
+ message: () => 'expected element to contain a FitViewport, but found none'
146
+ };
147
+ }
148
+ if (aspectRatio !== undefined) {
149
+ const actual = getAspectRatio(container);
150
+ if (actual !== aspectRatio) {
151
+ return {
152
+ pass: false,
153
+ message: () => `expected FitViewport to have aspect ratio ${JSON.stringify(aspectRatio)} ` + `(found ${JSON.stringify(actual)})`
154
+ };
53
155
  }
54
156
  }
157
+ return {
158
+ pass: true,
159
+ message: () => 'expected element not to contain a FitViewport'
160
+ };
161
+ }
162
+ function getAspectRatio(container) {
163
+ const cssValue = container.style.getPropertyValue('--fit-viewport-aspect-ratio');
164
+ const match = cssValue.match(/var\(--theme-aspect-ratio-(.+)\)/);
165
+ return match ? match[1] : cssValue;
166
+ }
167
+
168
+ /**
169
+ * Assert that all inputs rendered by a configuration editor are
170
+ * described by a configuration schema. Registered via
171
+ * {@link useConfigurationEditorMatchers}.
172
+ *
173
+ * The subject is a function that renders the configuration editor. Only
174
+ * inputs of the rendered form are seen. Views on nested routes render a
175
+ * form of their own and are checked against the part of the schema that
176
+ * describes the object they configure.
177
+ *
178
+ * @param {Object} schema - Configuration schema of the subject.
179
+ * @param {Object} [options]
180
+ * @param {Array<string>} [options.path] - Path of the configuration the
181
+ * rendered form writes, if it is not the root. `*` stands for array
182
+ * items and map values.
183
+ *
184
+ * @example
185
+ * expect(() => renderContentElementConfigurationEditor({entry, contentElement}))
186
+ * .toRenderInputsMatching(require('./schema.json'));
187
+ *
188
+ * @example
189
+ * expect(() => renderBackboneView(new SidebarEditAreaView(options)))
190
+ * .toRenderInputsMatching(require('../schema.json'), {path: ['areas', '*']});
191
+ */
192
+ function toRenderInputsMatching(render, schema, {
193
+ path = []
194
+ } = {}) {
195
+ const subschema = subschemaAt(schema, path);
196
+ if (!subschema) {
197
+ return {
198
+ pass: false,
199
+ message: () => `expected schema to describe configuration at ${path.join('/')}`
200
+ };
201
+ }
202
+ const inputs = recordInputs(render);
203
+ if (!inputs.length) {
204
+ return {
205
+ pass: false,
206
+ message: () => 'expected configuration editor to render inputs, but it rendered none'
207
+ };
208
+ }
209
+ const mismatches = inputs.flatMap(input => mismatchesOf(input, subschema));
210
+ return {
211
+ pass: mismatches.length === 0,
212
+ message: () => mismatches.length ? `expected rendered inputs to match schema:\n ${mismatches.join('\n ')}` : 'expected rendered inputs not to match schema'
213
+ };
214
+ }
215
+ function subschemaAt(schema, path) {
216
+ return path.reduce((current, segment) => dereference(child(current, segment), schema), dereference(schema, schema));
217
+ }
218
+ function child(schema, segment) {
219
+ var _schema$properties;
220
+ if (!schema) {
221
+ return undefined;
222
+ }
223
+ return segment === '*' ? schema.items || schema.additionalProperties : (_schema$properties = schema.properties) === null || _schema$properties === void 0 ? void 0 : _schema$properties[segment];
224
+ }
225
+ function dereference(schema, document) {
226
+ var _schema2;
227
+ const seen = [];
228
+ while (((_schema = schema) === null || _schema === void 0 ? void 0 : _schema.$ref) && !seen.includes(schema.$ref)) {
229
+ var _schema;
230
+ seen.push(schema.$ref);
231
+ schema = localTarget(document, schema.$ref);
232
+ }
233
+ return ((_schema2 = schema) === null || _schema2 === void 0 ? void 0 : _schema2.$ref) ? undefined : schema;
234
+ }
235
+ function localTarget(document, ref) {
236
+ const [otherDocumentId, pointer] = ref.split('#');
237
+ if (otherDocumentId) {
238
+ return undefined;
239
+ }
240
+ return pointer.split('/').filter(Boolean).reduce((value, segment) => value === null || value === void 0 ? void 0 : value[segment], document);
241
+ }
242
+ function recordInputs(render) {
243
+ const inputs = [];
244
+ const input = ConfigurationEditorTabView.prototype.input;
245
+ jest.spyOn(ConfigurationEditorTabView.prototype, 'input').mockImplementation(function (propertyName, view, options = {}) {
246
+ inputs.push({
247
+ propertyName,
248
+ view,
249
+ options
250
+ });
251
+ return input.call(this, propertyName, view, options);
252
+ });
253
+ try {
254
+ render();
255
+ } finally {
256
+ ConfigurationEditorTabView.prototype.input.mockRestore();
257
+ }
258
+ return inputs;
259
+ }
260
+ const checks = [fileCollectionMismatch];
261
+ function mismatchesOf({
262
+ propertyName,
263
+ view,
264
+ options
265
+ }, schema) {
266
+ const property = (schema.properties || {})[propertyName];
267
+ return checks.flatMap(check => check({
268
+ propertyName,
269
+ view,
270
+ options,
271
+ property
272
+ }) || []);
273
+ }
274
+ function fileCollectionMismatch({
275
+ propertyName,
276
+ view,
277
+ options,
278
+ property
279
+ }) {
280
+ if (!isFileInput(view) || !options.collection) {
281
+ return;
282
+ }
283
+ const declared = property && property['x-fileCollection'];
284
+ if (declared !== options.collection) {
285
+ return `${propertyName}: expected x-fileCollection ${JSON.stringify(options.collection)}, ` + `found ${JSON.stringify(declared)}`;
286
+ }
287
+ }
288
+ function isFileInput(view) {
289
+ return view === FileInputView || view.prototype instanceof FileInputView;
290
+ }
55
291
 
56
- return target;
292
+ /**
293
+ * Register the public content element matchers for the surrounding
294
+ * `describe` block: {@link toContainContentElementBox} and
295
+ * {@link toContainFitViewport}.
296
+ *
297
+ * Call inside a `describe` block. Content element plugins can use this to
298
+ * assert that their component opts into the framework chrome correctly.
299
+ *
300
+ * @example
301
+ * import {renderInContentElement, useContentElementMatchers} from 'pageflow-scrolled/testHelpers';
302
+ *
303
+ * describe('MyContentElement', () => {
304
+ * useContentElementMatchers();
305
+ *
306
+ * it('renders inside a box', () => {
307
+ * const {container} = renderInContentElement(<MyContentElement />);
308
+ * expect(container).toContainContentElementBox();
309
+ * });
310
+ * });
311
+ */
312
+ function useContentElementMatchers() {
313
+ beforeEach(() => {
314
+ expect.extend({
315
+ toContainContentElementBox,
316
+ toContainFitViewport
317
+ });
318
+ });
319
+ }
320
+
321
+ /**
322
+ * Register the configuration editor matchers for the surrounding
323
+ * `describe` block: {@link toRenderInputsMatching}.
324
+ *
325
+ * Call inside a `describe` block. Content element plugins can use this to
326
+ * assert that their configuration schema keeps up with their editor
327
+ * integration.
328
+ *
329
+ * @example
330
+ * import {
331
+ * renderContentElementConfigurationEditor, useConfigurationEditorMatchers
332
+ * } from 'pageflow-scrolled/testHelpers';
333
+ *
334
+ * describe('myContentElement/editor', () => {
335
+ * useConfigurationEditorMatchers();
336
+ *
337
+ * it('renders inputs described by schema', () => {
338
+ * expect(() => renderContentElementConfigurationEditor({entry, contentElement}))
339
+ * .toRenderInputsMatching(schema);
340
+ * });
341
+ * });
342
+ */
343
+ function useConfigurationEditorMatchers() {
344
+ beforeEach(() => {
345
+ expect.extend({
346
+ toRenderInputsMatching
347
+ });
348
+ });
57
349
  }
58
350
 
59
351
  /**
@@ -68,10 +360,16 @@ function _objectSpread2(target) {
68
360
  * @param {Object} [options.legalInfo] - imprint, copyright and privacy information of entry.
69
361
  * @param {Object} [options.themeOptions] - Options set via theme registration.
70
362
  * @param {Object} [options.themeAssets] - Paths to theme assets.
363
+ * @param {Object} [options.themeTranslations] - Pre-merged theme translations (e.g., scales).
71
364
  * @param {Object} [options.additionalSeedData] - Seed data by name.
72
365
  * @param {Array} [options.consentVendors] - Server rendered consent vendor data.
73
366
  * @param {Object} [options.contentElementConsentVendors] - Consent vendor name by content element id.
74
367
  * @param {Object} [options.entry] - attributes of entry.
368
+ * @param {Object} [options.fileModelTypes] - Mapping of file collection names to model types.
369
+ * Only needed to associate nested files with their parent. Collections of file types
370
+ * registered by plugins get a placeholder model type.
371
+ * @param {Object} [options.fileUrlTemplates] - Mapping of file collection names to mappings of
372
+ * url template names to url templates.
75
373
  * @param {Array} [options.imageFiles] - Array of objects with image file attributes of entry.
76
374
  * @param {Array} [options.videoFiles] - Array of objects with video file attributes of entry.
77
375
  * @param {Array} [options.audioFiles] - Array of objects with audio file attributes of entry.
@@ -83,75 +381,89 @@ function _objectSpread2(target) {
83
381
  * @param {Array} [options.widgets] - Array of objects with widget attributes of entry.
84
382
  * @returns {Object} - Data that resembles seed generated by server side rendering.
85
383
  */
86
- function normalizeSeed() {
87
- var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
88
- imageFileUrlTemplates = _ref.imageFileUrlTemplates,
89
- fileUrlTemplates = _ref.fileUrlTemplates,
90
- fileModelTypes = _ref.fileModelTypes,
91
- prettyUrl = _ref.prettyUrl,
92
- shareUrlTemplates = _ref.shareUrlTemplates,
93
- defaultFileRights = _ref.defaultFileRights,
94
- legalInfo = _ref.legalInfo,
95
- themeOptions = _ref.themeOptions,
96
- themeAssets = _ref.themeAssets,
97
- entry = _ref.entry,
98
- imageFiles = _ref.imageFiles,
99
- videoFiles = _ref.videoFiles,
100
- audioFiles = _ref.audioFiles,
101
- textTrackFiles = _ref.textTrackFiles,
102
- storylines = _ref.storylines,
103
- chapters = _ref.chapters,
104
- sections = _ref.sections,
105
- contentElements = _ref.contentElements,
106
- widgets = _ref.widgets,
107
- additionalSeedData = _ref.additionalSeedData,
108
- consentVendors = _ref.consentVendors,
109
- contentElementConsentVendors = _ref.contentElementConsentVendors,
110
- cutOff = _ref.cutOff,
111
- fileLicenses = _ref.fileLicenses,
112
- entryTranslations = _ref.entryTranslations;
113
- var entries = entry ? [entry] : [{}];
114
- var normalizedEntries = normalizeCollection(entries, {
384
+ function normalizeSeed({
385
+ imageFileUrlTemplates,
386
+ fileUrlTemplates,
387
+ fileModelTypes,
388
+ prettyUrl,
389
+ shareUrlTemplates,
390
+ defaultFileRights,
391
+ legalInfo,
392
+ themeOptions,
393
+ themeAssets,
394
+ themeTranslations,
395
+ entry,
396
+ imageFiles,
397
+ videoFiles,
398
+ audioFiles,
399
+ textTrackFiles,
400
+ storylines,
401
+ chapters,
402
+ sections,
403
+ contentElements,
404
+ widgets,
405
+ additionalSeedData,
406
+ consentVendors,
407
+ contentElementConsentVendors,
408
+ cutOff,
409
+ embed,
410
+ originUrl,
411
+ fileLicenses,
412
+ entryTranslations,
413
+ fileReferenceLocations,
414
+ ...customFiles
415
+ } = {}) {
416
+ const customFileCollectionNames = [...new Set([...Object.keys(customFiles).filter(name => name.endsWith('Files')), ...Object.keys(fileModelTypes || {})])].filter(name => !builtInFileCollectionNames.includes(name));
417
+ const entries = entry ? [entry] : [{}];
418
+ const normalizedEntries = normalizeCollection(entries, {
115
419
  locale: 'en',
116
420
  configuration: {}
117
421
  });
118
- var normalizedContentElements = normalizeCollection(contentElements, {
422
+ const normalizedContentElements = normalizeCollection(contentElements, {
119
423
  typeName: 'textBlock',
120
424
  configuration: {}
121
425
  });
122
- var normalizedSections = normalizeSections(sections, normalizedContentElements);
123
- var normalizedChapters = normalizeChapters(chapters, normalizedSections);
124
- var normalizedStorylines = normalizeStorylines(storylines, normalizedChapters);
426
+ const normalizedSections = normalizeSections(sections, normalizedContentElements);
427
+ const normalizedChapters = normalizeChapters(chapters, normalizedSections);
428
+ const normalizedStorylines = normalizeStorylines(storylines, normalizedChapters);
125
429
  return {
126
430
  config: {
127
- fileUrlTemplates: _objectSpread2({
128
- imageFiles: _objectSpread2({}, imageFileUrlTemplates),
431
+ fileUrlTemplates: {
432
+ imageFiles: {
433
+ ...imageFileUrlTemplates
434
+ },
129
435
  videoFiles: {},
130
436
  audioFiles: {},
131
- textTrackFiles: {}
132
- }, fileUrlTemplates),
133
- fileModelTypes: _objectSpread2({
437
+ textTrackFiles: {},
438
+ ...emptyUrlTemplates(customFileCollectionNames),
439
+ ...fileUrlTemplates
440
+ },
441
+ fileModelTypes: {
134
442
  audioFiles: 'Pageflow::AudioFile',
135
443
  imageFiles: 'Pageflow::ImageFile',
136
444
  textTrackFiles: 'Pageflow::TextTrackFile',
137
- videoFiles: 'Pageflow::VideoFile'
138
- }, fileModelTypes),
445
+ videoFiles: 'Pageflow::VideoFile',
446
+ ...placeholderModelTypes(customFileCollectionNames),
447
+ ...fileModelTypes
448
+ },
139
449
  prettyUrl: prettyUrl,
140
450
  shareUrlTemplates: normalizeShareUrlTemplates(shareUrlTemplates),
141
451
  defaultFileRights: defaultFileRights,
142
452
  legalInfo: normalizeLegalInfo(legalInfo),
143
453
  theme: normalizeTheme({
144
- themeOptions: themeOptions,
145
- themeAssets: themeAssets
454
+ themeOptions,
455
+ themeAssets,
456
+ themeTranslations
146
457
  }),
147
- additionalSeedData: additionalSeedData || {
148
- frontendVersion: 1
149
- },
458
+ additionalSeedData: additionalSeedData || {},
150
459
  consentVendors: consentVendors || [],
151
460
  contentElementConsentVendors: contentElementConsentVendors || {},
152
- cutOff: cutOff,
461
+ cutOff,
462
+ embed,
463
+ originUrl,
153
464
  fileLicenses: fileLicenses || {},
154
- entryTranslations: entryTranslations || []
465
+ entryTranslations: entryTranslations || [],
466
+ fileReferenceLocations: normalizeFileReferenceLocations(fileReferenceLocations)
155
467
  },
156
468
  collections: {
157
469
  entries: normalizedEntries,
@@ -178,6 +490,7 @@ function normalizeSeed() {
178
490
  parentFileType: null,
179
491
  configuration: {}
180
492
  }),
493
+ ...customFileCollections(customFileCollectionNames, customFiles),
181
494
  storylines: normalizedStorylines,
182
495
  chapters: normalizedChapters,
183
496
  sections: normalizedSections,
@@ -186,10 +499,25 @@ function normalizeSeed() {
186
499
  }
187
500
  };
188
501
  }
189
- function normalizeSections() {
190
- var sections = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
191
- var contentElements = arguments.length > 1 ? arguments[1] : undefined;
192
- var sectionDefaults = {
502
+ const builtInFileCollectionNames = ['imageFiles', 'videoFiles', 'audioFiles', 'textTrackFiles'];
503
+
504
+ // Model types are only compared to associate nested files with their
505
+ // parent. Specs that seed nested files need to pass the real model type
506
+ // via the fileModelTypes option.
507
+ function placeholderModelTypes(collectionNames) {
508
+ return Object.fromEntries(collectionNames.map(name => [name, name.replace(/Files$/, 'File').replace(/^./, letter => letter.toUpperCase())]));
509
+ }
510
+ function emptyUrlTemplates(collectionNames) {
511
+ return Object.fromEntries(collectionNames.map(name => [name, {}]));
512
+ }
513
+ function customFileCollections(collectionNames, customFiles) {
514
+ return Object.fromEntries(collectionNames.map(name => [name, normalizeCollection(customFiles[name], {
515
+ isReady: true,
516
+ configuration: {}
517
+ })]));
518
+ }
519
+ function normalizeSections(sections = [], contentElements) {
520
+ const sectionDefaults = {
193
521
  configuration: {
194
522
  transition: 'scroll',
195
523
  backdrop: {
@@ -198,46 +526,38 @@ function normalizeSections() {
198
526
  }
199
527
  };
200
528
  if (contentElements.length && !sections.length) {
201
- contentElements.forEach(function (contentElement) {
202
- return contentElement.sectionId = 10;
203
- });
204
- return [_objectSpread2({
529
+ contentElements.forEach(contentElement => contentElement.sectionId = 10);
530
+ return [{
205
531
  id: 10,
206
- permaId: 1
207
- }, sectionDefaults)];
532
+ permaId: 1,
533
+ ...sectionDefaults
534
+ }];
208
535
  }
209
- return normalizeCollection(sections, sectionDefaults).map(function (section) {
210
- return _objectSpread2(_objectSpread2({}, section), {}, {
211
- configuration: _objectSpread2({
212
- transition: sectionDefaults.configuration.transition
213
- }, section.configuration)
214
- });
215
- });
536
+ return normalizeCollection(sections, sectionDefaults).map(section => ({
537
+ ...section,
538
+ configuration: {
539
+ transition: sectionDefaults.configuration.transition,
540
+ ...section.configuration
541
+ }
542
+ }));
216
543
  }
217
- function normalizeChapters() {
218
- var chapters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
219
- var sections = arguments.length > 1 ? arguments[1] : undefined;
220
- var chapterDefaults = {
544
+ function normalizeChapters(chapters = [], sections) {
545
+ const chapterDefaults = {
221
546
  configuration: {}
222
547
  };
223
548
  if (sections.length && !chapters.length) {
224
- sections.forEach(function (section) {
225
- return section.chapterId = 100;
226
- });
227
- return [_objectSpread2({
549
+ sections.forEach(section => section.chapterId = 100);
550
+ return [{
228
551
  id: 100,
229
- permaId: 10
230
- }, chapterDefaults)];
552
+ permaId: 10,
553
+ ...chapterDefaults
554
+ }];
231
555
  }
232
556
  return normalizeCollection(chapters, chapterDefaults);
233
557
  }
234
- function normalizeStorylines() {
235
- var storylines = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
236
- var chapters = arguments.length > 1 ? arguments[1] : undefined;
558
+ function normalizeStorylines(storylines = [], chapters) {
237
559
  if (!storylines.length) {
238
- chapters.forEach(function (chapter) {
239
- return chapter.storylineId = 1000;
240
- });
560
+ chapters.forEach(chapter => chapter.storylineId = 1000);
241
561
  return [{
242
562
  id: 1000,
243
563
  permaId: 100,
@@ -285,132 +605,75 @@ function normalizeLegalInfo(legalInfo) {
285
605
  };
286
606
  }
287
607
  }
288
- function normalizeTheme(_ref2) {
289
- var themeAssets = _ref2.themeAssets,
290
- themeOptions = _ref2.themeOptions;
608
+ function normalizeTheme({
609
+ themeAssets,
610
+ themeOptions,
611
+ themeTranslations
612
+ }) {
291
613
  return {
292
- assets: _objectSpread2({
293
- icons: {}
294
- }, themeAssets),
295
- options: _objectSpread2({
296
- colors: {}
297
- }, themeOptions)
614
+ assets: {
615
+ icons: {},
616
+ ...themeAssets
617
+ },
618
+ options: {
619
+ colors: {},
620
+ ...themeOptions
621
+ },
622
+ translations: themeTranslations
298
623
  };
299
624
  }
300
- function normalizeWidgets() {
301
- var widgets = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
302
- return widgets.map(function (widget) {
303
- return _objectSpread2({
304
- permaId: widget.role,
305
- configuration: {}
306
- }, widget);
307
- });
308
- }
309
- function normalizeCollection() {
310
- var collection = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
311
- var defaults = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
312
- return collection.map(function (item, index) {
313
- return _objectSpread2(_objectSpread2({
314
- id: index + 1,
315
- permaId: index + 1
316
- }, defaults), item);
317
- });
318
- }
319
-
320
- function _objectWithoutPropertiesLoose(source, excluded) {
321
- if (source == null) return {};
322
- var target = {};
323
- var sourceKeys = Object.keys(source);
324
- var key, i;
325
-
326
- for (i = 0; i < sourceKeys.length; i++) {
327
- key = sourceKeys[i];
328
- if (excluded.indexOf(key) >= 0) continue;
329
- target[key] = source[key];
330
- }
331
-
332
- return target;
333
- }
334
-
335
- function _objectWithoutProperties(source, excluded) {
336
- if (source == null) return {};
337
- var target = _objectWithoutPropertiesLoose(source, excluded);
338
- var key, i;
339
-
340
- if (Object.getOwnPropertySymbols) {
341
- var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
342
-
343
- for (i = 0; i < sourceSymbolKeys.length; i++) {
344
- key = sourceSymbolKeys[i];
345
- if (excluded.indexOf(key) >= 0) continue;
346
- if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
347
- target[key] = source[key];
348
- }
349
- }
350
-
351
- return target;
352
- }
353
-
354
- function _arrayWithHoles(arr) {
355
- if (Array.isArray(arr)) return arr;
625
+ function normalizeWidgets(widgets = []) {
626
+ return widgets.map(widget => ({
627
+ permaId: widget.role,
628
+ configuration: {},
629
+ ...widget
630
+ }));
356
631
  }
357
-
358
- function _iterableToArrayLimit(arr, i) {
359
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
360
- var _arr = [];
361
- var _n = true;
362
- var _d = false;
363
- var _e = undefined;
364
-
365
- try {
366
- for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
367
- _arr.push(_s.value);
368
-
369
- if (i && _arr.length === i) break;
370
- }
371
- } catch (err) {
372
- _d = true;
373
- _e = err;
374
- } finally {
375
- try {
376
- if (!_n && _i["return"] != null) _i["return"]();
377
- } finally {
378
- if (_d) throw _e;
379
- }
380
- }
381
-
382
- return _arr;
632
+ function normalizeCollection(collection = [], defaults = {}) {
633
+ return collection.map((item, index) => ({
634
+ id: index + 1,
635
+ permaId: index + 1,
636
+ ...defaults,
637
+ ...item
638
+ }));
383
639
  }
384
-
385
- function _arrayLikeToArray(arr, len) {
386
- if (len == null || len > arr.length) len = arr.length;
387
-
388
- for (var i = 0, arr2 = new Array(len); i < len; i++) {
389
- arr2[i] = arr[i];
390
- }
391
-
392
- return arr2;
393
- }
394
-
395
- function _unsupportedIterableToArray(o, minLen) {
396
- if (!o) return;
397
- if (typeof o === "string") return _arrayLikeToArray(o, minLen);
398
- var n = Object.prototype.toString.call(o).slice(8, -1);
399
- if (n === "Object" && o.constructor) n = o.constructor.name;
400
- if (n === "Map" || n === "Set") return Array.from(n);
401
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
402
- }
403
-
404
- function _nonIterableRest() {
405
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
640
+ function normalizeFileReferenceLocations(fileReferenceLocations) {
641
+ return {
642
+ contentElements: {},
643
+ sections: [],
644
+ entry: [],
645
+ ...fileReferenceLocations
646
+ };
406
647
  }
407
648
 
408
- function _slicedToArray(arr, i) {
409
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
649
+ /**
650
+ * Render the configuration editor of a content element. Content element
651
+ * packs can use this to assert how their editor integration behaves.
652
+ *
653
+ * @param {Object} options
654
+ * @param {Object} options.entry - Entry the content element belongs to.
655
+ * @param {Object} options.contentElement - Content element to configure.
656
+ *
657
+ * @example
658
+ * import {renderContentElementConfigurationEditor} from 'pageflow-scrolled/testHelpers';
659
+ */
660
+ function renderContentElementConfigurationEditor({
661
+ entry,
662
+ contentElement
663
+ }) {
664
+ // Normally contributed by the text inline file rights widget type
665
+ // once the widgets of the entry have been set up.
666
+ ConfigurationEditorTabView.groups.define('ContentElementInlineFileRightsSettings', () => {});
667
+ const view = new EditContentElementView({
668
+ model: contentElement,
669
+ editor,
670
+ entry
671
+ });
672
+ renderBackboneView(view);
673
+ return ConfigurationEditor.find(view);
410
674
  }
411
675
 
412
- var _excluded = ["seed", "setup", "wrapper", "consent"],
413
- _excluded2 = ["seed", "setup", "wrapper"];
676
+ var _jsxFileName = "/home/tfischbach/code/pageflow/entry_types/scrolled/package/src/testHelpers/rendering.js";
414
677
 
415
678
  /**
416
679
  * Render a component that depends on entry state. Accepts all options
@@ -448,17 +711,18 @@ var _excluded = ["seed", "setup", "wrapper", "consent"],
448
711
  * function. The normalized seed constructed from the `seed` option
449
712
  * is passed as a second parameter.
450
713
  */
451
- function renderInEntry(ui) {
452
- var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
453
- seed = _ref.seed,
454
- setup = _ref.setup,
455
- wrapper = _ref.wrapper,
456
- _ref$consent = _ref.consent,
457
- consent = _ref$consent === void 0 ? Consent.create() : _ref$consent,
458
- options = _objectWithoutProperties(_ref, _excluded);
459
- options = _objectSpread2({
460
- wrapper: createWrapper(seed, setup, wrapper, consent)
461
- }, options);
714
+ function renderInEntry(ui, {
715
+ seed,
716
+ setup,
717
+ wrapper,
718
+ consent = Consent.create(),
719
+ phonePlatform,
720
+ ...options
721
+ } = {}) {
722
+ options = {
723
+ wrapper: createWrapper(seed, setup, wrapper, consent, phonePlatform),
724
+ ...options
725
+ };
462
726
  if (typeof ui === 'function') {
463
727
  // Evaluate `ui` inside a React component to allow using hooks in
464
728
  // the test. We also could have used `ui` as a React component
@@ -473,20 +737,34 @@ function renderInEntry(ui) {
473
737
  // render call and mount a new one. We therefore define a single
474
738
  // component that is reused across rerenders to ensure
475
739
  // `MyComponent` stays mounted and just receives new props.
476
- var HooksWrapper = function HooksWrapper(_ref2) {
477
- var ui = _ref2.ui;
740
+ function HooksWrapper({
741
+ ui
742
+ }) {
478
743
  return ui();
479
- };
480
- var result = render( /*#__PURE__*/React.createElement(HooksWrapper, {
481
- ui: ui
744
+ }
745
+ const result = render( /*#__PURE__*/React.createElement(HooksWrapper, {
746
+ ui: ui,
747
+ __self: this,
748
+ __source: {
749
+ fileName: _jsxFileName,
750
+ lineNumber: 71,
751
+ columnNumber: 27
752
+ }
482
753
  }), options);
483
- return _objectSpread2(_objectSpread2({}, result), {}, {
484
- rerender: function rerender(ui) {
754
+ return {
755
+ ...result,
756
+ rerender(ui) {
485
757
  result.rerender( /*#__PURE__*/React.createElement(HooksWrapper, {
486
- ui: ui
758
+ ui: ui,
759
+ __self: this,
760
+ __source: {
761
+ fileName: _jsxFileName,
762
+ lineNumber: 76,
763
+ columnNumber: 25
764
+ }
487
765
  }));
488
766
  }
489
- });
767
+ };
490
768
  } else {
491
769
  return render(ui, options);
492
770
  }
@@ -505,39 +783,75 @@ function renderInEntry(ui) {
505
783
  * @param {Object} [options.seed] - Seed data for entry state. Passed through {@link normalizeSeed}.
506
784
  * @param {Function} [options.setup] - See {@link renderInEntry}.
507
785
  */
508
- function renderHookInEntry(callback) {
509
- var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
510
- seed = _ref3.seed,
511
- setup = _ref3.setup,
512
- wrapper = _ref3.wrapper,
513
- options = _objectWithoutProperties(_ref3, _excluded2);
514
- return renderHook(callback, _objectSpread2({
515
- wrapper: createWrapper(seed, setup, wrapper)
516
- }, options));
517
- }
518
- function createWrapper(seed, setup, originalWrapper, consent) {
519
- var normalizedSeed = normalizeSeed(seed);
520
- var OriginalWrapper = originalWrapper || function Noop(_ref4) {
521
- var children = _ref4.children;
786
+ function renderHookInEntry(callback, {
787
+ seed,
788
+ setup,
789
+ wrapper,
790
+ phonePlatform,
791
+ ...options
792
+ } = {}) {
793
+ return renderHook(callback, {
794
+ wrapper: createWrapper(seed, setup, wrapper, undefined, phonePlatform),
795
+ ...options
796
+ });
797
+ }
798
+ function createWrapper(seed, setup, originalWrapper, consent, phonePlatform) {
799
+ const normalizedSeed = normalizeSeed(seed);
800
+ const OriginalWrapper = originalWrapper || function Noop({
801
+ children
802
+ }) {
522
803
  return children;
523
804
  };
524
- return function Wrapper(_ref5) {
525
- var children = _ref5.children;
805
+ return function Wrapper({
806
+ children
807
+ }) {
808
+ let content = /*#__PURE__*/React.createElement(Dispatcher, {
809
+ callback: setup,
810
+ seed: normalizedSeed,
811
+ __self: this,
812
+ __source: {
813
+ fileName: _jsxFileName,
814
+ lineNumber: 112,
815
+ columnNumber: 7
816
+ }
817
+ }, /*#__PURE__*/React.createElement(OriginalWrapper, {
818
+ __self: this,
819
+ __source: {
820
+ fileName: _jsxFileName,
821
+ lineNumber: 113,
822
+ columnNumber: 9
823
+ }
824
+ }, children));
825
+ if (phonePlatform !== undefined) {
826
+ content = /*#__PURE__*/React.createElement(PhonePlatformContext.Provider, {
827
+ value: phonePlatform,
828
+ __self: this,
829
+ __source: {
830
+ fileName: _jsxFileName,
831
+ lineNumber: 121,
832
+ columnNumber: 9
833
+ }
834
+ }, content);
835
+ }
526
836
  return /*#__PURE__*/React.createElement(RootProviders, {
527
837
  seed: normalizedSeed,
528
- consent: consent
529
- }, /*#__PURE__*/React.createElement(Dispatcher, {
530
- callback: setup,
531
- seed: normalizedSeed
532
- }, /*#__PURE__*/React.createElement(OriginalWrapper, null, children)));
838
+ consent: consent,
839
+ __self: this,
840
+ __source: {
841
+ fileName: _jsxFileName,
842
+ lineNumber: 128,
843
+ columnNumber: 7
844
+ }
845
+ }, content);
533
846
  };
534
847
  }
535
- function Dispatcher(_ref6) {
536
- var children = _ref6.children,
537
- seed = _ref6.seed,
538
- callback = _ref6.callback;
539
- var dispatch = useEntryStateDispatch();
540
- useEffect(function () {
848
+ function Dispatcher({
849
+ children,
850
+ seed,
851
+ callback
852
+ }) {
853
+ const dispatch = useEntryStateDispatch();
854
+ useEffect(() => {
541
855
  if (callback) {
542
856
  callback(dispatch, seed);
543
857
  }
@@ -545,7 +859,7 @@ function Dispatcher(_ref6) {
545
859
  return children;
546
860
  }
547
861
 
548
- var _excluded$1 = ["lifecycleContext", "wrapper"];
862
+ var _jsxFileName$1 = "/home/tfischbach/code/pageflow/entry_types/scrolled/package/src/testHelpers/scrollPositionLifecycle.js";
549
863
 
550
864
  /**
551
865
  * Takes the same options as {@link renderInEntry} but returns
@@ -557,37 +871,38 @@ var _excluded$1 = ["lifecycleContext", "wrapper"];
557
871
  * // => Turns `shouldLoad` and `shouldPrepare` to true
558
872
  */
559
873
  function renderInEntryWithContentElementLifecycle(ui, options) {
560
- return renderInEntryWithScrollPositionLifecycle(ui, _objectSpread2({
561
- lifecycleContext: ContentElementLifecycleContext
562
- }, options));
563
- }
564
- function renderInEntryWithScrollPositionLifecycle(ui) {
565
- var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
566
- lifecycleContext = _ref.lifecycleContext,
567
- wrapper = _ref.wrapper,
568
- options = _objectWithoutProperties(_ref, _excluded$1);
569
- var emitter = createEmitter();
570
- return withSimulateScrollPositionHelper(emitter, renderInEntry(ui, _objectSpread2({
571
- wrapper: createScrollPositionProvider(lifecycleContext, emitter, wrapper)
572
- }, options)));
874
+ return renderInEntryWithScrollPositionLifecycle(ui, {
875
+ lifecycleContext: ContentElementLifecycleContext,
876
+ ...options
877
+ });
878
+ }
879
+ function renderInEntryWithScrollPositionLifecycle(ui, {
880
+ lifecycleContext,
881
+ wrapper,
882
+ ...options
883
+ } = {}) {
884
+ const emitter = createEmitter();
885
+ return withSimulateScrollPositionHelper(emitter, renderInEntry(ui, {
886
+ wrapper: createScrollPositionProvider(lifecycleContext, emitter, wrapper),
887
+ ...options
888
+ }));
573
889
  }
574
890
  function createScrollPositionProvider(Context, emitter, originalWrapper) {
575
- var OriginalWrapper = originalWrapper || function Noop(_ref2) {
576
- var children = _ref2.children;
891
+ const OriginalWrapper = originalWrapper || function Noop({
892
+ children
893
+ }) {
577
894
  return children;
578
895
  };
579
- return function ScrollPositionProvider(_ref3) {
580
- var children = _ref3.children;
581
- var _useState = useState({
582
- shouldLoad: false,
583
- shouldPrepare: false,
584
- isVisible: false,
585
- isActive: false
586
- }),
587
- _useState2 = _slicedToArray(_useState, 2),
588
- value = _useState2[0],
589
- setValue = _useState2[1];
590
- useEffect(function () {
896
+ return function ScrollPositionProvider({
897
+ children
898
+ }) {
899
+ const [value, setValue] = useState({
900
+ shouldLoad: false,
901
+ shouldPrepare: false,
902
+ isVisible: false,
903
+ isActive: false
904
+ });
905
+ useEffect(() => {
591
906
  function handle(scrollPosition) {
592
907
  switch (scrollPosition) {
593
908
  case 'near viewport':
@@ -623,89 +938,251 @@ function createScrollPositionProvider(Context, emitter, originalWrapper) {
623
938
  }
624
939
  }
625
940
  emitter.on('scroll', handle);
626
- return function () {
627
- return emitter.off('scroll', handle);
628
- };
941
+ return () => emitter.off('scroll', handle);
629
942
  });
630
- return /*#__PURE__*/React.createElement(OriginalWrapper, null, /*#__PURE__*/React.createElement(Context.Provider, {
631
- value: value
943
+ return /*#__PURE__*/React.createElement(OriginalWrapper, {
944
+ __self: this,
945
+ __source: {
946
+ fileName: _jsxFileName$1,
947
+ lineNumber: 69,
948
+ columnNumber: 7
949
+ }
950
+ }, /*#__PURE__*/React.createElement(Context.Provider, {
951
+ value: value,
952
+ __self: this,
953
+ __source: {
954
+ fileName: _jsxFileName$1,
955
+ lineNumber: 70,
956
+ columnNumber: 9
957
+ }
632
958
  }, children));
633
959
  };
634
960
  }
635
- var allowedScrollPositions = ['outside viewport', 'near viewport', 'in viewport', 'center of viewport'];
961
+ const allowedScrollPositions = ['outside viewport', 'near viewport', 'in viewport', 'center of viewport'];
636
962
  function withSimulateScrollPositionHelper(emitter, result) {
637
- return _objectSpread2(_objectSpread2({}, result), {}, {
638
- simulateScrollPosition: function simulateScrollPosition(scrollPosition) {
963
+ return {
964
+ ...result,
965
+ simulateScrollPosition(scrollPosition) {
639
966
  if (!allowedScrollPositions.includes(scrollPosition)) {
640
- throw new Error("Invalid scrollPosition '".concat(scrollPosition, "'. ") + "Allowed values: ".concat(allowedScrollPositions.join(', ')));
967
+ throw new Error(`Invalid scrollPosition '${scrollPosition}'. ` + `Allowed values: ${allowedScrollPositions.join(', ')}`);
641
968
  }
642
- act(function () {
969
+ act(() => {
643
970
  emitter.trigger('scroll', scrollPosition);
644
971
  });
645
972
  }
646
- });
973
+ };
647
974
  }
648
975
  function createEmitter() {
649
- return _objectSpread2({}, BackboneEvents);
976
+ return {
977
+ ...BackboneEvents
978
+ };
650
979
  }
651
980
 
652
- var _excluded$2 = ["editorState", "phonePlatform", "wrapper"];
981
+ var _jsxFileName$2 = "/home/tfischbach/code/pageflow/entry_types/scrolled/package/src/testHelpers/renderInContentElement.js";
653
982
 
654
983
  /**
655
984
  * Provide context as if component was rendered inside of a content element.
656
985
  *
657
- * Returns two additionals functions to control content element scroll
658
- * lifecycle and editor commands: `simulateScrollPosition` and `triggerEditorCommand`.
986
+ * Returns additional functions to control content element scroll
987
+ * lifecycle, view timeline progress, editor commands, and storyline
988
+ * mode: `simulateScrollPosition`, `simulateScrollProgress`,
989
+ * `triggerEditorCommand`, and `simulateStorylineMode`.
990
+ *
991
+ * `simulateScrollProgress` passes the given progress to all
992
+ * `useContentElementViewTimelineProgress` callbacks, no matter which
993
+ * range they observe. Pass a `range` option to only invoke callbacks
994
+ * observing that range.
659
995
  *
660
996
  * @param {Function} callback - React component or function returning a React component.
661
997
  * @param {Object} [options] - Supports all options supported by {@link `renderInEntry`}.
662
- * @param {Object} [options.editorState] - Fake result of `useContentElementEditorState`.
998
+ * @param {boolean|Object} [options.inlineEditing] -
999
+ * Opt the content element into an inline-editing context (matching
1000
+ * what's available when inline editing is loaded in production).
1001
+ * Pass `true` for editable defaults, or an object to override:
1002
+ * `{isEditable, isSelected, transientState}`. `transientState` is
1003
+ * installed as the `setTransientState` function on the editor state
1004
+ * context — pass a jest spy to assert on it. Omitting the option
1005
+ * gives the frontend-mode defaults (`isEditable: false, isSelected:
1006
+ * false, setTransientState: noop`) without wrapping any
1007
+ * inline-editing-only providers (DndProvider, editor command
1008
+ * emitter).
663
1009
  * @param {Object} [options.phonePlatform] - Fake result of `usePhonePlatform`.
1010
+ * @param {Object} [options.consentState] - Pass 'undecided' to render third party consent opt in.
664
1011
  *
665
1012
  * @example
666
1013
  *
667
- * const {getByRole, simulateScrollPosition, triggerEditorCommand} =
1014
+ * const {getByRole, simulateScrollPosition, triggerEditorCommand, simulateStorylineMode} =
668
1015
  * renderInContentElement(<MyContentElement />, {
669
- * seed: {...}
1016
+ * seed: {...},
1017
+ * inlineEditing: {isSelected: true}
670
1018
  * });
671
1019
  * simulateScrollPosition('near viewport');
1020
+ * simulateScrollProgress(0.5);
1021
+ * simulateScrollProgress(0.5, {range: 'pinned'});
672
1022
  * triggerEditorCommand({type: 'HIGHLIGHT'});
1023
+ * simulateStorylineMode('background');
673
1024
  */
674
- function renderInContentElement(ui) {
675
- var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
676
- editorState = _ref.editorState,
677
- _ref$phonePlatform = _ref.phonePlatform,
678
- phonePlatform = _ref$phonePlatform === void 0 ? false : _ref$phonePlatform,
679
- OriginalWrapper = _ref.wrapper,
680
- options = _objectWithoutProperties(_ref, _excluded$2);
681
- var emitter = Object.assign({}, BackboneEvents);
682
- function Wrapper(_ref2) {
683
- var children = _ref2.children;
684
- var defaultEditorState = useContext(ContentElementEditorStateContext);
685
- return /*#__PURE__*/React.createElement(PhonePlatformContext.Provider, {
686
- value: phonePlatform
687
- }, /*#__PURE__*/React.createElement(DndProvider, {
688
- backend: HTML5Backend
1025
+ function renderInContentElement(ui, {
1026
+ inlineEditing,
1027
+ phonePlatform = false,
1028
+ wrapper: OriginalWrapper,
1029
+ consentState = 'accepted',
1030
+ seed,
1031
+ ...options
1032
+ } = {}) {
1033
+ const emitter = Object.assign({}, BackboneEvents);
1034
+ const storylineEmitter = Object.assign({}, BackboneEvents);
1035
+ const viewTimelineEmitter = Object.assign({}, BackboneEvents);
1036
+ const viewTimeline = {
1037
+ subscribe(range, callback) {
1038
+ function handleProgress(progress, options) {
1039
+ if (!options.range || options.range === range) {
1040
+ callback(progress);
1041
+ }
1042
+ }
1043
+ viewTimelineEmitter.on('progress', handleProgress);
1044
+ return () => viewTimelineEmitter.off('progress', handleProgress);
1045
+ }
1046
+ };
1047
+ const inlineEditingConfig = resolveInlineEditing(inlineEditing);
1048
+ function Wrapper({
1049
+ children
1050
+ }) {
1051
+ const [storylineMode, setStorylineMode] = useState('active');
1052
+ useEffect(() => {
1053
+ storylineEmitter.on('storylineMode', setStorylineMode);
1054
+ return () => storylineEmitter.off('storylineMode', setStorylineMode);
1055
+ }, []);
1056
+ let tree = OriginalWrapper ? /*#__PURE__*/React.createElement(OriginalWrapper, {
1057
+ children: children,
1058
+ __self: this,
1059
+ __source: {
1060
+ fileName: _jsxFileName$2,
1061
+ lineNumber: 95,
1062
+ columnNumber: 34
1063
+ }
1064
+ }) : children;
1065
+ if (inlineEditingConfig) {
1066
+ tree = /*#__PURE__*/React.createElement(DndProvider, {
1067
+ backend: HTML5Backend,
1068
+ __self: this,
1069
+ __source: {
1070
+ fileName: _jsxFileName$2,
1071
+ lineNumber: 99,
1072
+ columnNumber: 9
1073
+ }
1074
+ }, /*#__PURE__*/React.createElement(ContentElementEditorCommandEmitterContext.Provider, {
1075
+ value: emitter,
1076
+ __self: this,
1077
+ __source: {
1078
+ fileName: _jsxFileName$2,
1079
+ lineNumber: 100,
1080
+ columnNumber: 11
1081
+ }
1082
+ }, /*#__PURE__*/React.createElement(ContentElementEditorStateContext.Provider, {
1083
+ value: inlineEditingConfig,
1084
+ __self: this,
1085
+ __source: {
1086
+ fileName: _jsxFileName$2,
1087
+ lineNumber: 101,
1088
+ columnNumber: 13
1089
+ }
1090
+ }, tree)));
1091
+ }
1092
+ return /*#__PURE__*/React.createElement(MainStorylineActivity, {
1093
+ activeExcursion: storylineMode !== 'active' ? {
1094
+ id: 1
1095
+ } : null,
1096
+ __self: this,
1097
+ __source: {
1098
+ fileName: _jsxFileName$2,
1099
+ lineNumber: 110,
1100
+ columnNumber: 7
1101
+ }
689
1102
  }, /*#__PURE__*/React.createElement(ContentElementAttributesProvider, {
690
- id: 42
691
- }, /*#__PURE__*/React.createElement(ContentElementEditorCommandEmitterContext.Provider, {
692
- value: emitter
693
- }, /*#__PURE__*/React.createElement(ContentElementEditorStateContext.Provider, {
694
- value: _objectSpread2(_objectSpread2({}, defaultEditorState), editorState)
695
- }, OriginalWrapper ? /*#__PURE__*/React.createElement(OriginalWrapper, {
696
- children: children
697
- }) : children)))));
1103
+ id: 42,
1104
+ __self: this,
1105
+ __source: {
1106
+ fileName: _jsxFileName$2,
1107
+ lineNumber: 111,
1108
+ columnNumber: 9
1109
+ }
1110
+ }, /*#__PURE__*/React.createElement(ContentElementViewTimelineContext.Provider, {
1111
+ value: viewTimeline,
1112
+ __self: this,
1113
+ __source: {
1114
+ fileName: _jsxFileName$2,
1115
+ lineNumber: 112,
1116
+ columnNumber: 11
1117
+ }
1118
+ }, tree)));
698
1119
  }
699
- return _objectSpread2(_objectSpread2({}, renderInEntryWithScrollPositionLifecycle(ui, _objectSpread2({
700
- lifecycleContext: ContentElementLifecycleContext,
701
- wrapper: Wrapper
702
- }, options))), {}, {
703
- triggerEditorCommand: function triggerEditorCommand(command) {
704
- act(function () {
705
- emitter.trigger("command:42", command);
1120
+ return {
1121
+ ...renderInEntryWithScrollPositionLifecycle(ui, {
1122
+ lifecycleContext: ContentElementLifecycleContext,
1123
+ wrapper: Wrapper,
1124
+ consent: createConsent(consentState),
1125
+ phonePlatform,
1126
+ seed: {
1127
+ ...consentSeed,
1128
+ ...seed
1129
+ },
1130
+ ...options
1131
+ }),
1132
+ triggerEditorCommand(command) {
1133
+ act(() => {
1134
+ emitter.trigger(`command:42`, command);
1135
+ });
1136
+ },
1137
+ simulateStorylineMode(mode) {
1138
+ act(() => {
1139
+ storylineEmitter.trigger('storylineMode', mode);
1140
+ });
1141
+ },
1142
+ simulateScrollProgress(progress, {
1143
+ range
1144
+ } = {}) {
1145
+ act(() => {
1146
+ viewTimelineEmitter.trigger('progress', progress, {
1147
+ range
1148
+ });
706
1149
  });
707
1150
  }
1151
+ };
1152
+ }
1153
+ function resolveInlineEditing(option) {
1154
+ var _overrides$isEditable, _overrides$isSelected, _overrides$transientS;
1155
+ if (!option) return null;
1156
+ const overrides = option === true ? {} : option;
1157
+ return {
1158
+ isEditable: (_overrides$isEditable = overrides.isEditable) !== null && _overrides$isEditable !== void 0 ? _overrides$isEditable : true,
1159
+ isSelected: (_overrides$isSelected = overrides.isSelected) !== null && _overrides$isSelected !== void 0 ? _overrides$isSelected : false,
1160
+ setTransientState: (_overrides$transientS = overrides.transientState) !== null && _overrides$transientS !== void 0 ? _overrides$transientS : () => {},
1161
+ select: () => {},
1162
+ selectNewThread: () => {}
1163
+ };
1164
+ }
1165
+ function createConsent(consentState) {
1166
+ const consent = Consent.create();
1167
+ consent.registerVendor('testVendor', {
1168
+ displayName: 'Test Vendor',
1169
+ description: 'Test embeds',
1170
+ paradigm: 'lazy opt-in'
708
1171
  });
1172
+ consent.closeVendorRegistration();
1173
+ if (consentState === 'accepted') {
1174
+ consent.accept('testVendor');
1175
+ }
1176
+ return consent;
709
1177
  }
1178
+ const consentSeed = {
1179
+ consentVendors: [{
1180
+ name: 'testVendor',
1181
+ paradigm: 'lazy opt-in'
1182
+ }],
1183
+ contentElementConsentVendors: {
1184
+ 42: 'testVendor'
1185
+ }
1186
+ };
710
1187
 
711
- export { normalizeSeed, renderHookInEntry, renderInContentElement, renderInEntry, renderInEntryWithContentElementLifecycle, renderInEntryWithScrollPositionLifecycle };
1188
+ export { normalizeSeed, renderContentElementConfigurationEditor, renderHookInEntry, renderInContentElement, renderInEntry, renderInEntryWithContentElementLifecycle, renderInEntryWithScrollPositionLifecycle, useConfigurationEditorMatchers, useContentElementMatchers };