view_component_storybook 0.10.1 → 0.11.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: b6ef1bcb6cf8fd3beccec9242854aa913bd31fe1c7c44c44cdde6b59be8fd67f
4
- data.tar.gz: de047bb6e4e453db106270ba7f28b4d252fed881816266e10b99bb8c2d4b93f0
3
+ metadata.gz: 0dd708afcf2aa165339b0b1fde82ae610665554e3633c1e50b25509088b43e79
4
+ data.tar.gz: 950739b2892877a1451c262dc2110d6885a86c740c7fec9cd626a305feb8313d
5
5
  SHA512:
6
- metadata.gz: 4d96299b59366d1f50d034399299209a7edc01aa99f3ff728b0e6ed1721b1f80679aa8f7bbb953be549baa59fd74d090eb8d5b7e615bdc7b26164ebb0f011373
7
- data.tar.gz: d97204b485ac6d1b61c7848e6c2b68cec798328eef7e78738f3f675daa805dc6db73a0e6d1e6e7f17373de4b5ca90cfe000716bb4b25f29f8ae77d9985f0580e
6
+ metadata.gz: 3f9b37292f202ac1c79fd4d31b9ace6b067efff03bf60a6f6a7b44a241ce5661c3669a382d9e549461dfe391d9c77fe0b9b98d33a3a5a47ce821d47e9c26ec7d
7
+ data.tar.gz: 8e7db14af389ae4d4b2dbcf269269476ac9309c9e518226afedbef769e31f72d32ed44a61382f9830836cab5a4ceba45a701af0a66861de31f928713be9b6c55
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
- require "view_component/storybook"
5
4
 
6
5
  module ViewComponent
7
6
  module Storybook
@@ -45,3 +44,14 @@ module ViewComponent
45
44
  end
46
45
  end
47
46
  end
47
+
48
+ # :nocov:
49
+ unless defined?(ViewComponent::Storybook)
50
+ ActiveSupport::Deprecation.warn(
51
+ "This manually engine loading is deprecated and will be removed in v1.0.0. " \
52
+ "Remove `require \"view_component/storybook/engine\"`."
53
+ )
54
+
55
+ require "view_component/storybook"
56
+ end
57
+ # :nocov:
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Storybook
5
+ module MethodArgs
6
+ ##
7
+ # Class representing the constructor args for a Component
8
+ class ComponentConstructorArgs < ControlMethodArgs
9
+ def self.from_component_class(component_class, *args, **kwargs)
10
+ if DryInitializerComponentConstructorArgs.dry_initialize?(component_class)
11
+ DryInitializerComponentConstructorArgs.new(component_class, *args, **kwargs)
12
+ else
13
+ new(component_class, *args, **kwargs)
14
+ end
15
+ end
16
+
17
+ def initialize(component_class, *args, **kwargs)
18
+ super(component_class.instance_method(:initialize), *args, **kwargs)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Storybook
5
+ module MethodArgs
6
+ ##
7
+ # Class representing the constructor args for a Component the extends dry-initializer
8
+ class DryInitializerComponentConstructorArgs < ControlMethodArgs
9
+ INITIALIZE_METHOD = :__dry_initializer_initialize__
10
+
11
+ ##
12
+ # Method Parameters names that are dry-initialize aware
13
+ # THe main difference is that Dry Initializer keyword args cannot be deduced from the constructor initialize params
14
+ # Instead we have to introspect the dry_initializer config for the option definitions
15
+ class DryConstructorParametersNames < MethodParametersNames
16
+ attr_reader :dry_initializer
17
+
18
+ def initialize(component_class)
19
+ super(component_class.instance_method(INITIALIZE_METHOD))
20
+ @dry_initializer = component_class.dry_initializer
21
+ end
22
+
23
+ # Required keywords are the only thing we need.
24
+ # We could define kwarg_names similarly but wihout the `optional` check. However, dry-initializer
25
+ # always ends has supports_keyrest? == true so kwarg_names isn't needed
26
+ def req_kwarg_names
27
+ @req_kwarg_names ||= dry_initializer.definitions.map do |name, definition|
28
+ name if definition.option && !definition.optional
29
+ end.compact
30
+ end
31
+ end
32
+
33
+ def self.dry_initialize?(component_class)
34
+ component_class.private_method_defined?(INITIALIZE_METHOD)
35
+ end
36
+
37
+ def initialize(component_class, *args, **kwargs)
38
+ super(component_class.instance_method(INITIALIZE_METHOD), *args, **kwargs)
39
+
40
+ @target_method_params_names = DryConstructorParametersNames.new(component_class)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -57,7 +57,7 @@ module ViewComponent
57
57
  def kwarg_names
58
58
  @kwarg_names ||= parameters.map do |type, name|
59
59
  name if KWARG_TYPES.include?(type)
60
- end.compact.to_set
60
+ end.compact
61
61
  end
62
62
 
63
63
  def arg_names
@@ -9,8 +9,9 @@ module ViewComponent
9
9
 
10
10
  autoload :MethodArgs
11
11
  autoload :MethodParametersNames
12
- autoload :ValidatableMethodArgs
13
12
  autoload :ControlMethodArgs
13
+ autoload :ComponentConstructorArgs
14
+ autoload :DryInitializerComponentConstructorArgs
14
15
  end
15
16
  end
16
17
  end
@@ -4,10 +4,13 @@ module ViewComponent
4
4
  module Storybook
5
5
  module Slots
6
6
  class SlotConfig
7
+ include ActiveModel::Validations
7
8
  include ContentConcern
8
9
 
9
10
  attr_reader :slot_name, :slot_method_args, :param, :content_block
10
11
 
12
+ validate :validate_slot_method_args
13
+
11
14
  def initialize(slot_name, slot_method_args, param, content_block)
12
15
  @slot_name = slot_name
13
16
  @slot_method_args = slot_method_args
@@ -16,11 +19,12 @@ module ViewComponent
16
19
  end
17
20
 
18
21
  def self.from_component(component_class, slot_name, param, *args, **kwargs, &block)
19
- slot_method_args =
20
- MethodArgs::ControlMethodArgs
21
- .new(component_class.instance_method(slot_name), *args, **kwargs)
22
- .with_param_prefix(param)
23
- new(slot_name, slot_method_args, param, block)
22
+ new(
23
+ slot_name,
24
+ slot_method_args(component_class, slot_name, *args, **kwargs).with_param_prefix(param),
25
+ param,
26
+ block
27
+ )
24
28
  end
25
29
 
26
30
  def slot(componeont, params)
@@ -38,6 +42,37 @@ module ViewComponent
38
42
  def content_param
39
43
  "#{param}__content".to_sym
40
44
  end
45
+
46
+ private
47
+
48
+ def validate_slot_method_args
49
+ return if slot_method_args.valid?
50
+
51
+ slot_method_args_errors = slot_method_args.errors.full_messages.join(', ')
52
+ errors.add(:slot_method_args, :invalid, errors: slot_method_args_errors)
53
+ end
54
+
55
+ def self.slot_method_args(component_class, slot_name, *args, **kwargs)
56
+ # Reverse engineer the signature of the slot so that we can validate control args to the slot
57
+ # The slot methods themselves just have rest params so we can't introspect them. Instead we
58
+ # look for 'renderable' details of the registered slot
59
+ # This approach is tightly coupled to internal ViewCopmonent apis and might prove to be brittle
60
+ registred_slot_name = component_class.slot_type(slot_name) == :collection_item ? ActiveSupport::Inflector.pluralize(slot_name).to_sym : slot_name
61
+
62
+ registered_slot = component_class.registered_slots[registred_slot_name]
63
+
64
+ if registered_slot[:renderable] || registered_slot[:renderable_class_name]
65
+ # The slot is a component - either a class or a string representing the class
66
+ component_class = registered_slot[:renderable] || component_class.const_get(registered_slot[:renderable_class_name])
67
+ MethodArgs::ComponentConstructorArgs.from_component_class(component_class, *args, **kwargs)
68
+ else
69
+ # the slot is a lamba or a simple content slot
70
+ slot_lamba = registered_slot[:renderable_function] || proc {}
71
+ MethodArgs::ControlMethodArgs.new(slot_lamba, *args, **kwargs)
72
+ end
73
+ end
74
+
75
+ private_class_method :slot_method_args
41
76
  end
42
77
  end
43
78
  end
@@ -47,9 +47,7 @@ module ViewComponent
47
47
 
48
48
  def write_csf_json
49
49
  json_path = File.join(stories_path, "#{stories_name}.stories.json")
50
- File.open(json_path, "w") do |f|
51
- f.write(JSON.pretty_generate(to_csf_params))
52
- end
50
+ File.write(json_path, JSON.pretty_generate(to_csf_params))
53
51
  json_path
54
52
  end
55
53
 
@@ -9,7 +9,7 @@ module ViewComponent
9
9
 
10
10
  attr_reader :id, :name, :component_class
11
11
 
12
- validate :validate_constructor_args
12
+ validate :validate_constructor_args, :validate_slots_args
13
13
 
14
14
  def initialize(id, name, component_class, layout)
15
15
  @id = id
@@ -20,8 +20,8 @@ module ViewComponent
20
20
  end
21
21
 
22
22
  def constructor(*args, **kwargs, &block)
23
- @constructor_args = MethodArgs::ControlMethodArgs.new(
24
- component_constructor,
23
+ @constructor_args = MethodArgs::ComponentConstructorArgs.from_component_class(
24
+ component_class,
25
25
  *args,
26
26
  **kwargs
27
27
  )
@@ -117,11 +117,7 @@ module ViewComponent
117
117
  private
118
118
 
119
119
  def constructor_args
120
- @constructor_args ||= MethodArgs::ControlMethodArgs.new(component_constructor)
121
- end
122
-
123
- def component_constructor
124
- component_class.instance_method(:initialize)
120
+ @constructor_args ||= MethodArgs::ComponentConstructorArgs.from_component_class(component_class)
125
121
  end
126
122
 
127
123
  def slots
@@ -135,6 +131,13 @@ module ViewComponent
135
131
  errors.add(:constructor_args, :invalid, errors: constructor_args_errors)
136
132
  end
137
133
 
134
+ def validate_slots_args
135
+ slots.reject(&:valid?).each do |slot_config|
136
+ slot_errors = slot_config.errors.full_messages.join(', ')
137
+ errors.add(:slots, :invalid, errors: slot_errors)
138
+ end
139
+ end
140
+
138
141
  def slot(slot_name, *args, **kwargs, &block)
139
142
  # if the name is a slot then build a SlotConfig with slot_name and param the same
140
143
  if component_class.slot_type(slot_name) == :collection_item
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ViewComponent
4
4
  module Storybook
5
- VERSION = "0.10.1"
5
+ VERSION = "0.11.0"
6
6
  end
7
7
  end
@@ -41,6 +41,17 @@ module ViewComponent
41
41
  "/rails/stories"
42
42
  end
43
43
 
44
+ # :nocov:
45
+ if defined?(ViewComponent::Storybook::Engine)
46
+ ActiveSupport::Deprecation.warn(
47
+ "This manually engine loading is deprecated and will be removed in v1.0.0. " \
48
+ "Remove `require \"view_component/storybook/engine\"`."
49
+ )
50
+ elsif defined?(Rails::Engine)
51
+ require "view_component/storybook/engine"
52
+ end
53
+ # :nocov:
54
+
44
55
  ActiveSupport.run_load_hooks(:view_component_storybook, self)
45
56
  end
46
57
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component_storybook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Palmer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-08 00:00:00.000000000 Z
11
+ date: 2022-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: view_component
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dry-initializer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.4
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.4
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -210,7 +224,9 @@ files:
210
224
  - lib/view_component/storybook/dsl/legacy_controls_dsl.rb
211
225
  - lib/view_component/storybook/engine.rb
212
226
  - lib/view_component/storybook/method_args.rb
227
+ - lib/view_component/storybook/method_args/component_constructor_args.rb
213
228
  - lib/view_component/storybook/method_args/control_method_args.rb
229
+ - lib/view_component/storybook/method_args/dry_initializer_component_constructor_args.rb
214
230
  - lib/view_component/storybook/method_args/method_args.rb
215
231
  - lib/view_component/storybook/method_args/method_parameters_names.rb
216
232
  - lib/view_component/storybook/slots.rb
@@ -228,6 +244,7 @@ metadata:
228
244
  allowed_push_host: https://rubygems.org
229
245
  homepage_uri: https://github.com/jonspalmer/view_component_storybook
230
246
  source_code_uri: https://github.com/jonspalmer/view_component_storybook
247
+ rubygems_mfa_required: 'true'
231
248
  post_install_message:
232
249
  rdoc_options: []
233
250
  require_paths:
@@ -243,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
260
  - !ruby/object:Gem::Version
244
261
  version: '0'
245
262
  requirements: []
246
- rubygems_version: 3.1.6
263
+ rubygems_version: 3.0.6
247
264
  signing_key:
248
265
  specification_version: 4
249
266
  summary: Storybook for Rails View Components