ckeditor5 1.17.4 → 1.18.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 882d02a7893248e643ac6549d344940b08c946ef9e64193ca227551f4a9ed174
4
- data.tar.gz: d6e17a5e168c474c76f53b1be819c5a3248b46cb41e6144097088123aefeba6f
3
+ metadata.gz: e22407a5c1e37a744b94252008f0cba4be068964b5503661ef5441fb090831c7
4
+ data.tar.gz: eeb8757cf2c15de70ed40b9652071bfb1aa7bb9224a14842238d2d0670fa3f9e
5
5
  SHA512:
6
- metadata.gz: 48ded629378991aa8fac441598dac72a8006dd7cf11c094ac910eb0fe295bc099594dc36921563a37796132b483b05d74dba5f760980d1ce7d833ba0f33fb182
7
- data.tar.gz: 6ebf7b52994e7da6b52922f7cb970260deee6cc65c0a0e87f136f768e9d0abc5180431f2350a3608d22962ae888e58bf7bf410e2c310ff25fdde88dcdcc1c49e
6
+ metadata.gz: 06a0b97e9bd86bbba3c905b283f9fdebcf83dd5014bec69caca4898141f937db802b5036d5671dc2e72aa7c7f31b4174876e9ac9499696cf61a85e05d8c65e94
7
+ data.tar.gz: '09c16514d790101da1069e1de3db5a1ba59b28e70d322f41958cd62be12fc5ceb2fea06a7d474d566845186b468161cca1d596f2c764780baa09f1174ff49305'
data/Gemfile CHANGED
@@ -8,7 +8,6 @@ group :development do
8
8
  gem 'brakeman', '~> 6.1', '>= 6.1.1', require: false
9
9
  gem 'guard', '~> 2.19', '>= 2.19.0'
10
10
  gem 'guard-process', '~> 1.2'
11
- gem 'guard-rspec', '~> 4.7', '>= 4.7.3'
12
11
  gem 'pry', '~> 0.15', '>= 0.15.0'
13
12
  gem 'pry-rails', '~> 0.3', '>= 0.3.11'
14
13
  gem 'rails', '~> 7.0', '>= 7.0.0'
data/README.md CHANGED
@@ -129,7 +129,9 @@ For extending CKEditor's functionality, refer to the [plugins directory](https:/
129
129
  - [`plugin(name, premium:, import_name:)` method](#pluginname-premium-import_name-method)
130
130
  - [`plugins(*names, **kwargs)` method](#pluginsnames-kwargs-method)
131
131
  - [`inline_plugin(name, code)` method](#inline_pluginname-code-method)
132
+ - [`external_plugin(name, script:, import_as: nil, window_name: nil, stylesheets: [])` method](#external_pluginname-script-import_as-nil-window_name-nil-stylesheets--method)
132
133
  - [`simple_upload_adapter(url)` method](#simple_upload_adapterurl-method)
134
+ - [`wproofreader(version: nil, cdn: nil, **config)` method](#wproofreaderversion-nil-cdn-nil-config-method)
133
135
  - [Controller / View helpers 📦](#controller--view-helpers-)
134
136
  - [`ckeditor5_element_ref(selector)` method](#ckeditor5_element_refselector-method)
135
137
  - [`ckeditor5_preset(name = nil, &block)` method](#ckeditor5_presetname--nil-block-method)
@@ -790,6 +792,84 @@ end
790
792
  This approach is resistant to XSS attacks as it avoids inline JavaScript.
791
793
  </details>
792
794
 
795
+ #### `external_plugin(name, script:, import_as: nil, window_name: nil, stylesheets: [])` method
796
+
797
+ <details>
798
+ <summary>Define external CKEditor plugins directly in the configuration</summary>
799
+
800
+ <br />
801
+
802
+ Defines an external plugin to be included in the editor. You can pass the name of the plugin as an argument. The `script` keyword argument specifies the URL of the script to import the plugin from. The `import_as` keyword argument specifies the name of the package to import the plugin from. The `window_name` keyword argument specifies the name of the plugin in the window object. The `stylesheets` keyword argument specifies the URLs of the stylesheets to import.
803
+
804
+ The example below shows how to define an external plugin that highlights the text:
805
+
806
+ ```rb
807
+ # config/initializers/ckeditor5.rb
808
+
809
+ CKEditor5::Rails.configure do
810
+ # ... other configuration
811
+
812
+ external_plugin :MyExternalPlugin,
813
+ script: 'https://example.com/my-external-plugin.js'
814
+ end
815
+ ```
816
+
817
+ In order to import a plugin from a custom ESM package, you can pass the `import_as` keyword argument:
818
+
819
+ ```rb
820
+ # config/initializers/ckeditor5.rb
821
+
822
+ CKEditor5::Rails.configure do
823
+ # ... other configuration
824
+
825
+ external_plugin :MyExternalPlugin,
826
+ script: 'https://example.com/my-external-plugin.js',
827
+ import_as: 'Plugin'
828
+ end
829
+ ```
830
+
831
+ It's equivalent to the following JavaScript code:
832
+
833
+ ```js
834
+ import { Plugin } from 'my-external-plugin';
835
+ ```
836
+
837
+ In order to import a plugin from a custom Window entry, you can pass the `window_name` keyword argument:
838
+
839
+ ```rb
840
+ # config/initializers/ckeditor5.rb
841
+
842
+ CKEditor5::Rails.configure do
843
+ # ... other configuration
844
+
845
+ external_plugin :MyExternalPlugin,
846
+ script: 'https://example.com/my-external-plugin.js',
847
+ window_name: 'MyExternalPlugin'
848
+ end
849
+ ```
850
+
851
+ It's equivalent to the following JavaScript code:
852
+
853
+ ```js
854
+ const Plugin = window.MyExternalPlugin;
855
+ ```
856
+
857
+ In order to import a plugin with stylesheets, you can pass the `stylesheets` keyword argument:
858
+
859
+ ```rb
860
+ # config/initializers/ckeditor5.rb
861
+
862
+ CKEditor5::Rails.configure do
863
+ # ... other configuration
864
+
865
+ external_plugin :MyExternalPlugin,
866
+ script: 'https://example.com/my-external-plugin.js',
867
+ stylesheets: ['https://example.com/my-external-plugin.css']
868
+ end
869
+ ```
870
+
871
+ </details>
872
+
793
873
  #### `simple_upload_adapter(url)` method
794
874
 
795
875
  <details>
@@ -813,6 +893,43 @@ end
813
893
  ```
814
894
  </details>
815
895
 
896
+ #### `wproofreader(version: nil, cdn: nil, **config)` method
897
+
898
+ <details>
899
+ <summary>Configure WProofreader plugin</summary>
900
+
901
+ <br />
902
+
903
+ Defines the WProofreader plugin to be included in the editor. The example below shows how to include the WProofreader plugin:
904
+
905
+ ```rb
906
+ # config/initializers/ckeditor5.rb
907
+
908
+ CKEditor5::Rails.configure do
909
+ # ... other configuration
910
+
911
+ wproofreader serviceId: 'your-service-ID',
912
+ srcUrl: 'https://svc.webspellchecker.net/spellcheck31/wscbundle/wscbundle.js'
913
+ end
914
+ ```
915
+
916
+ The `version` keyword argument allows you to specify the version of the WProofreader plugin. The `cdn` keyword argument allows you to specify the CDN to be used for the WProofreader plugin.
917
+
918
+ ```rb
919
+ # config/initializers/ckeditor5.rb
920
+
921
+ CKEditor5::Rails.configure do
922
+ # ... other configuration
923
+
924
+ wproofreader version: '2.0.0',
925
+ cdn: :jsdelivr,
926
+ serviceId: 'your-service-ID',
927
+ srcUrl: 'https://svc.webspellchecker.net/spellcheck31/wscbundle/wscbundle.js'
928
+ end
929
+ ```
930
+
931
+ </details>
932
+
816
933
  ### Controller / View helpers 📦
817
934
 
818
935
  #### `ckeditor5_element_ref(selector)` method
@@ -41,7 +41,7 @@ module CKEditor5::Rails::Assets
41
41
  class JSUrlImportMeta
42
42
  attr_reader :url, :import_meta
43
43
 
44
- delegate :esm?, :window?, :import_name, :window_name, :import_as, :to_h, to: :import_meta
44
+ delegate :esm?, :window?, :import_name, :window_name, :import_as, to: :import_meta
45
45
 
46
46
  def initialize(url, translation: false, **import_options)
47
47
  @url = url
@@ -52,6 +52,10 @@ module CKEditor5::Rails::Assets
52
52
  def translation?
53
53
  @translation
54
54
  end
55
+
56
+ def to_h
57
+ import_meta.to_h.merge({ url: url })
58
+ end
55
59
  end
56
60
 
57
61
  class JSImportMeta
@@ -64,14 +68,14 @@ module CKEditor5::Rails::Assets
64
68
  @window_name = window_name
65
69
  end
66
70
 
67
- def esm?
68
- import_name.present?
69
- end
70
-
71
71
  def window?
72
72
  window_name.present?
73
73
  end
74
74
 
75
+ def esm?
76
+ import_name.present?
77
+ end
78
+
75
79
  def to_h
76
80
  {
77
81
  import_as: import_as,
@@ -44,7 +44,11 @@ function loadAsyncImports(imports = []) {
44
44
  return module.default;
45
45
  };
46
46
 
47
- const loadExternalPlugin = async ({ import_name, import_as, window_name }) => {
47
+ const loadExternalPlugin = async ({ import_name, import_as, window_name, stylesheets }) => {
48
+ if (stylesheets?.length) {
49
+ await loadAsyncCSS(stylesheets);
50
+ }
51
+
48
52
  if (window_name) {
49
53
  if (!Object.prototype.hasOwnProperty.call(window, window_name)) {
50
54
  throw new Error(
@@ -60,7 +64,11 @@ function loadAsyncImports(imports = []) {
60
64
  const imported = module[import_as || 'default'];
61
65
 
62
66
  if (!imported) {
63
- throw new Error(`Plugin "${import_as}" not found in the ESM module "${import_name}"!`);
67
+ throw new Error(
68
+ `Plugin "${import_as || 'default'}" not found in the ESM module ` +
69
+ `"${import_name}"! Available imports: ${Object.keys(module).join(', ')}! ` +
70
+ 'Consider changing "import_as" value.'
71
+ );
64
72
  }
65
73
 
66
74
  return imported;
@@ -78,6 +86,35 @@ function loadAsyncImports(imports = []) {
78
86
  }));
79
87
  }
80
88
 
89
+ /**
90
+ * Dynamically loads CSS files based on configuration
91
+ *
92
+ * @param {Array<string>} imports - Array of CSS file URLs to load
93
+ * @returns {Promise<Array<void>>} Array of promises for each CSS file load
94
+ * @throws {Error} When CSS file loading fails
95
+ */
96
+ function loadAsyncCSS(stylesheets = []) {
97
+ const promises = stylesheets.map(href =>
98
+ new Promise((resolve, reject) => {
99
+ const link = document.createElement('link');
100
+
101
+ link.rel = 'stylesheet';
102
+ link.href = href;
103
+
104
+ link.onerror = reject;
105
+ link.onload = () => {
106
+ resolve();
107
+ };
108
+
109
+ document.head.appendChild(link);
110
+
111
+ return link;
112
+ })
113
+ );
114
+
115
+ return Promise.all(promises);
116
+ }
117
+
81
118
  /**
82
119
  * Checks if a key is safe to use in configuration objects to prevent prototype pollution
83
120
  *
@@ -19,7 +19,7 @@ module CKEditor5::Rails
19
19
  delegate :config, to: :@preset
20
20
 
21
21
  def serialize_plugins
22
- (config[:plugins] || []).map { |plugin| Editor::PropsPlugin.normalize(plugin).to_h }.to_json
22
+ (config[:plugins] || []).map { |plugin| Editor::PropsBasePlugin.normalize(plugin).to_h }.to_json
23
23
  end
24
24
 
25
25
  def serialize_config
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'props_plugin'
4
4
  require_relative 'props_inline_plugin'
5
+ require_relative 'props_external_plugin'
5
6
  require_relative 'props'
6
7
 
7
8
  module CKEditor5::Rails::Editor::Helpers
@@ -57,7 +57,7 @@ module CKEditor5::Rails::Editor
57
57
  end
58
58
 
59
59
  def serialize_plugins
60
- (config[:plugins] || []).map { |plugin| PropsPlugin.normalize(plugin).to_h }.to_json
60
+ (config[:plugins] || []).map { |plugin| PropsBasePlugin.normalize(plugin).to_h }.to_json
61
61
  end
62
62
 
63
63
  def serialize_config
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails::Editor
4
+ class PropsBasePlugin
5
+ attr_reader :name, :assets_bundle
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def preload_assets_urls
12
+ []
13
+ end
14
+
15
+ def to_h
16
+ raise NotImplementedError, 'Method #to_h must be implemented in a subclass'
17
+ end
18
+
19
+ def self.normalize(plugin, **kwargs)
20
+ case plugin
21
+ when String, Symbol then PropsPlugin.new(plugin, **kwargs)
22
+ when PropsBasePlugin then plugin
23
+ else raise ArgumentError, "Invalid plugin: #{plugin}"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'props_base_plugin'
4
+
5
+ module CKEditor5::Rails::Editor
6
+ class PropsExternalPlugin < PropsBasePlugin
7
+ attr_reader :stylesheets, :js_import_meta
8
+
9
+ def initialize(name, script:, import_as: nil, window_name: nil, stylesheets: [])
10
+ super(name)
11
+
12
+ @stylesheets = stylesheets
13
+ @js_import_meta = CKEditor5::Rails::Assets::JSUrlImportMeta.new(
14
+ script,
15
+ import_name: script,
16
+ import_as: import_as,
17
+ window_name: window_name
18
+ )
19
+ end
20
+
21
+ def preload_assets_urls
22
+ @stylesheets + [@js_import_meta.url]
23
+ end
24
+
25
+ def to_h
26
+ @js_import_meta.to_h.merge(
27
+ type: :external,
28
+ stylesheets: @stylesheets
29
+ )
30
+ end
31
+ end
32
+ end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'props_base_plugin'
4
+
3
5
  module CKEditor5::Rails::Editor
4
- class PropsInlinePlugin
5
- attr_reader :name, :code
6
+ class PropsInlinePlugin < PropsBasePlugin
7
+ attr_reader :code
6
8
 
7
9
  def initialize(name, code)
8
- @name = name
10
+ super(name)
11
+
9
12
  @code = code
10
13
  validate_code!
11
14
  end
@@ -1,36 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module CKEditor5::Rails::Editor
4
- class PropsPlugin
5
- attr_reader :name, :js_import_meta
3
+ require_relative 'props_base_plugin'
6
4
 
7
- delegate :to_h, to: :import_meta
5
+ module CKEditor5::Rails::Editor
6
+ class PropsPlugin < PropsBasePlugin
7
+ def initialize(name, premium: false, **js_import_meta_attrs)
8
+ super(name)
8
9
 
9
- def initialize(name, premium: false, **js_import_meta)
10
- @name = name
11
- @js_import_meta = if js_import_meta.empty?
12
- { import_name: premium ? 'ckeditor5-premium-features' : 'ckeditor5' }
13
- else
14
- js_import_meta
15
- end
16
- end
10
+ js_import_meta_attrs[:import_name] ||= if premium
11
+ 'ckeditor5-premium-features'
12
+ else
13
+ 'ckeditor5'
14
+ end
17
15
 
18
- def self.normalize(plugin)
19
- case plugin
20
- when String, Symbol then new(plugin)
21
- when PropsPlugin, PropsInlinePlugin then plugin
22
- else raise ArgumentError, "Invalid plugin: #{plugin}"
23
- end
16
+ @js_import_meta = ::CKEditor5::Rails::Assets::JSImportMeta.new(
17
+ import_as: js_import_meta_attrs[:window_name] ? nil : name,
18
+ **js_import_meta_attrs
19
+ )
24
20
  end
25
21
 
26
22
  def to_h
27
- meta = ::CKEditor5::Rails::Assets::JSImportMeta.new(
28
- import_as: js_import_meta[:window_name] ? nil : name,
29
- **js_import_meta
30
- ).to_h
31
-
32
- meta.merge!({ type: :external })
33
- meta
23
+ @js_import_meta.to_h.merge(type: :external)
34
24
  end
35
25
  end
36
26
  end
@@ -4,7 +4,9 @@ require 'rails/engine'
4
4
 
5
5
  require_relative 'presets/manager'
6
6
  require_relative 'hooks/form'
7
+
7
8
  require_relative 'plugins/simple_upload_adapter'
9
+ require_relative 'plugins/wproofreader'
8
10
 
9
11
  module CKEditor5::Rails
10
12
  class Engine < ::Rails::Engine
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../editor/props_external_plugin'
4
+
5
+ module CKEditor5::Rails::Plugins
6
+ class WProofreader < CKEditor5::Rails::Editor::PropsExternalPlugin
7
+ DEFAULT_VERSION = '3.1.2'
8
+ DEFAULT_CDN = 'https://cdn.jsdelivr.net/npm/@webspellchecker/wproofreader-ckeditor5'
9
+
10
+ def initialize(version: nil, cdn: nil)
11
+ cdn ||= DEFAULT_CDN
12
+ version ||= DEFAULT_VERSION
13
+
14
+ script_url = "#{cdn}@#{version}/dist/browser/index.js"
15
+ style_url = "#{cdn}@#{version}/dist/browser/index.css"
16
+
17
+ super(
18
+ :WProofreader,
19
+ script: script_url,
20
+ import_as: 'WProofreader',
21
+ stylesheets: [style_url],
22
+ )
23
+ end
24
+ end
25
+ end
@@ -4,14 +4,25 @@ module CKEditor5::Rails
4
4
  module Presets
5
5
  module Concerns
6
6
  module PluginMethods
7
+ private
8
+
9
+ def register_plugin(plugin_obj)
10
+ config[:plugins] << plugin_obj
11
+ plugin_obj
12
+ end
13
+
14
+ public
15
+
16
+ def external_plugin(name, **kwargs)
17
+ register_plugin(Editor::PropsExternalPlugin.new(name, **kwargs))
18
+ end
19
+
7
20
  def inline_plugin(name, code)
8
- config[:plugins] << Editor::PropsInlinePlugin.new(name, code)
21
+ register_plugin(Editor::PropsInlinePlugin.new(name, code))
9
22
  end
10
23
 
11
24
  def plugin(name, **kwargs)
12
- plugin_obj = PluginsBuilder.create_plugin(name, **kwargs)
13
- config[:plugins] << plugin_obj
14
- plugin_obj
25
+ register_plugin(PluginsBuilder.create_plugin(name, **kwargs))
15
26
  end
16
27
 
17
28
  def plugins(*names, **kwargs, &block)
@@ -9,7 +9,7 @@ module CKEditor5::Rails
9
9
  end
10
10
 
11
11
  def self.create_plugin(name, **kwargs)
12
- if name.is_a?(Editor::PropsInlinePlugin) || name.is_a?(Editor::PropsPlugin)
12
+ if name.is_a?(Editor::PropsBasePlugin)
13
13
  name
14
14
  else
15
15
  Editor::PropsPlugin.new(name, **kwargs)
@@ -204,6 +204,11 @@ module CKEditor5::Rails
204
204
  configure(:simpleUpload, { uploadUrl: upload_url })
205
205
  end
206
206
 
207
+ def wproofreader(version: nil, cdn: nil, **config)
208
+ plugin(Plugins::WProofreader.new(version: version, cdn: cdn))
209
+ configure :wproofreader, config
210
+ end
211
+
207
212
  private
208
213
 
209
214
  def deep_copy_toolbar(toolbar)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.17.4'
5
+ VERSION = '1.18.0'
6
6
 
7
7
  DEFAULT_CKEDITOR_VERSION = '43.3.1'
8
8
  end
@@ -157,6 +157,17 @@ RSpec.describe CKEditor5::Rails::Assets::JSUrlImportMeta do
157
157
  expect(meta).to be_translation
158
158
  end
159
159
  end
160
+
161
+ describe '#to_h' do
162
+ it 'returns hash with url and import meta' do
163
+ meta = described_class.new(url, import_name: 'module', import_as: 'alias')
164
+ expect(meta.to_h).to eq({
165
+ url: url,
166
+ import_name: 'module',
167
+ import_as: 'alias'
168
+ })
169
+ end
170
+ end
160
171
  end
161
172
 
162
173
  RSpec.describe CKEditor5::Rails::Assets::JSImportMeta do
@@ -54,7 +54,7 @@ RSpec.describe CKEditor5::Rails::Context::PresetBuilder do
54
54
  it 'accepts plugin options' do
55
55
  plugin = builder.plugin('Test', premium: true)
56
56
 
57
- expect(plugin.js_import_meta[:import_name]).to eq('ckeditor5-premium-features')
57
+ expect(plugin.to_h[:import_name]).to eq('ckeditor5-premium-features')
58
58
  end
59
59
  end
60
60
 
@@ -82,4 +82,41 @@ RSpec.describe CKEditor5::Rails::Context::PresetBuilder do
82
82
  expect(builder.config[:plugins].map(&:name)).to eq(%w[Test1 Test2])
83
83
  end
84
84
  end
85
+
86
+ describe '#inline_plugin' do
87
+ it 'adds inline plugin to config' do
88
+ plugin = builder.inline_plugin('Test', 'export default class Abc {}')
89
+
90
+ expect(builder.config[:plugins]).to include(plugin)
91
+ expect(plugin).to be_a(CKEditor5::Rails::Editor::PropsInlinePlugin)
92
+ end
93
+
94
+ it 'accepts plugin options' do
95
+ plugin = builder.inline_plugin('Test', 'export default class Abc {}')
96
+
97
+ expect(plugin.code).to eq('export default class Abc {}')
98
+ end
99
+ end
100
+
101
+ describe '#external_plugin' do
102
+ it 'adds external plugin to config' do
103
+ plugin = builder.external_plugin('Test', script: 'https://example.org/script.js')
104
+
105
+ expect(builder.config[:plugins]).to include(plugin)
106
+ expect(plugin).to be_a(CKEditor5::Rails::Editor::PropsExternalPlugin)
107
+ end
108
+
109
+ it 'accepts plugin options' do
110
+ plugin = builder.external_plugin(
111
+ 'Test',
112
+ script: 'https://example.org/script.js',
113
+ import_as: 'ABC',
114
+ stylesheets: ['https://example.org/style.css']
115
+ )
116
+
117
+ expect(plugin.to_h[:import_name]).to eq('https://example.org/script.js')
118
+ expect(plugin.to_h[:import_as]).to eq('ABC')
119
+ expect(plugin.to_h[:stylesheets]).to include('https://example.org/style.css')
120
+ end
121
+ end
85
122
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Editor::PropsBasePlugin do
6
+ describe '.normalize' do
7
+ it 'converts string to plugin instance' do
8
+ plugin = described_class.normalize('Bold')
9
+ expect(plugin).to be_a(described_class)
10
+ expect(plugin.name).to eq('Bold')
11
+ end
12
+
13
+ it 'converts symbol to plugin instance' do
14
+ plugin = described_class.normalize(:Bold)
15
+ expect(plugin).to be_a(described_class)
16
+ expect(plugin.name).to eq(:Bold)
17
+ end
18
+
19
+ it 'returns existing plugin instances unchanged' do
20
+ original = described_class.new(:Bold)
21
+ plugin = described_class.normalize(original)
22
+ expect(plugin).to be(original)
23
+ end
24
+
25
+ it 'returns inline plugin instances unchanged' do
26
+ inline = CKEditor5::Rails::Editor::PropsInlinePlugin.new(:Custom, 'export default class {}')
27
+ plugin = described_class.normalize(inline)
28
+ expect(plugin).to be(inline)
29
+ end
30
+
31
+ it 'raises error for invalid input' do
32
+ expect { described_class.normalize({}) }.to raise_error(ArgumentError)
33
+ end
34
+ end
35
+
36
+ describe '#preload_assets_urls' do
37
+ it 'returns empty array' do
38
+ plugin = described_class.new(:Bold)
39
+
40
+ expect(plugin.preload_assets_urls).to eq([])
41
+ end
42
+ end
43
+
44
+ describe '#to_h' do
45
+ it 'raises NotImplementedError' do
46
+ plugin = described_class.new(:Bold)
47
+ expect do
48
+ plugin.to_h
49
+ end.to raise_error(NotImplementedError, 'Method #to_h must be implemented in a subclass')
50
+ end
51
+ end
52
+
53
+ describe '#name' do
54
+ it 'returns the plugin name' do
55
+ plugin = described_class.new(:Bold)
56
+ expect(plugin.name).to eq(:Bold)
57
+ end
58
+
59
+ it 'preserves the type of name argument' do
60
+ string_plugin = described_class.new('Bold')
61
+ symbol_plugin = described_class.new(:Bold)
62
+
63
+ expect(string_plugin.name).to eq('Bold')
64
+ expect(symbol_plugin.name).to eq(:Bold)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Editor::PropsExternalPlugin do
6
+ describe '#initialize' do
7
+ it 'creates plugin with required parameters' do
8
+ plugin = described_class.new('Test', script: 'https://example.org/plugin.js')
9
+
10
+ expect(plugin.name).to eq('Test')
11
+ expect(plugin.preload_assets_urls).to include('https://example.org/plugin.js')
12
+ end
13
+
14
+ it 'accepts optional parameters' do
15
+ plugin = described_class.new(
16
+ 'Test',
17
+ script: 'https://example.org/plugin.js',
18
+ import_as: 'TestPlugin',
19
+ window_name: 'TestWindow',
20
+ stylesheets: ['https://example.org/style.css']
21
+ )
22
+
23
+ expect(plugin.preload_assets_urls).to include('https://example.org/style.css')
24
+ end
25
+ end
26
+
27
+ describe '#preload_assets_urls' do
28
+ it 'returns array with script and stylesheets urls' do
29
+ plugin = described_class.new(
30
+ 'Test',
31
+ script: 'https://example.org/plugin.js',
32
+ stylesheets: ['https://example.org/style1.css', 'https://example.org/style2.css']
33
+ )
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
+ ])
40
+ end
41
+ end
42
+
43
+ describe '#to_h' do
44
+ it 'returns hash with plugin configuration' do
45
+ plugin = described_class.new(
46
+ 'Test',
47
+ script: 'https://example.org/plugin.js',
48
+ import_as: 'TestPlugin',
49
+ window_name: 'TestWindow',
50
+ stylesheets: ['https://example.org/style.css']
51
+ )
52
+
53
+ expect(plugin.to_h).to include(
54
+ type: :external,
55
+ import_name: 'https://example.org/plugin.js',
56
+ import_as: 'TestPlugin',
57
+ window_name: 'TestWindow',
58
+ stylesheets: ['https://example.org/style.css']
59
+ )
60
+ end
61
+ end
62
+ end
@@ -3,36 +3,6 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe CKEditor5::Rails::Editor::PropsPlugin do
6
- describe '.normalize' do
7
- it 'converts string to plugin instance' do
8
- plugin = described_class.normalize('Bold')
9
- expect(plugin).to be_a(described_class)
10
- expect(plugin.name).to eq('Bold')
11
- end
12
-
13
- it 'converts symbol to plugin instance' do
14
- plugin = described_class.normalize(:Bold)
15
- expect(plugin).to be_a(described_class)
16
- expect(plugin.name).to eq(:Bold)
17
- end
18
-
19
- it 'returns existing plugin instances unchanged' do
20
- original = described_class.new(:Bold)
21
- plugin = described_class.normalize(original)
22
- expect(plugin).to be(original)
23
- end
24
-
25
- it 'returns inline plugin instances unchanged' do
26
- inline = CKEditor5::Rails::Editor::PropsInlinePlugin.new(:Custom, 'export default class {}')
27
- plugin = described_class.normalize(inline)
28
- expect(plugin).to be(inline)
29
- end
30
-
31
- it 'raises error for invalid input' do
32
- expect { described_class.normalize({}) }.to raise_error(ArgumentError)
33
- end
34
- end
35
-
36
6
  describe '#to_h' do
37
7
  it 'generates hash for standard plugin' do
38
8
  plugin = described_class.new(:Bold)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Plugins::WProofreader do
6
+ let(:default_cdn) { 'https://cdn.jsdelivr.net/npm/@webspellchecker/wproofreader-ckeditor5' }
7
+ let(:default_version) { '3.1.2' }
8
+
9
+ describe '#initialize' do
10
+ context 'with default parameters' do
11
+ subject(:plugin) { described_class.new }
12
+
13
+ it 'has correct name' do
14
+ expect(plugin.name).to eq(:WProofreader)
15
+ end
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)
23
+ end
24
+
25
+ it 'returns correct hash representation' do
26
+ expected_hash = {
27
+ type: :external,
28
+ stylesheets: ["#{default_cdn}@#{default_version}/dist/browser/index.css"],
29
+ url: "#{default_cdn}@#{default_version}/dist/browser/index.js",
30
+ import_name: "#{default_cdn}@#{default_version}/dist/browser/index.js",
31
+ import_as: 'WProofreader'
32
+ }
33
+ expect(plugin.to_h).to eq(expected_hash)
34
+ end
35
+ end
36
+
37
+ context 'with custom parameters' do
38
+ let(:custom_cdn) { 'https://custom-cdn.com/wproofreader' }
39
+ let(:custom_version) { '4.0.0' }
40
+ subject(:plugin) { described_class.new(version: custom_version, cdn: custom_cdn) }
41
+
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)
48
+ end
49
+
50
+ it 'returns correct hash representation with custom CDN' do
51
+ expected_hash = {
52
+ type: :external,
53
+ stylesheets: ["#{custom_cdn}@#{custom_version}/dist/browser/index.css"],
54
+ url: "#{custom_cdn}@#{custom_version}/dist/browser/index.js",
55
+ import_name: "#{custom_cdn}@#{custom_version}/dist/browser/index.js",
56
+ import_as: 'WProofreader'
57
+ }
58
+ expect(plugin.to_h).to eq(expected_hash)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -380,4 +380,22 @@ RSpec.describe CKEditor5::Rails::Presets::PresetBuilder do
380
380
  end
381
381
  end
382
382
  end
383
+
384
+ describe 'wproofreader' do
385
+ it 'configures WProofreader plugin' do
386
+ builder.wproofreader(version: '1.0.0', cdn: 'https://cdn.example.com')
387
+ plugin = builder.config[:plugins].first
388
+
389
+ expect(plugin).to be_a(CKEditor5::Rails::Editor::PropsExternalPlugin)
390
+ expect(plugin.name).to eq(:WProofreader)
391
+ expect(plugin.to_h[:import_name]).to eq('https://cdn.example.com@1.0.0/dist/browser/index.js')
392
+ expect(plugin.to_h[:stylesheets]).to eq(['https://cdn.example.com@1.0.0/dist/browser/index.css'])
393
+ end
394
+
395
+ it 'sets proper editor configuration in wproofreader key' do
396
+ builder.wproofreader(version: '1.0.0', cdn: 'https://cdn.example.com', language: 'en')
397
+
398
+ expect(builder.config[:wproofreader]).to eq({ language: 'en' })
399
+ end
400
+ end
383
401
  end
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.17.4
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński
@@ -63,6 +63,8 @@ files:
63
63
  - lib/ckeditor5/rails/editor/helpers/config_helpers.rb
64
64
  - lib/ckeditor5/rails/editor/helpers/editor_helpers.rb
65
65
  - lib/ckeditor5/rails/editor/props.rb
66
+ - lib/ckeditor5/rails/editor/props_base_plugin.rb
67
+ - lib/ckeditor5/rails/editor/props_external_plugin.rb
66
68
  - lib/ckeditor5/rails/editor/props_inline_plugin.rb
67
69
  - lib/ckeditor5/rails/editor/props_plugin.rb
68
70
  - lib/ckeditor5/rails/engine.rb
@@ -70,6 +72,7 @@ files:
70
72
  - lib/ckeditor5/rails/hooks/form.rb
71
73
  - lib/ckeditor5/rails/hooks/simple_form.rb
72
74
  - lib/ckeditor5/rails/plugins/simple_upload_adapter.rb
75
+ - lib/ckeditor5/rails/plugins/wproofreader.rb
73
76
  - lib/ckeditor5/rails/presets/concerns/configuration_methods.rb
74
77
  - lib/ckeditor5/rails/presets/concerns/plugin_methods.rb
75
78
  - lib/ckeditor5/rails/presets/manager.rb
@@ -98,12 +101,15 @@ files:
98
101
  - spec/lib/ckeditor5/rails/editor/editable_height_normalizer_spec.rb
99
102
  - spec/lib/ckeditor5/rails/editor/helpers/config_helpers_spec.rb
100
103
  - spec/lib/ckeditor5/rails/editor/helpers/editor_helpers_spec.rb
104
+ - spec/lib/ckeditor5/rails/editor/props_base_plugin_spec.rb
105
+ - spec/lib/ckeditor5/rails/editor/props_external_plugin_spec.rb
101
106
  - spec/lib/ckeditor5/rails/editor/props_inline_plugin_spec.rb
102
107
  - spec/lib/ckeditor5/rails/editor/props_plugin_spec.rb
103
108
  - spec/lib/ckeditor5/rails/editor/props_spec.rb
104
109
  - spec/lib/ckeditor5/rails/engine_spec.rb
105
110
  - spec/lib/ckeditor5/rails/hooks/form_spec.rb
106
111
  - spec/lib/ckeditor5/rails/hooks/simple_form_spec.rb
112
+ - spec/lib/ckeditor5/rails/plugins/wproofreader_spec.rb
107
113
  - spec/lib/ckeditor5/rails/presets/manager_spec.rb
108
114
  - spec/lib/ckeditor5/rails/presets/plugins_builder_spec.rb
109
115
  - spec/lib/ckeditor5/rails/presets/preset_builder_spec.rb
@@ -160,12 +166,15 @@ test_files:
160
166
  - spec/lib/ckeditor5/rails/editor/editable_height_normalizer_spec.rb
161
167
  - spec/lib/ckeditor5/rails/editor/helpers/config_helpers_spec.rb
162
168
  - spec/lib/ckeditor5/rails/editor/helpers/editor_helpers_spec.rb
169
+ - spec/lib/ckeditor5/rails/editor/props_base_plugin_spec.rb
170
+ - spec/lib/ckeditor5/rails/editor/props_external_plugin_spec.rb
163
171
  - spec/lib/ckeditor5/rails/editor/props_inline_plugin_spec.rb
164
172
  - spec/lib/ckeditor5/rails/editor/props_plugin_spec.rb
165
173
  - spec/lib/ckeditor5/rails/editor/props_spec.rb
166
174
  - spec/lib/ckeditor5/rails/engine_spec.rb
167
175
  - spec/lib/ckeditor5/rails/hooks/form_spec.rb
168
176
  - spec/lib/ckeditor5/rails/hooks/simple_form_spec.rb
177
+ - spec/lib/ckeditor5/rails/plugins/wproofreader_spec.rb
169
178
  - spec/lib/ckeditor5/rails/presets/manager_spec.rb
170
179
  - spec/lib/ckeditor5/rails/presets/plugins_builder_spec.rb
171
180
  - spec/lib/ckeditor5/rails/presets/preset_builder_spec.rb