pageflow 15.1.0 → 15.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pageflow might be problematic. Click here for more details.

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -161
  3. data/README.md +1 -2
  4. data/admins/pageflow/accounts.rb +94 -19
  5. data/app/assets/javascripts/pageflow/admin/accounts.js +3 -3
  6. data/app/assets/javascripts/pageflow/dist/frontend.js +2 -0
  7. data/app/assets/javascripts/pageflow/dist/ui.js +9 -0
  8. data/app/assets/stylesheets/pageflow/themes/default/logo/variant/background_image.scss +4 -0
  9. data/app/assets/stylesheets/pageflow/themes/default/logo/variant/watermark.scss +5 -0
  10. data/app/controllers/pageflow/editor/widgets_controller.rb +1 -1
  11. data/app/helpers/pageflow/pages_helper.rb +1 -0
  12. data/app/models/pageflow/account.rb +6 -0
  13. data/app/models/pageflow/draft_entry.rb +13 -3
  14. data/app/models/pageflow/entry.rb +8 -1
  15. data/app/models/pageflow/entry_template.rb +55 -0
  16. data/app/models/pageflow/published_entry.rb +13 -3
  17. data/app/models/pageflow/revision.rb +1 -1
  18. data/app/models/pageflow/theming.rb +8 -47
  19. data/app/policies/pageflow/entry_template_policy.rb +18 -0
  20. data/app/policies/pageflow/theming_policy.rb +0 -4
  21. data/app/views/admin/accounts/_configuration_label.html.erb +5 -0
  22. data/app/views/admin/accounts/_entry_template_details.html.arb +17 -0
  23. data/app/views/admin/accounts/_form.html.erb +43 -23
  24. data/app/views/admin/accounts/_share_providers_label.html.erb +5 -0
  25. data/app/views/admin/accounts/_theming_details.html.arb +0 -12
  26. data/app/views/pageflow/themes/_theme.json.jbuilder +1 -0
  27. data/config/locales/de.yml +12 -7
  28. data/config/locales/en.yml +12 -7
  29. data/db/migrate/20200122115400_create_pageflow_entry_templates.rb +75 -0
  30. data/db/migrate/20200206134400_convert_legacy_scrolled_content_element_types.rb +48 -0
  31. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +42 -4
  32. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +1 -0
  33. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +4 -1
  34. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +2 -1
  35. data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +35 -0
  36. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_seed.json.jbuilder +1 -1
  37. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +14 -2
  38. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +10 -1
  39. data/entry_types/scrolled/config/locales/new/de.yml +231 -8
  40. data/entry_types/scrolled/config/locales/new/en.yml +228 -10
  41. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +3 -0
  42. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +9 -4
  43. data/entry_types/scrolled/lib/tasks/pageflow_scrolled_tasks.rake +96 -0
  44. data/entry_types/scrolled/package/contentElements-editor.js +410 -0
  45. data/entry_types/scrolled/package/contentElements-frontend.js +533 -0
  46. data/entry_types/scrolled/package/editor.js +498 -2561
  47. data/entry_types/scrolled/package/frontend.js +713 -1238
  48. data/entry_types/scrolled/package/package.json +12 -2
  49. data/lib/pageflow/ability_mixin.rb +3 -2
  50. data/lib/pageflow/seeds.rb +0 -2
  51. data/lib/pageflow/theme.rb +4 -0
  52. data/lib/pageflow/themes.rb +5 -1
  53. data/lib/pageflow/version.rb +1 -1
  54. data/packages/pageflow/config/jest/index.js +0 -1
  55. data/packages/pageflow/config/jest/transformers/jst.js +1 -1
  56. data/packages/pageflow/config/webpack.js +0 -1
  57. data/packages/pageflow/editor.js +33 -4
  58. data/packages/pageflow/package.json +2 -1
  59. data/packages/pageflow/testHelpers.js +367 -4
  60. data/packages/pageflow/ui.js +9 -0
  61. data/spec/factories/accounts.rb +6 -0
  62. data/spec/factories/entry_templates.rb +8 -0
  63. data/spec/factories/published_entries.rb +3 -1
  64. data/spec/factories/themings.rb +0 -1
  65. metadata +14 -4
  66. data/app/assets/javascripts/pageflow/dist/editor.js +0 -11881
  67. data/app/assets/javascripts/pageflow/dist/index.js +0 -3
@@ -7,14 +7,20 @@
7
7
  "author": "Codevise Solutions GmbH <info@codevise.de>",
8
8
  "license": "MIT",
9
9
  "dependencies": {
10
+ "prop-types": "^15.7.2",
10
11
  "react": "^16.9.0",
11
12
  "react-dom": "^16.9.0",
12
- "react-tooltip": "^3.11.1"
13
+ "react-player": "^1.15.2",
14
+ "react-tooltip": "^3.11.1",
15
+ "react-compare-image": "^2.0.4"
13
16
  },
14
17
  "peerDependencies": {
15
18
  "pageflow": "15.1.0"
16
19
  },
17
20
  "devDependencies": {
21
+ "@percy/storybook": "^3.2.0",
22
+ "@storybook/addon-viewport": "^5.3.13",
23
+ "@storybook/react": "^5.3.9",
18
24
  "@testing-library/jest-dom": "^4.2.4",
19
25
  "@testing-library/react": "^9.4.0",
20
26
  "@testing-library/react-hooks": "^3.2.1",
@@ -22,6 +28,7 @@
22
28
  "@typescript-eslint/parser": "2.x",
23
29
  "babel-eslint": "10.x",
24
30
  "babel-jest": "^24.9.0",
31
+ "babel-loader": "^8.0.6",
25
32
  "eslint": "6.x",
26
33
  "eslint-config-react-app": "^5.1.0",
27
34
  "eslint-import-resolver-jest": "^3.0.0",
@@ -38,6 +45,9 @@
38
45
  },
39
46
  "scripts": {
40
47
  "test": "jest",
41
- "lint": "eslint ."
48
+ "lint": "eslint .",
49
+ "start-storybook": "start-storybook",
50
+ "build-storybook": "build-storybook -o .storybook/out",
51
+ "snapshot": "STORYBOOK_SPLIT=true build-storybook --quiet -o .storybook/out && PERCY_TOKEN=${PERCY_TOKEN:-$PT} percy-storybook --widths=1280 --build_dir=.storybook/out"
42
52
  }
43
53
  }
@@ -214,8 +214,8 @@ module Pageflow
214
214
  ThemingPolicy.new(user, theming).edit?
215
215
  end
216
216
 
217
- can :index_widgets_for, Theming do |theming|
218
- ThemingPolicy.new(user, theming).index_widgets_for?
217
+ can :edit, EntryTemplate do |entry_template|
218
+ EntryTemplatePolicy.new(user, entry_template).edit?
219
219
  end
220
220
 
221
221
  can :create, ::User do |managed_user|
@@ -260,6 +260,7 @@ module Pageflow
260
260
  can :manage, Pageflow.config.file_types.map(&:model)
261
261
  can :manage, Folder
262
262
  can :manage, Theming
263
+ can :manage, EntryTemplate
263
264
  can :manage, ::User
264
265
  end
265
266
  end
@@ -54,8 +54,6 @@ module Pageflow
54
54
  # @return [Theming] newly built theming
55
55
  def build_default_theming_for(account, attributes = {}, &block)
56
56
  default_attributes = {
57
- theme_name: Pageflow.config_for(account).themes.names.first,
58
-
59
57
  imprint_link_label: 'Impressum',
60
58
  imprint_link_url: 'http://example.com/impressum.html',
61
59
  copyright_link_label: '&copy; Pageflow 2014',
@@ -44,6 +44,10 @@ module Pageflow
44
44
  !!@options[:emphasized_pages]
45
45
  end
46
46
 
47
+ def supports_hide_logo_on_pages?
48
+ !!@options[:hide_logo_option]
49
+ end
50
+
47
51
  def page_change_by_scrolling?
48
52
  !@options[:no_page_change_by_scrolling]
49
53
  end
@@ -31,7 +31,11 @@ module Pageflow
31
31
  #
32
32
  # @option options :no_hide_text_on_swipe [Boolean]
33
33
  # Pass true if hiding the text by swiping to left shall be
34
- # deactived on mobile devices.
34
+ # deactivated on mobile devices.
35
+ #
36
+ # @option options :hide_logo_option [Boolean]
37
+ # Pass true if hiding the logo on specific pages should be supported
38
+ # as an option in the editor.
35
39
  def register(name, options = {})
36
40
  @themes[name] = Theme.new(name, options)
37
41
  end
@@ -1,3 +1,3 @@
1
1
  module Pageflow
2
- VERSION = '15.1.0'.freeze
2
+ VERSION = '15.2.2'.freeze
3
3
  end
@@ -10,7 +10,6 @@ module.exports = {
10
10
  '^backbone$': resolve('../../src/vendor/backbone'),
11
11
  '^underscore$': resolve('../../src/vendor/underscore'),
12
12
  '^cocktail$': resolve('../../src/vendor/cocktail'),
13
- '^i18n-js$': resolve('../../src/vendor/i18n'),
14
13
  '^iscroll$': resolve('../../src/vendor/iscroll'),
15
14
  '^wysihtml5': resolve('../../spec/support/wysihtmlStub'),
16
15
  },
@@ -3,6 +3,6 @@ const jst = jstPlugin();
3
3
 
4
4
  module.exports = {
5
5
  process(data, id) {
6
- return jst.transform(data, id).replace('export default', 'var I18n = require("i18n-js").default; module.exports =');
6
+ return jst.transform(data, id).replace('export default', 'var I18n = require("i18n-js"); module.exports =');
7
7
  }
8
8
  };
@@ -8,7 +8,6 @@ module.exports = {
8
8
  'jquery.minicolors': 'jQuery',
9
9
  'underscore': '_',
10
10
  'backbone.marionette': 'Backbone.Marionette',
11
- 'i18n-js': 'I18n',
12
11
  'iscroll': 'IScroll',
13
12
  'wysihtml5': 'wysihtml5'
14
13
  }
@@ -1119,7 +1119,9 @@ var EditorApi = Object$1.extend(
1119
1119
  }
1120
1120
 
1121
1121
  var payloadJson = JSON.parse(decodeURIComponent(encodedPayload));
1122
- return new this.fileSelectionHandlers[handlerName](payloadJson);
1122
+ return new this.fileSelectionHandlers[handlerName](_objectSpread({}, payloadJson, {
1123
+ entry: state.entry
1124
+ }));
1123
1125
  },
1124
1126
  createPageConfigurationEditorView: function createPageConfigurationEditorView(page, options) {
1125
1127
  var view = this.pageTypes.findByPage(page).createConfigurationEditorView(_$1.extend(options, {
@@ -1132,6 +1134,13 @@ var EditorApi = Object$1.extend(
1132
1134
 
1133
1135
  var editor$1 = new EditorApi();
1134
1136
  var startEditor = function startEditor(options) {
1137
+ // In Webpack builds, I18n object from the i18n-js module is not
1138
+ // identical to window.I18n which is provided by the i18n-js gem via
1139
+ // the asset pipeline. Make translations provided via the asset
1140
+ // pipeline available in Webpack bundle.
1141
+ I18n$1.defaultLocale = window.I18n.defaultLocale;
1142
+ I18n$1.locale = window.I18n.locale;
1143
+ I18n$1.translations = window.I18n.translations;
1135
1144
  $(function () {
1136
1145
  $.when($.getJSON('/editor/entries/' + options.entryId + '/seed'), pageflow.browser.detectFeatures()).done(function (result) {
1137
1146
  app.start(result[0]);
@@ -2259,6 +2268,9 @@ var Theme = Backbone.Model.extend({
2259
2268
  },
2260
2269
  supportsScrollIndicatorModes: function supportsScrollIndicatorModes() {
2261
2270
  return this.get('scroll_indicator_modes');
2271
+ },
2272
+ supportsHideLogoOnPages: function supportsHideLogoOnPages() {
2273
+ return this.get('hide_logo_option');
2262
2274
  }
2263
2275
  });
2264
2276
 
@@ -3294,7 +3306,16 @@ var Entry = Backbone.Model.extend({
3294
3306
  initialize: function initialize(attributes, options) {
3295
3307
  options = options || {};
3296
3308
  this.metadata = new EntryMetadata(this.get('metadata') || {});
3297
- this.metadata.parent = this;
3309
+ this.metadata.parent = this; // In 15.1 `entry.configuration` was turned into a new `Metadata`
3310
+ // model. Some of the entry type specific data (like
3311
+ // `home_button_enabled`) was extraced into
3312
+ // `entry.metadata.configuration`. Attributes like `title` or `locale`
3313
+ // which used to live in `entry.configuration` now live in
3314
+ // entry.metadata. Since some plugins (e.g. `pageflow-vr`) depend on
3315
+ // reading the locale from `entry.configuration`, this `configuration`
3316
+ // keeps backwards compatibility.
3317
+
3318
+ this.configuration = this.metadata;
3298
3319
  this.themes = options.themes || state.themes;
3299
3320
  this.files = options.files || state.files;
3300
3321
  this.fileTypes = options.fileTypes || editor$1.fileTypes;
@@ -3890,6 +3911,7 @@ var BackButtonDecoratorView = Marionette.Layout.extend({
3890
3911
  this.outlet.show(this.options.view);
3891
3912
  },
3892
3913
  goBack: function goBack() {
3914
+ this.options.view.onGoBack && this.options.view.onGoBack();
3893
3915
  editor$1.navigate('/', {
3894
3916
  trigger: true
3895
3917
  });
@@ -5464,6 +5486,8 @@ var EditPageView = Marionette.Layout.extend({
5464
5486
  'change:template': 'load'
5465
5487
  },
5466
5488
  onRender: function onRender() {
5489
+ var _this = this;
5490
+
5467
5491
  this.pageTypeContainer.show(new ExtendedSelectInputView({
5468
5492
  model: this.model,
5469
5493
  propertyName: 'template',
@@ -5474,7 +5498,8 @@ var EditPageView = Marionette.Layout.extend({
5474
5498
  descriptionTranslationKeyProperty: 'description_translation_key',
5475
5499
  pictogramClass: 'type_pictogram',
5476
5500
  helpLinkClicked: function helpLinkClicked(value) {
5477
- var pageType = this.options.api.pageTypes.findByName(value);
5501
+ var pageType = _this.options.api.pageTypes.findByName(value);
5502
+
5478
5503
  app.trigger('toggle-help', pageType.seed.help_entry_translation_key);
5479
5504
  }
5480
5505
  }));
@@ -8084,7 +8109,7 @@ var ListView = Marionette.ItemView.extend({
8084
8109
  typeName: this.options.itemTypeName,
8085
8110
  typeDescription: this.options.itemTypeDescription,
8086
8111
  isInvalid: this.options.itemIsInvalid
8087
- }, _$1(this.options).pick('onEdit', 'onDelete', 'highlight')),
8112
+ }, _$1(this.options).pick('onEdit', 'onRemove', 'highlight')),
8088
8113
  blankSlateViewConstructor: Marionette.ItemView.extend({
8089
8114
  tagName: 'li',
8090
8115
  className: 'list_blank_slate',
@@ -8469,6 +8494,10 @@ ConfigurationEditorTabView.groups.define('options', function (options) {
8469
8494
  collection: state.audioFiles
8470
8495
  });
8471
8496
 
8497
+ if (theme.supportsHideLogoOnPages()) {
8498
+ this.input('hide_logo', CheckBoxInputView);
8499
+ }
8500
+
8472
8501
  if (options.canPauseAtmo) {
8473
8502
  this.input('atmo_during_playback', SelectInputView, {
8474
8503
  values: pageflow.Atmo.duringPlaybackModes
@@ -22,6 +22,7 @@
22
22
  "lint": "eslint ."
23
23
  },
24
24
  "dependencies": {
25
- "core-js": "^3.4.1"
25
+ "core-js": "^3.4.1",
26
+ "i18n-js": "^3.5.1"
26
27
  }
27
28
  }
@@ -1,7 +1,281 @@
1
- import Backbone from 'backbone';
1
+ import $ from 'jquery';
2
2
  import _ from 'underscore';
3
+ import { Object as Object$1 } from 'pageflow/ui';
4
+ import Backbone from 'backbone';
3
5
  import { Entry, Theme, FileTypes, FilesCollection, SubsetCollection, ImageFile, WidgetTypes, EditorApi, VideoFile, TextTrackFile } from 'pageflow/editor';
6
+ import I18n from 'i18n-js';
7
+
8
+ var Base = Object$1.extend({
9
+ initialize: function initialize($el) {
10
+ this.$el = $el;
11
+ }
12
+ });
13
+
14
+ Base.classMethods = function (Constructor) {
15
+ return {
16
+ find: function find(viewOrParentElement) {
17
+ var selector = Constructor.prototype.selector;
18
+ var parentElement = viewOrParentElement.$el || viewOrParentElement;
19
+ var element = parentElement.find(selector);
20
+
21
+ if (element.length > 1) {
22
+ throw new Error('Selector "' + selector + '" matches multiple elements in view. Expected only one');
23
+ }
24
+
25
+ if (element.length === 0) {
26
+ throw new Error('Selector "' + selector + '" did not match any elements in view.');
27
+ }
28
+
29
+ return new Constructor(element);
30
+ },
31
+ findAll: function findAll(viewOrParentElement) {
32
+ var selector = Constructor.prototype.selector;
33
+ var parentElement = viewOrParentElement.$el || viewOrParentElement;
34
+ var elements = parentElement.find(selector);
35
+ return elements.map(function () {
36
+ return new Constructor($(this));
37
+ }).get();
38
+ },
39
+ findBy: function findBy(predicate, options) {
40
+ var predicateString = options.predicateName ? ' filtered by ' + options.predicateName : '';
41
+ var selector = Constructor.prototype.selector;
42
+ var selectorString = 'Selector "' + selector + '"' + predicateString;
43
+ var elements = options.inView.$el.find(selector);
44
+ var element = elements.filter(function () {
45
+ return predicate($(this));
46
+ });
47
+
48
+ if (element.length > 1) {
49
+ throw new Error(selectorString + ' matches multiple elements in view. Expected only one');
50
+ }
51
+
52
+ if (element.length === 0) {
53
+ throw new Error(selectorString + ' did not match any elements in view.');
54
+ }
55
+
56
+ return new Constructor(element);
57
+ },
58
+ render: function render(view, options) {
59
+ view.render();
60
+
61
+ if (options && options.appendTo) {
62
+ options.appendTo.append(view.$el);
63
+ }
64
+
65
+ return new Constructor(view.$el);
66
+ }
67
+ };
68
+ };
69
+
70
+ Base.extend = function ()
71
+ /* arguments */
72
+ {
73
+ var result = Object$1.extend.apply(this, arguments);
74
+
75
+ _.extend(result, Base.classMethods(result));
76
+
77
+ return result;
78
+ };
79
+
80
+ var DropDownButton = Base.extend({
81
+ selector: '.drop_down_button',
82
+ menuItemNames: function menuItemNames() {
83
+ return this.$el.find('li').map(function () {
84
+ return $(this).data('name');
85
+ }).get();
86
+ },
87
+ menuItemLabels: function menuItemLabels() {
88
+ return this.$el.find('li a').map(function () {
89
+ return $(this).text();
90
+ }).get();
91
+ },
92
+ selectMenuItemByName: function selectMenuItemByName(name) {
93
+ var menuItem = this.$el.find('li').filter(function () {
94
+ return $(this).data('name') == name;
95
+ });
96
+
97
+ if (!menuItem.length) {
98
+ throw new Error('Could not find menu item with name "' + name + '"');
99
+ }
100
+
101
+ menuItem.find('a').trigger('click');
102
+ },
103
+ selectMenuItemByLabel: function selectMenuItemByLabel(label) {
104
+ var menuItemLink = this.$el.find('li a').filter(function () {
105
+ return $(this).text() == label;
106
+ });
107
+
108
+ if (!menuItemLink.length) {
109
+ throw new Error('Could not find menu item with label "' + label + '"');
110
+ }
111
+
112
+ menuItemLink.trigger('click');
113
+ }
114
+ });
115
+
116
+ var FileMetaDataTable = Base.extend({
117
+ selector: '.file_meta_data table',
118
+ values: function values() {
119
+ return this.$el.find('.value').map(function () {
120
+ return $(this).text();
121
+ }).get();
122
+ }
123
+ });
124
+
125
+ var FileStageItem = Base.extend({
126
+ selector: '.file_stage_item'
127
+ });
128
+
129
+ var FileThumbnail = Base.extend({
130
+ selector: '.file_thumbnail',
131
+ backgroundImage: function backgroundImage() {
132
+ return this.$el.css('backgroundImage');
133
+ }
134
+ });
135
+
136
+ var ReferenceInput = Base.extend({
137
+ clickChooseButton: function clickChooseButton() {
138
+ this.$el.find('.choose').trigger('click');
139
+ }
140
+ });
141
+
142
+ var StaticThumbnail = Base.extend({
143
+ selector: '.static_thumbnail',
144
+ backgroundImage: function backgroundImage() {
145
+ return this.$el.css('backgroundImage');
146
+ }
147
+ });
148
+
149
+ var ThemeItem = Base.extend({
150
+ selector: '.theme_item',
151
+ hover: function hover() {
152
+ this.$el.trigger('mouseenter');
153
+ },
154
+ click: function click() {
155
+ this.$el.trigger('click');
156
+ },
157
+ clickUseButton: function clickUseButton() {
158
+ this.$el.find('.use_theme').trigger('click');
159
+ }
160
+ });
161
+
162
+ ThemeItem.findByName = function (themeName, options) {
163
+ return this.findBy(function ($el) {
164
+ return $el.data('themeName') === themeName;
165
+ }, _.extend({
166
+ predicateName: 'theme name ' + themeName
167
+ }, options));
168
+ };
169
+
170
+ var ConfigurationEditorTab = Base.extend({
171
+ selector: '.configuration_editor_tab',
172
+ inputPropertyNames: function inputPropertyNames() {
173
+ return this.$el.find('.input').map(function () {
174
+ return $(this).data('inputPropertyName');
175
+ }).get();
176
+ },
177
+ inputLabels: function inputLabels() {
178
+ return this.$el.find('.input').map(function () {
179
+ return $(this).data('labelText');
180
+ }).get();
181
+ },
182
+ inlineHelpTexts: function inlineHelpTexts() {
183
+ return this.$el.find('.input').map(function () {
184
+ return $(this).data('inlineHelpText');
185
+ }).get();
186
+ }
187
+ });
188
+
189
+ var Tabs = Base.extend({
190
+ selector: '.tabs_view',
191
+ tabNames: function tabNames() {
192
+ return this.$el.find('[data-tab-name]').map(function () {
193
+ return $(this).data('tabName');
194
+ }).get();
195
+ },
196
+ tabLabels: function tabLabels() {
197
+ return this.$el.find('[data-tab-name]').map(function () {
198
+ return $(this).text();
199
+ }).get();
200
+ }
201
+ });
202
+
203
+ var ConfigurationEditor = Base.extend({
204
+ selector: '.configuration_editor',
205
+ tabNames: function tabNames() {
206
+ return Tabs.find(this.$el).tabNames();
207
+ },
208
+ tabLabels: function tabLabels() {
209
+ return Tabs.find(this.$el).tabLabels();
210
+ },
211
+ inputPropertyNames: function inputPropertyNames() {
212
+ return ConfigurationEditorTab.find(this.$el).inputPropertyNames();
213
+ },
214
+ inputLabels: function inputLabels() {
215
+ return ConfigurationEditorTab.find(this.$el).inputLabels();
216
+ },
217
+ inlineHelpTexts: function inlineHelpTexts() {
218
+ return ConfigurationEditorTab.find(this.$el).inlineHelpTexts();
219
+ }
220
+ });
221
+
222
+ var Table = Base.extend({
223
+ selector: '.table_view',
224
+ columnNames: function columnNames() {
225
+ return this.$el.find('th').map(function () {
226
+ return $(this).data('columnName');
227
+ }).get();
228
+ }
229
+ });
230
+
231
+ var ColorInput = Base.extend({
232
+ value: function value() {
233
+ return this._input().val();
234
+ },
235
+ fillIn: function fillIn(value, clock) {
236
+ this._input().val(value);
237
+
238
+ this._input().trigger('keyup');
239
+
240
+ clock.tick(500);
241
+ },
242
+ swatches: function swatches() {
243
+ return this.$el.find('.minicolors-swatches span').map(function () {
244
+ return window.getComputedStyle(this)['background-color'];
245
+ }).get();
246
+ },
247
+ _input: function _input() {
248
+ return this.$el.find('input');
249
+ }
250
+ });
4
251
 
252
+ var SelectInput = Base.extend({
253
+ selector: 'select',
254
+ values: function values() {
255
+ return this.$el.find('option').map(function () {
256
+ return $(this).attr('value');
257
+ }).get();
258
+ }
259
+ });
260
+
261
+ function _defineProperty(obj, key, value) {
262
+ if (key in obj) {
263
+ Object.defineProperty(obj, key, {
264
+ value: value,
265
+ enumerable: true,
266
+ configurable: true,
267
+ writable: true
268
+ });
269
+ } else {
270
+ obj[key] = value;
271
+ }
272
+
273
+ return obj;
274
+ }
275
+
276
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
277
+
278
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
5
279
  /**
6
280
  * Build editor Backbone models for tests.
7
281
  */
@@ -40,7 +314,9 @@ var factories = {
40
314
 
41
315
  ensureFileTypes(options);
42
316
  ensureFilesCollections(options);
43
- var entry = new model(attributes, _.extend({
317
+ var entry = new model(_objectSpread({
318
+ id: 1
319
+ }, attributes), _.extend({
44
320
  storylines: new Backbone.Collection(),
45
321
  chapters: new Backbone.Collection()
46
322
  }, options));
@@ -233,7 +509,17 @@ var factories = {
233
509
  return widgetTypes;
234
510
  },
235
511
  editorApi: function editorApi(beforeSetup) {
236
- var api = new EditorApi();
512
+ var api = new EditorApi({
513
+ router: {
514
+ navigate: function navigate(path, _ref) {
515
+ var trigger = _ref.trigger;
516
+
517
+ if (trigger) {
518
+ api.trigger('navigate', path);
519
+ }
520
+ }
521
+ }
522
+ });
237
523
 
238
524
  if (beforeSetup) {
239
525
  beforeSetup(api);
@@ -265,4 +551,81 @@ function ensureFilesCollections(options) {
265
551
  }
266
552
  }
267
553
 
268
- export { factories };
554
+ /**
555
+ * Define translations to use in tests.
556
+ *
557
+ * @param {Object} translations -
558
+ * A mapping of either the form `(translation key => translated
559
+ * text)`. Translation keys can contains dots.
560
+ * @param {Object} [options]
561
+ * @param {boolean} [options.multiLocale] -
562
+ * Set to `true` if keys include the locale name.
563
+ *
564
+ * @example
565
+ * import {useFakeTranslations} from 'pageflow/testHelpers';
566
+ * import I18n from 'i18n-js';
567
+ *
568
+ * describe('...', () => {
569
+ * useFakeTranslations({
570
+ * 'some.key': 'some translation'
571
+ * });
572
+ *
573
+ * it('...', () => {
574
+ * I18n.t('some.key') // => 'some translation'
575
+ * });
576
+ * });
577
+ *
578
+ * @example
579
+ * import {useFakeTranslations} from 'pageflow/testHelpers';
580
+ * import I18n from 'i18n-js';
581
+ *
582
+ * describe('...', () => {
583
+ * useFakeTranslations({
584
+ * 'en.some.key': 'some text',
585
+ * 'de.some.key': 'etwas Text'
586
+ * }, {multiLocale: true});
587
+ *
588
+ * it('...', () => {
589
+ * I18n.locale = 'de';
590
+ * I18n.t('some.key') // => 'etwas Text'
591
+ * });
592
+ * });
593
+ */
594
+
595
+ function useFakeTranslations(translations) {
596
+ var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
597
+ multiLocale = _ref.multiLocale;
598
+
599
+ var originalTranslations;
600
+ beforeEach(function () {
601
+ originalTranslations = I18n.translations;
602
+
603
+ if (multiLocale) {
604
+ I18n.translations = keysWithDotsToNestedObjects(translations);
605
+ } else {
606
+ I18n.translations = {
607
+ en: keysWithDotsToNestedObjects(translations)
608
+ };
609
+ }
610
+ });
611
+ afterEach(function () {
612
+ I18n.translations = originalTranslations;
613
+ });
614
+ }
615
+
616
+ function keysWithDotsToNestedObjects(translations) {
617
+ return _(translations).reduce(function (result, value, key) {
618
+ var keys = key.split('.');
619
+ var last = keys.pop();
620
+
621
+ var inner = _(keys).reduce(function (r, key) {
622
+ r[key] = r[key] || {};
623
+ return r[key];
624
+ }, result);
625
+
626
+ inner[last] = value;
627
+ return result;
628
+ }, {});
629
+ }
630
+
631
+ export { ColorInput, ConfigurationEditor, ConfigurationEditorTab, DropDownButton, FileMetaDataTable, FileStageItem, FileThumbnail, ReferenceInput, SelectInput, StaticThumbnail, Table, Tabs, ThemeItem, factories, useFakeTranslations };