ckeditor5 1.18.3 → 1.19.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60fcadf8181318c867cdb47b5baf04d0c3630faf3182145f1d3f37952778aeb9
4
- data.tar.gz: c04696863a8b29644a37c6782b75dca5317a9bd2d0e166da05ae0cab357f1bc0
3
+ metadata.gz: 39db7b05796b6fd781f005ccb45d014a7936312619e4068d053378a5a5050bfd
4
+ data.tar.gz: 2f1746e2a1cb82ea6dc65465a616995e10744983f122f5e2822158e89ac710df
5
5
  SHA512:
6
- metadata.gz: d13f472504a89a4a8ebc13caa463546995ac8592da03aba6ec52a85705990e0cf5c4b8582cccf143ee560541406db5ca449782e22642c57cf0d91beeeaa4edd3
7
- data.tar.gz: 7c2f98547ea4a7044f73ec6017f35dadae2842aa61ea91ba7961ec0e114cb81491c5e30dc90c1ec964d394e3e49b1b6a28beaeed24a3264154e4d9a6e3bc7cee
6
+ metadata.gz: 5bbf9f23f3db08a9afef4873c4ba6d9ae57289130b0aa2c8581175c380a51d116668d32b209fd817abe7a255dbc0b3efc7cb869e7ec004749705d571df4c1a75
7
+ data.tar.gz: f096236e1d7a76c12803928b3e3efdb7d07d4b4ba5d5011af9d1597e8c33ba1622897430dcba9516fb87ee538ab1d5dce49210771a357e05d233ea03ba0c3839
data/README.md CHANGED
@@ -161,6 +161,7 @@ For extending CKEditor's functionality, refer to the [plugins directory](https:/
161
161
  - [Setting the language in the initializer](#setting-the-language-in-the-initializer)
162
162
  - [Setting the language on the editor level](#setting-the-language-on-the-editor-level)
163
163
  - [Preloading multiple translation packs](#preloading-multiple-translation-packs)
164
+ - [Spell and Grammar Checking 📝](#spell-and-grammar-checking-)
164
165
  - [Integrating with Forms 📋](#integrating-with-forms-)
165
166
  - [Rails form builder integration](#rails-form-builder-integration)
166
167
  - [Simple form integration](#simple-form-integration)
@@ -928,6 +929,23 @@ CKEditor5::Rails.configure do
928
929
  end
929
930
  ```
930
931
 
932
+ If no `language` is set, the plugin will use the default language of the editor. If you want to set the language of the WProofreader plugin, you can use the `language` keyword argument:
933
+
934
+ ```rb
935
+ # config/initializers/ckeditor5.rb
936
+
937
+ CKEditor5::Rails.configure do
938
+ # ... other configuration
939
+
940
+ wproofreader serviceId: 'your-service-ID',
941
+ srcUrl: 'https://svc.webspellchecker.net/spellcheck31/wscbundle/wscbundle.js',
942
+ language: :en_US,
943
+ localization: :pl
944
+ end
945
+ ```
946
+
947
+ For more info about the WProofreader plugin, check the [official documentation](https://ckeditor.com/docs/ckeditor5/latest/features/spelling-and-grammar-checking.html).
948
+
931
949
  </details>
932
950
 
933
951
  ### Controller / View helpers 📦
@@ -1585,6 +1603,23 @@ CKEditor5::Rails.configure do
1585
1603
  end
1586
1604
  ```
1587
1605
 
1606
+ ### Spell and Grammar Checking 📝
1607
+
1608
+ CKEditor 5 provides a spell and grammar checking feature through the WProofreader plugin. You can enable this feature by configuring the WProofreader plugin in the initializer.
1609
+
1610
+ ```rb
1611
+ # config/initializers/ckeditor5.rb
1612
+
1613
+ CKEditor5::Rails.configure do
1614
+ wproofreader serviceId: 'your-service-ID',
1615
+ srcUrl: 'https://svc.webspellchecker.net/spellcheck31/wscbundle/wscbundle.js'
1616
+ end
1617
+ ```
1618
+
1619
+ See [`wproofreader(version: nil, cdn: nil, **config)` method](#wproofreaderversion-nil-cdn-nil-config-method) for more information about the WProofreader plugin configuration.
1620
+
1621
+ See the [official documentation](https://ckeditor.com/docs/ckeditor5/latest/features/spelling-and-grammar-checking.html) for more information about the WProofreader plugin.
1622
+
1588
1623
  ### Integrating with Forms 📋
1589
1624
 
1590
1625
  You can integrate CKEditor 5 with Rails form builders like `form_for` or `simple_form`. The example below shows how to integrate CKEditor 5 with a Rails form using the `form_for` helper:
@@ -1,11 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/core_ext/module'
4
+ require 'uri'
4
5
 
5
6
  module CKEditor5::Rails::Assets
6
7
  class AssetsBundle
7
- def initialize
8
- validate_implementation!
8
+ def initialize(scripts: nil, stylesheets: nil)
9
+ @scripts = scripts
10
+ @stylesheets = stylesheets
11
+ end
12
+
13
+ def scripts
14
+ @scripts || []
15
+ end
16
+
17
+ def stylesheets
18
+ @stylesheets || []
9
19
  end
10
20
 
11
21
  def empty?
@@ -17,7 +27,7 @@ module CKEditor5::Rails::Assets
17
27
  end
18
28
 
19
29
  def preloads
20
- stylesheets + scripts.map(&:url)
30
+ stylesheets + scripts.map(&:preloads)
21
31
  end
22
32
 
23
33
  def <<(other)
@@ -26,16 +36,6 @@ module CKEditor5::Rails::Assets
26
36
  @scripts = scripts + other.scripts
27
37
  @stylesheets = stylesheets + other.stylesheets
28
38
  end
29
-
30
- private
31
-
32
- def validate_implementation!
33
- %i[scripts stylesheets].each do |method|
34
- unless respond_to?(method, true)
35
- raise NotImplementedError, "#{self.class} must implement the ##{method} method"
36
- end
37
- end
38
- end
39
39
  end
40
40
 
41
41
  class JSUrlImportMeta
@@ -56,6 +56,14 @@ module CKEditor5::Rails::Assets
56
56
  def to_h
57
57
  import_meta.to_h.merge({ url: url })
58
58
  end
59
+
60
+ def preloads
61
+ {
62
+ as: 'script',
63
+ rel: esm? ? 'modulepreload' : 'preload',
64
+ href: url
65
+ }
66
+ end
59
67
  end
60
68
 
61
69
  class JSImportMeta
@@ -19,10 +19,10 @@ module CKEditor5::Rails::Assets
19
19
 
20
20
  def to_html
21
21
  safe_join([
22
+ scripts_import_map_tag,
22
23
  preload_tags,
23
24
  styles_tags,
24
25
  window_scripts_tags,
25
- scripts_import_map_tag,
26
26
  web_component_tag
27
27
  ])
28
28
  end
@@ -51,7 +51,9 @@ module CKEditor5::Rails::Assets
51
51
  return @scripts_import_map_tag if defined?(@scripts_import_map_tag)
52
52
 
53
53
  import_map = bundle.scripts.each_with_object({}) do |script, map|
54
- map[script.import_name] = script.url if script.esm?
54
+ next if !script.esm? || looks_like_url?(script.import_name)
55
+
56
+ map[script.import_name] = script.url
55
57
  end
56
58
 
57
59
  @scripts_import_map_tag = tag.script(
@@ -68,10 +70,28 @@ module CKEditor5::Rails::Assets
68
70
  end
69
71
 
70
72
  def preload_tags
71
- @preload_tags ||= safe_join(bundle.preloads.map do |url|
72
- tag.link(href: url, rel: 'preload', as: self.class.url_resource_preload_type(url),
73
- crossorigin: 'anonymous')
73
+ @preload_tags ||= safe_join(bundle.preloads.map do |preload|
74
+ if preload.is_a?(Hash) && preload[:as] && preload[:href]
75
+ tag.link(
76
+ **preload,
77
+ crossorigin: 'anonymous'
78
+ )
79
+ else
80
+ tag.link(
81
+ href: preload,
82
+ rel: 'preload',
83
+ as: self.class.url_resource_preload_type(preload),
84
+ crossorigin: 'anonymous'
85
+ )
86
+ end
74
87
  end)
75
88
  end
89
+
90
+ def looks_like_url?(str)
91
+ uri = URI.parse(str)
92
+ uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
93
+ rescue URI::InvalidURIError
94
+ false
95
+ end
76
96
  end
77
97
  end
@@ -86,6 +86,20 @@ function loadAsyncImports(imports = []) {
86
86
  }));
87
87
  }
88
88
 
89
+ /**
90
+ * Checks if stylesheet with given href already exists in document
91
+ *
92
+ * @param {string} href - Stylesheet URL to check
93
+ * @returns {boolean} True if stylesheet already exists
94
+ */
95
+ function stylesheetExists(href) {
96
+ return Array
97
+ .from(document.styleSheets)
98
+ .some(sheet =>
99
+ sheet.href === href || sheet.href === new URL(href, window.location.href).href
100
+ );
101
+ }
102
+
89
103
  /**
90
104
  * Dynamically loads CSS files based on configuration
91
105
  *
@@ -96,19 +110,19 @@ function loadAsyncImports(imports = []) {
96
110
  function loadAsyncCSS(stylesheets = []) {
97
111
  const promises = stylesheets.map(href =>
98
112
  new Promise((resolve, reject) => {
99
- const link = document.createElement('link');
113
+ if (stylesheetExists(href)) {
114
+ resolve();
115
+ return;
116
+ }
100
117
 
118
+ const link = document.createElement('link');
101
119
  link.rel = 'stylesheet';
102
120
  link.href = href;
103
121
 
104
122
  link.onerror = reject;
105
- link.onload = () => {
106
- resolve();
107
- };
123
+ link.onload = () => resolve();
108
124
 
109
125
  document.head.appendChild(link);
110
-
111
- return link;
112
126
  })
113
127
  );
114
128
 
@@ -26,6 +26,7 @@ module CKEditor5::Rails
26
26
  bundle = build_base_cdn_bundle(cdn, version, translations)
27
27
  bundle << build_premium_cdn_bundle(cdn, version, translations) if premium
28
28
  bundle << build_ckbox_cdn_bundle(ckbox) if ckbox
29
+ bundle << build_plugins_cdn_bundle(mapped_preset.plugins.items)
29
30
 
30
31
  @__ckeditor_context = {
31
32
  license_key: license_key,
@@ -98,5 +99,11 @@ module CKEditor5::Rails
98
99
  cdn: ckbox[:cdn] || :ckbox
99
100
  )
100
101
  end
102
+
103
+ def build_plugins_cdn_bundle(plugins)
104
+ plugins.each_with_object(Assets::AssetsBundle.new(scripts: [], stylesheets: [])) do |plugin, bundle|
105
+ bundle << plugin.preload_assets_bundle if plugin.preload_assets_bundle.present?
106
+ end
107
+ end
101
108
  end
102
109
  end
@@ -8,8 +8,8 @@ module CKEditor5::Rails::Editor
8
8
  @name = name
9
9
  end
10
10
 
11
- def preload_assets_urls
12
- []
11
+ def preload_assets_bundle
12
+ nil
13
13
  end
14
14
 
15
15
  def to_h
@@ -18,8 +18,11 @@ module CKEditor5::Rails::Editor
18
18
  )
19
19
  end
20
20
 
21
- def preload_assets_urls
22
- @stylesheets + [@js_import_meta.url]
21
+ def preload_assets_bundle
22
+ @preload_assets_bundle ||= CKEditor5::Rails::Assets::AssetsBundle.new(
23
+ scripts: [@js_import_meta],
24
+ stylesheets: @stylesheets
25
+ )
23
26
  end
24
27
 
25
28
  def to_h
@@ -22,6 +22,7 @@ module CKEditor5::Rails
22
22
  end
23
23
 
24
24
  def plugin(name, **kwargs)
25
+ premium(true) if kwargs[:premium] && respond_to?(:premium)
25
26
  register_plugin(PluginsBuilder.create_plugin(name, **kwargs))
26
27
  end
27
28
 
@@ -207,8 +207,8 @@ module CKEditor5::Rails
207
207
  def wproofreader(version: nil, cdn: nil, **config)
208
208
  configure :wproofreader, config
209
209
  plugins do
210
- prepend Plugins::WProofreaderSync.new
211
- append Plugins::WProofreader.new(version: version, cdn: cdn)
210
+ prepend(Plugins::WProofreaderSync.new)
211
+ append(Plugins::WProofreader.new(version: version, cdn: cdn))
212
212
  end
213
213
  end
214
214
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.18.3'
5
+ VERSION = '1.19.1'
6
6
 
7
7
  DEFAULT_CKEDITOR_VERSION = '43.3.1'
8
8
  end
@@ -3,20 +3,6 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
6
- let(:test_bundle_class) do
7
- Class.new(CKEditor5::Rails::Assets::AssetsBundle) do
8
- attr_accessor :scripts, :stylesheets
9
-
10
- def initialize(scripts, stylesheets)
11
- @scripts = scripts
12
- @stylesheets = stylesheets
13
- super()
14
- end
15
- end
16
- end
17
-
18
- let(:bundle) { test_bundle_class.new(scripts, stylesheets) }
19
-
20
6
  let(:scripts) do
21
7
  [
22
8
  CKEditor5::Rails::Assets::JSUrlImportMeta.new(
@@ -31,7 +17,7 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
31
17
  end
32
18
 
33
19
  let(:stylesheets) { ['https://cdn.com/style1.css', 'https://cdn.com/style2.css'] }
34
- let(:preloads) { bundle.preloads }
20
+ let(:bundle) { CKEditor5::Rails::Assets::AssetsBundle.new(scripts: scripts, stylesheets: stylesheets) }
35
21
 
36
22
  subject(:serializer) { described_class.new(bundle) }
37
23
 
@@ -48,43 +34,102 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
48
34
  describe '#to_html' do
49
35
  subject(:html) { serializer.to_html }
50
36
 
51
- it 'includes window scripts' do
52
- expect(html).to include(
53
- '<script src="https://cdn.com/script1.js" nonce="true" crossorigin="anonymous">'
54
- )
37
+ it 'includes window script tags' do
38
+ expect(html).to have_tag('script', with: {
39
+ src: 'https://cdn.com/script1.js',
40
+ nonce: 'true',
41
+ crossorigin: 'anonymous'
42
+ })
55
43
  end
56
44
 
57
45
  it 'includes import map' do
58
- expect(html).to include('type="importmap"')
59
- expect(html).to include('"@ckeditor/script2":"https://cdn.com/script2.js"')
46
+ expect(html).to have_tag('script', with: { type: 'importmap' }) do
47
+ with_text(%r{"@ckeditor/script2":"https://cdn\.com/script2\.js"})
48
+ end
60
49
  end
61
50
 
62
- it 'includes stylesheet links' do
63
- stylesheets.each do |url|
64
- expect(html).to include("<link href=\"#{url}\" rel=\"stylesheet\" crossorigin=\"anonymous\">")
65
- end
51
+ it 'includes import map with correct attributes' do
52
+ expect(html).to have_tag('script', with: {
53
+ type: 'importmap',
54
+ nonce: 'true'
55
+ })
66
56
  end
67
57
 
68
- it 'includes preload links' do
69
- expect(html).to include(
70
- '<link href="https://cdn.com/style1.css" rel="preload" as="style" crossorigin="anonymous">'
58
+ it 'does not include URL-like imports in import map' do
59
+ bundle = CKEditor5::Rails::Assets::AssetsBundle.new(
60
+ scripts: [
61
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
62
+ 'https://cdn.com/script.js',
63
+ import_name: 'https://example.com/module'
64
+ ),
65
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
66
+ 'https://cdn.com/script.js',
67
+ import_name: 'module'
68
+ )
69
+ ]
71
70
  )
71
+ html = described_class.new(bundle).to_html
72
72
 
73
- expect(html).to include(
74
- '<link href="https://cdn.com/style2.css" rel="preload" as="style" crossorigin="anonymous">'
75
- )
73
+ expect(html).to have_tag('script', with: { type: 'importmap' }) do
74
+ with_text('{"imports":{"module":"https://cdn.com/script.js"}}')
75
+ end
76
+ end
76
77
 
77
- expect(html).to include(
78
- '<link href="https://cdn.com/script1.js" rel="preload" as="script" crossorigin="anonymous">'
78
+ it 'includes only ESM scripts in import map' do
79
+ bundle = CKEditor5::Rails::Assets::AssetsBundle.new(
80
+ scripts: [
81
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
82
+ 'https://cdn.com/script1.js',
83
+ window_name: 'WindowScript'
84
+ ),
85
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
86
+ 'https://cdn.com/script2.js',
87
+ import_name: '@ckeditor/module'
88
+ )
89
+ ]
79
90
  )
91
+ html = described_class.new(bundle).to_html
80
92
 
81
- expect(html).to include(
82
- '<link href="https://cdn.com/script2.js" rel="preload" as="script" crossorigin="anonymous">'
83
- )
93
+ expect(html).to have_tag('script', with: { type: 'importmap' }) do
94
+ with_text('{"imports":{"@ckeditor/module":"https://cdn.com/script2.js"}}')
95
+ end
96
+ end
97
+
98
+ it 'includes stylesheet links' do
99
+ stylesheets.each do |url|
100
+ expect(html).to have_tag('link', with: {
101
+ href: url,
102
+ rel: 'stylesheet',
103
+ crossorigin: 'anonymous'
104
+ })
105
+ end
106
+ end
107
+
108
+ it 'includes preload links' do
109
+ scripts.each do |script|
110
+ expect(html).to have_tag('link', with: {
111
+ href: script.url,
112
+ rel: script.esm? ? 'modulepreload' : 'preload',
113
+ as: 'script',
114
+ crossorigin: 'anonymous'
115
+ })
116
+ end
117
+
118
+ stylesheets.each do |url|
119
+ expect(html).to have_tag('link', with: {
120
+ href: url,
121
+ rel: 'preload',
122
+ as: 'style',
123
+ crossorigin: 'anonymous'
124
+ })
125
+ end
84
126
  end
85
127
 
86
128
  it 'includes web component script' do
87
- expect(html).to include('<script type="module" nonce="true">')
129
+ expect(html).to have_tag('script', with: {
130
+ type: 'module',
131
+ nonce: 'true'
132
+ })
88
133
  end
89
134
 
90
135
  it 'memoizes scripts import map' do
@@ -108,4 +153,17 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
108
153
  expect(described_class.url_resource_preload_type('file.unknown')).to eq('fetch')
109
154
  end
110
155
  end
156
+
157
+ describe '#looks_like_url? (private)' do
158
+ subject(:serializer) { described_class.new(CKEditor5::Rails::Assets::AssetsBundle.new) }
159
+
160
+ it 'returns false for invalid URIs' do
161
+ expect(serializer.send(:looks_like_url?, '@ckeditor/foo')).to be false
162
+ expect(serializer.send(:looks_like_url?, 'http')).to be false
163
+ expect(serializer.send(:looks_like_url?, 'invalid')).to be false
164
+ expect(serializer.send(:looks_like_url?, 'http://[invalid')).to be false
165
+ expect(serializer.send(:looks_like_url?, "http://example.com\nmalicious")).to be false
166
+ expect(serializer.send(:looks_like_url?, 'http://<invalid>')).to be false
167
+ end
168
+ end
111
169
  end
@@ -3,48 +3,45 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe CKEditor5::Rails::Assets::AssetsBundle do
6
- let(:concrete_class) do
7
- Class.new(described_class) do
8
- def scripts
9
- []
10
- end
11
-
12
- def stylesheets
13
- []
14
- end
15
- end
16
- end
17
-
18
6
  describe '#initialize' do
19
- it 'raises error when required methods are not implemented' do
20
- expect { described_class.new }.to raise_error(NotImplementedError)
7
+ it 'initializes with empty arrays by default' do
8
+ bundle = described_class.new
9
+ expect(bundle.scripts).to eq([])
10
+ expect(bundle.stylesheets).to eq([])
21
11
  end
22
12
 
23
- it 'initializes successfully when required methods are implemented' do
24
- expect { concrete_class.new }.not_to raise_error
13
+ it 'accepts scripts and stylesheets' do
14
+ bundle = described_class.new(scripts: [:script], stylesheets: [:stylesheet])
15
+ expect(bundle.scripts).to eq([:script])
16
+ expect(bundle.stylesheets).to eq([:stylesheet])
25
17
  end
26
18
  end
27
19
 
28
20
  describe '#empty?' do
29
- subject(:bundle) { concrete_class.new }
30
-
31
21
  it 'returns true when no assets are present' do
22
+ bundle = described_class.new
32
23
  expect(bundle).to be_empty
33
24
  end
25
+
26
+ it 'returns false when scripts are present' do
27
+ bundle = described_class.new(scripts: [:script])
28
+ expect(bundle).not_to be_empty
29
+ end
30
+
31
+ it 'returns false when stylesheets are present' do
32
+ bundle = described_class.new(stylesheets: [:stylesheet])
33
+ expect(bundle).not_to be_empty
34
+ end
34
35
  end
35
36
 
36
37
  describe '#translations_scripts' do
37
- let(:bundle) { concrete_class.new }
38
38
  let(:translation_script) do
39
39
  instance_double(CKEditor5::Rails::Assets::JSUrlImportMeta, translation?: true)
40
40
  end
41
41
  let(:regular_script) { instance_double(CKEditor5::Rails::Assets::JSUrlImportMeta, translation?: false) }
42
42
 
43
- before do
44
- allow(bundle).to receive(:scripts).and_return([translation_script, regular_script])
45
- end
46
-
47
43
  it 'returns only translation scripts' do
44
+ bundle = described_class.new(scripts: [translation_script, regular_script])
48
45
  expect(bundle.translations_scripts).to eq([translation_script])
49
46
  end
50
47
  end
@@ -54,41 +51,8 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundle do
54
51
  let(:script2) { instance_double(CKEditor5::Rails::Assets::JSUrlImportMeta) }
55
52
  let(:stylesheet1) { '/path/to/style1.css' }
56
53
  let(:stylesheet2) { '/path/to/style2.css' }
57
-
58
- let(:bundle1) do
59
- Class.new(described_class) do
60
- attr_writer :scripts, :stylesheets
61
-
62
- def scripts
63
- @scripts ||= []
64
- end
65
-
66
- def stylesheets
67
- @stylesheets ||= []
68
- end
69
- end.new
70
- end
71
-
72
- let(:bundle2) do
73
- Class.new(described_class) do
74
- attr_writer :scripts, :stylesheets
75
-
76
- def scripts
77
- @scripts ||= []
78
- end
79
-
80
- def stylesheets
81
- @stylesheets ||= []
82
- end
83
- end.new
84
- end
85
-
86
- before do
87
- bundle1.scripts = [script1]
88
- bundle1.stylesheets = [stylesheet1]
89
- bundle2.scripts = [script2]
90
- bundle2.stylesheets = [stylesheet2]
91
- end
54
+ let(:bundle1) { described_class.new(scripts: [script1], stylesheets: [stylesheet1]) }
55
+ let(:bundle2) { described_class.new(scripts: [script2], stylesheets: [stylesheet2]) }
92
56
 
93
57
  it 'raises TypeError when argument is not an AssetsBundle' do
94
58
  expect { bundle1 << 'not a bundle' }.to raise_error(TypeError)
@@ -103,36 +67,23 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundle do
103
67
  end
104
68
 
105
69
  describe '#preloads' do
106
- let(:script1) { instance_double(CKEditor5::Rails::Assets::JSUrlImportMeta, url: '/js/script1.js') }
107
- let(:script2) { instance_double(CKEditor5::Rails::Assets::JSUrlImportMeta, url: '/js/script2.js') }
70
+ let(:script1) { CKEditor5::Rails::Assets::JSUrlImportMeta.new('/js/script1.js', import_name: 'script1') }
71
+ let(:script2) { CKEditor5::Rails::Assets::JSUrlImportMeta.new('/js/script2.js', import_name: 'script2') }
108
72
  let(:stylesheet1) { '/css/style1.css' }
109
73
  let(:stylesheet2) { '/css/style2.css' }
110
-
111
74
  let(:bundle) do
112
- Class.new(described_class) do
113
- attr_writer :scripts, :stylesheets
114
-
115
- def scripts
116
- @scripts ||= []
117
- end
118
-
119
- def stylesheets
120
- @stylesheets ||= []
121
- end
122
- end.new
123
- end
124
-
125
- before do
126
- bundle.scripts = [script1, script2]
127
- bundle.stylesheets = [stylesheet1, stylesheet2]
75
+ described_class.new(
76
+ scripts: [script1, script2],
77
+ stylesheets: [stylesheet1, stylesheet2]
78
+ )
128
79
  end
129
80
 
130
81
  it 'returns array of stylesheet paths and script urls' do
131
82
  expect(bundle.preloads).to eq([
132
83
  '/css/style1.css',
133
84
  '/css/style2.css',
134
- '/js/script1.js',
135
- '/js/script2.js'
85
+ { as: 'script', rel: 'modulepreload', href: '/js/script1.js' },
86
+ { as: 'script', rel: 'modulepreload', href: '/js/script2.js' }
136
87
  ])
137
88
  end
138
89
  end
@@ -168,6 +119,18 @@ RSpec.describe CKEditor5::Rails::Assets::JSUrlImportMeta do
168
119
  })
169
120
  end
170
121
  end
122
+
123
+ describe '#preloads' do
124
+ it 'returns preload hash' do
125
+ meta = described_class.new(url, window_name: 'module')
126
+ expect(meta.preloads).to eq({ as: 'script', rel: 'preload', href: url })
127
+ end
128
+
129
+ it 'returns modulepreload hash when esm' do
130
+ meta = described_class.new(url, import_name: 'module')
131
+ expect(meta.preloads).to include({ as: 'script', rel: 'modulepreload', href: url })
132
+ end
133
+ end
171
134
  end
172
135
 
173
136
  RSpec.describe CKEditor5::Rails::Assets::JSImportMeta do
@@ -108,6 +108,30 @@ RSpec.describe CKEditor5::Rails::Cdn::Helpers do
108
108
  helper.ckeditor5_assets(preset: :default)
109
109
  end
110
110
  end
111
+
112
+ context 'with plugins having preload assets' do
113
+ let(:plugin_bundle) { CKEditor5::Rails::Assets::AssetsBundle.new(scripts: ['plugin.js']) }
114
+ let(:plugin) { instance_double('Plugin', preload_assets_bundle: plugin_bundle) }
115
+ let(:plugin_without_preload) { instance_double('Plugin', preload_assets_bundle: nil) }
116
+
117
+ before do
118
+ allow(preset).to receive_message_chain(:plugins, :items)
119
+ .and_return([plugin, plugin_without_preload])
120
+ end
121
+
122
+ it 'includes plugin preload assets in the bundle' do
123
+ helper.ckeditor5_assets(preset: :default)
124
+ expect(context[:bundle].scripts).to include('plugin.js')
125
+ end
126
+
127
+ it 'merges plugin assets with the main bundle' do
128
+ expect(serializer).to receive(:to_html)
129
+ helper.ckeditor5_assets(preset: :default)
130
+
131
+ bundle = context[:bundle]
132
+ expect(bundle.scripts).to include('plugin.js')
133
+ end
134
+ end
111
135
  end
112
136
 
113
137
  context 'when overriding preset values' do
@@ -33,11 +33,11 @@ RSpec.describe CKEditor5::Rails::Editor::PropsBasePlugin do
33
33
  end
34
34
  end
35
35
 
36
- describe '#preload_assets_urls' do
37
- it 'returns empty array' do
36
+ describe '#preload_assets_bundle' do
37
+ it 'returns nil by default' do
38
38
  plugin = described_class.new(:Bold)
39
39
 
40
- expect(plugin.preload_assets_urls).to eq([])
40
+ expect(plugin.preload_assets_bundle).to be_nil
41
41
  end
42
42
  end
43
43
 
@@ -8,7 +8,7 @@ RSpec.describe CKEditor5::Rails::Editor::PropsExternalPlugin do
8
8
  plugin = described_class.new('Test', script: 'https://example.org/plugin.js')
9
9
 
10
10
  expect(plugin.name).to eq('Test')
11
- expect(plugin.preload_assets_urls).to include('https://example.org/plugin.js')
11
+ expect(plugin.preload_assets_bundle.scripts.first.url).to eq('https://example.org/plugin.js')
12
12
  end
13
13
 
14
14
  it 'accepts optional parameters' do
@@ -20,23 +20,21 @@ RSpec.describe CKEditor5::Rails::Editor::PropsExternalPlugin do
20
20
  stylesheets: ['https://example.org/style.css']
21
21
  )
22
22
 
23
- expect(plugin.preload_assets_urls).to include('https://example.org/style.css')
23
+ expect(plugin.preload_assets_bundle.stylesheets).to include('https://example.org/style.css')
24
24
  end
25
25
  end
26
26
 
27
- describe '#preload_assets_urls' do
28
- it 'returns array with script and stylesheets urls' do
27
+ describe '#preload_assets_bundle' do
28
+ it 'returns bundle with script and stylesheets' do
29
29
  plugin = described_class.new(
30
30
  'Test',
31
31
  script: 'https://example.org/plugin.js',
32
32
  stylesheets: ['https://example.org/style1.css', 'https://example.org/style2.css']
33
33
  )
34
34
 
35
- expect(plugin.preload_assets_urls).to eq([
36
- 'https://example.org/style1.css',
37
- 'https://example.org/style2.css',
38
- 'https://example.org/plugin.js'
39
- ])
35
+ bundle = plugin.preload_assets_bundle
36
+ expect(bundle.scripts.first.url).to eq('https://example.org/plugin.js')
37
+ expect(bundle.stylesheets).to eq(['https://example.org/style1.css', 'https://example.org/style2.css'])
40
38
  end
41
39
  end
42
40
 
@@ -78,10 +78,28 @@ RSpec.describe CKEditor5::Rails::Engine do
78
78
  end
79
79
  end
80
80
 
81
- describe 'simple_form initializer', if: defined?(SimpleForm) do
82
- it 'registers ckeditor5 input type' do
83
- expect(SimpleForm::FormBuilder.mappings[:ckeditor5])
84
- .to eq(CKEditor5::Rails::Hooks::SimpleForm::CKEditor5Input)
81
+ describe 'simple_form initializer' do
82
+ context 'when SimpleForm is defined' do
83
+ it 'registers ckeditor5 input type' do
84
+ expect(SimpleForm::FormBuilder.mappings[:ckeditor5])
85
+ .to eq(CKEditor5::Rails::Hooks::SimpleForm::CKEditor5Input)
86
+ end
87
+ end
88
+
89
+ context 'when SimpleForm is not defined' do
90
+ before do
91
+ @simple_form = SimpleForm if defined?(SimpleForm)
92
+ Object.send(:remove_const, :SimpleForm) if defined?(SimpleForm)
93
+ end
94
+
95
+ after do
96
+ Object.const_set(:SimpleForm, @simple_form) if @simple_form
97
+ end
98
+
99
+ it 'does not raise error' do
100
+ initializer = described_class.initializers.find { |i| i.name == 'ckeditor5.simple_form' }
101
+ expect { initializer.run(Rails.application) }.not_to raise_error
102
+ end
85
103
  end
86
104
  end
87
105
  end
@@ -14,12 +14,10 @@ RSpec.describe CKEditor5::Rails::Plugins::WProofreader do
14
14
  expect(plugin.name).to eq(:WProofreader)
15
15
  end
16
16
 
17
- it 'returns correct preload assets urls' do
18
- expected_urls = [
19
- "#{default_cdn}@#{default_version}/dist/browser/index.css",
20
- "#{default_cdn}@#{default_version}/dist/browser/index.js"
21
- ]
22
- expect(plugin.preload_assets_urls).to eq(expected_urls)
17
+ it 'returns correct preload assets bundle' do
18
+ bundle = plugin.preload_assets_bundle
19
+ expect(bundle.stylesheets).to eq(["#{default_cdn}@#{default_version}/dist/browser/index.css"])
20
+ expect(bundle.scripts.first.url).to eq("#{default_cdn}@#{default_version}/dist/browser/index.js")
23
21
  end
24
22
 
25
23
  it 'returns correct hash representation' do
@@ -39,12 +37,10 @@ RSpec.describe CKEditor5::Rails::Plugins::WProofreader do
39
37
  let(:custom_version) { '4.0.0' }
40
38
  subject(:plugin) { described_class.new(version: custom_version, cdn: custom_cdn) }
41
39
 
42
- it 'returns correct preload assets urls with custom CDN' do
43
- expected_urls = [
44
- "#{custom_cdn}@#{custom_version}/dist/browser/index.css",
45
- "#{custom_cdn}@#{custom_version}/dist/browser/index.js"
46
- ]
47
- expect(plugin.preload_assets_urls).to eq(expected_urls)
40
+ it 'returns correct preload assets bundle with custom CDN' do
41
+ bundle = plugin.preload_assets_bundle
42
+ expect(bundle.stylesheets).to eq(["#{custom_cdn}@#{custom_version}/dist/browser/index.css"])
43
+ expect(bundle.scripts.first.url).to eq("#{custom_cdn}@#{custom_version}/dist/browser/index.js")
48
44
  end
49
45
 
50
46
  it 'returns correct hash representation with custom CDN' do
@@ -226,6 +226,48 @@ RSpec.describe CKEditor5::Rails::Presets::PresetBuilder do
226
226
  end
227
227
  end
228
228
 
229
+ describe '#plugin' do
230
+ it 'adds normalized plugin to config' do
231
+ plugin = builder.plugin('Test')
232
+
233
+ expect(builder.config[:plugins]).to include(plugin)
234
+ expect(plugin).to be_a(CKEditor5::Rails::Editor::PropsPlugin)
235
+ end
236
+
237
+ it 'accepts plugin options' do
238
+ plugin = builder.plugin('Test', premium: true)
239
+
240
+ expect(plugin.to_h[:import_name]).to eq('ckeditor5-premium-features')
241
+ end
242
+
243
+ it 'sets premium flag when premium option provided' do
244
+ builder.plugin('Test', premium: true)
245
+ expect(builder.premium?).to be true
246
+ end
247
+ end
248
+
249
+ describe '#external_plugin' do
250
+ it 'adds external plugin to config' do
251
+ plugin = builder.external_plugin('Test', script: 'https://example.org/script.js')
252
+
253
+ expect(builder.config[:plugins]).to include(plugin)
254
+ expect(plugin).to be_a(CKEditor5::Rails::Editor::PropsExternalPlugin)
255
+ end
256
+
257
+ it 'accepts plugin options' do
258
+ plugin = builder.external_plugin(
259
+ 'Test',
260
+ script: 'https://example.org/script.js',
261
+ import_as: 'ABC',
262
+ stylesheets: ['https://example.org/style.css']
263
+ )
264
+
265
+ expect(plugin.to_h[:import_name]).to eq('https://example.org/script.js')
266
+ expect(plugin.to_h[:import_as]).to eq('ABC')
267
+ expect(plugin.to_h[:stylesheets]).to include('https://example.org/style.css')
268
+ end
269
+ end
270
+
229
271
  describe '#cdn' do
230
272
  it 'returns current cdn when called without arguments' do
231
273
  expect(builder.cdn).to eq(:jsdelivr)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ckeditor5
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.18.3
4
+ version: 1.19.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-11-28 00:00:00.000000000 Z
12
+ date: 2024-11-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails