view_component_storybook 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -138
  3. data/app/controllers/view_component/storybook/stories_controller.rb +9 -11
  4. data/app/views/view_component/storybook/stories/show.html.erb +8 -3
  5. data/config/locales/en.yml +1 -1
  6. data/lib/view_component/storybook.rb +14 -2
  7. data/lib/view_component/storybook/content_concern.rb +42 -0
  8. data/lib/view_component/storybook/controls.rb +3 -1
  9. data/lib/view_component/storybook/controls/base_options_config.rb +41 -0
  10. data/lib/view_component/storybook/controls/control_config.rb +4 -0
  11. data/lib/view_component/storybook/{dsl/controls_dsl.rb → controls/controls_helpers.rb} +21 -16
  12. data/lib/view_component/storybook/controls/custom_config.rb +2 -4
  13. data/lib/view_component/storybook/controls/multi_options_config.rb +46 -0
  14. data/lib/view_component/storybook/controls/options_config.rb +12 -26
  15. data/lib/view_component/storybook/controls/simple_control_config.rb +1 -1
  16. data/lib/view_component/storybook/dsl.rb +0 -2
  17. data/lib/view_component/storybook/dsl/legacy_controls_dsl.rb +2 -2
  18. data/lib/view_component/storybook/engine.rb +2 -1
  19. data/lib/view_component/storybook/method_args/control_method_args.rb +15 -12
  20. data/lib/view_component/storybook/method_args/method_args.rb +37 -4
  21. data/lib/view_component/storybook/slots.rb +14 -0
  22. data/lib/view_component/storybook/slots/slot.rb +24 -0
  23. data/lib/view_component/storybook/slots/slot_config.rb +44 -0
  24. data/lib/view_component/storybook/stories.rb +16 -7
  25. data/lib/view_component/storybook/story.rb +18 -0
  26. data/lib/view_component/storybook/story_config.rb +115 -24
  27. data/lib/view_component/storybook/tasks/write_stories_json.rake +1 -1
  28. data/lib/view_component/storybook/version.rb +1 -1
  29. metadata +27 -9
  30. data/lib/view_component/storybook/controls/array_config.rb +0 -37
  31. data/lib/view_component/storybook/dsl/story_dsl.rb +0 -51
  32. data/lib/view_component/storybook/method_args/validatable_method_args.rb +0 -50
@@ -4,37 +4,76 @@ module ViewComponent
4
4
  module Storybook
5
5
  class StoryConfig
6
6
  include ActiveModel::Validations
7
+ include ContentConcern
8
+ include Controls::ControlsHelpers
7
9
 
8
- attr_reader :id, :name, :component
9
- attr_accessor :parameters, :layout, :content_block
10
+ attr_reader :id, :name, :component_class
10
11
 
11
12
  validate :validate_constructor_args
12
13
 
13
- def initialize(id, name, component, layout)
14
+ def initialize(id, name, component_class, layout)
14
15
  @id = id
15
16
  @name = name
16
- @component = component
17
+ @component_class = component_class
17
18
  @layout = layout
19
+ @slots ||= {}
18
20
  end
19
21
 
20
- def constructor_args(*args, **kwargs, &block)
21
- if args.empty? && kwargs.empty? && block.nil?
22
- @constructor_args ||= ViewComponent::Storybook::MethodArgs::ControlMethodArgs.new(component_constructor)
22
+ def constructor(*args, **kwargs, &block)
23
+ @constructor_args = MethodArgs::ControlMethodArgs.new(
24
+ component_constructor,
25
+ *args,
26
+ **kwargs
27
+ )
28
+ content(nil, &block)
29
+
30
+ self
31
+ end
32
+
33
+ # Once deprecated block version is removed make this a private getter
34
+ def controls(&block)
35
+ if block_given?
36
+ ActiveSupport::Deprecation.warn("`controls` will be removed in v1.0.0. Use `#constructor` instead.")
37
+ controls_dsl = Dsl::LegacyControlsDsl.new
38
+ controls_dsl.instance_eval(&block)
39
+
40
+ controls_hash = controls_dsl.controls.index_by(&:param)
41
+ constructor(**controls_hash)
23
42
  else
24
- @constructor_args = ViewComponent::Storybook::MethodArgs::ControlMethodArgs.new(
25
- component_constructor,
26
- *args,
27
- **kwargs,
28
- &block
29
- )
43
+ list = constructor_args.controls.dup
44
+ list << content_control if content_control
45
+ list += slots.flat_map(&:controls) if slots
46
+ list
30
47
  end
31
48
  end
32
49
 
50
+ def layout(layout = nil)
51
+ @layout = layout unless layout.nil?
52
+ @layout
53
+ end
54
+
55
+ def parameters(parameters = nil)
56
+ @parameters = parameters unless parameters.nil?
57
+ @parameters
58
+ end
59
+
60
+ def method_missing(method_name, *args, **kwargs, &block)
61
+ if component_class.slot_type(method_name)
62
+ slot(method_name, *args, **kwargs, &block)
63
+ else
64
+ super
65
+ end
66
+ end
67
+
68
+ def respond_to_missing?(method_name, _include_private = false)
69
+ component_class.slot_type(method_name).present?
70
+ end
71
+
33
72
  def to_csf_params
34
73
  validate!
35
74
  csf_params = { name: name, parameters: { server: { id: id } } }
36
75
  csf_params.deep_merge!(parameters: parameters) if parameters.present?
37
- constructor_args.controls.each do |control|
76
+ controls.each do |control|
38
77
  csf_params.deep_merge!(control.to_csf_params)
39
78
  end
40
79
  csf_params
@@ -44,17 +83,25 @@ module ViewComponent
44
83
  valid? || raise(ValidationError, self)
45
84
  end
46
85
 
47
- def self.configure(id, name, component, layout, &configuration)
48
- config = new(id, name, component, layout)
49
- ViewComponent::Storybook::Dsl::StoryDsl.evaluate!(config, &configuration)
50
- config
51
- end
86
+ ##
87
+ # Build a Story from this config
88
+ # * Resolves the values of the constructor args from the params
89
+ # * constructs the component
90
+ # * resolve the content_control and content_block to a single block
91
+ # * builds a list of Slots by resolving their args from the params
92
+ def story(params)
93
+ # constructor_args.target_method is UnboundMethod so can't call it directly
94
+ component = constructor_args.call(params) do |*args, **kwargs|
95
+ component_class.new(*args, **kwargs)
96
+ end
52
97
 
53
- def validate_constructor_args
54
- return if constructor_args.valid?
98
+ story_content_block = resolve_content_block(params)
55
99
 
56
- constructor_args_errors = constructor_args.errors.full_messages.join(', ')
57
- errors.add(:constructor_args, :invalid, errors: constructor_args_errors)
100
+ story_slots = slots.map do |slot_config|
101
+ slot_config.slot(component, params)
102
+ end
103
+
104
+ Storybook::Story.new(component, story_content_block, story_slots, layout)
58
105
  end
59
106
 
60
107
  class ValidationError < StandardError
@@ -69,8 +116,52 @@ module ViewComponent
69
116
 
70
117
  private
71
118
 
119
+ def constructor_args
120
+ @constructor_args ||= MethodArgs::ControlMethodArgs.new(component_constructor)
121
+ end
122
+
72
123
  def component_constructor
73
- component.instance_method(:initialize)
124
+ component_class.instance_method(:initialize)
125
+ end
126
+
127
+ def slots
128
+ @slots.values.flatten
129
+ end
130
+
131
+ def validate_constructor_args
132
+ return if constructor_args.valid?
133
+
134
+ constructor_args_errors = constructor_args.errors.full_messages.join(', ')
135
+ errors.add(:constructor_args, :invalid, errors: constructor_args_errors)
136
+ end
137
+
138
+ def slot(slot_name, *args, **kwargs, &block)
139
+ # if the name is a slot then build a SlotConfig with slot_name and param the same
140
+ if component_class.slot_type(slot_name) == :collection_item
141
+ # generate a unique param generated by the count of slots with this name already added
142
+ @slots[slot_name] ||= []
143
+ slot_index = @slots[slot_name].count + 1
144
+ slot_config = Slots::SlotConfig.from_component(
145
+ component_class,
146
+ slot_name,
147
+ "#{slot_name}#{slot_index}".to_sym,
148
+ *args,
149
+ **kwargs,
150
+ &block
151
+ )
152
+ @slots[slot_name] << slot_config
153
+ else
154
+ slot_config = Slots::SlotConfig.from_component(
155
+ component_class,
156
+ slot_name,
157
+ slot_name,
158
+ *args,
159
+ **kwargs,
160
+ &block
161
+ )
162
+ @slots[slot_name] = slot_config
163
+ end
164
+ slot_config
74
165
  end
75
166
  end
76
167
  end
@@ -12,7 +12,7 @@ namespace :view_component_storybook do
12
12
  exceptions << e
13
13
  end
14
14
 
15
- raise StandardError, exceptions.map(:message).join(", ") if exceptions.present?
15
+ raise StandardError, exceptions.map(&:message).join(", ") if exceptions.present?
16
16
 
17
17
  puts "Done"
18
18
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ViewComponent
4
4
  module Storybook
5
- VERSION = "0.9.0"
5
+ VERSION = "0.10.0"
6
6
  end
7
7
  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.9.0
4
+ version: 0.10.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-07-13 00:00:00.000000000 Z
11
+ date: 2021-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: view_component
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.2'
19
+ version: '2.36'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.2'
26
+ version: '2.36'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: capybara
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -177,29 +191,33 @@ files:
177
191
  - app/views/view_component/storybook/stories/show.html.erb
178
192
  - config/locales/en.yml
179
193
  - lib/view_component/storybook.rb
194
+ - lib/view_component/storybook/content_concern.rb
180
195
  - lib/view_component/storybook/controls.rb
181
- - lib/view_component/storybook/controls/array_config.rb
196
+ - lib/view_component/storybook/controls/base_options_config.rb
182
197
  - lib/view_component/storybook/controls/boolean_config.rb
183
198
  - lib/view_component/storybook/controls/color_config.rb
184
199
  - lib/view_component/storybook/controls/control_config.rb
200
+ - lib/view_component/storybook/controls/controls_helpers.rb
185
201
  - lib/view_component/storybook/controls/custom_config.rb
186
202
  - lib/view_component/storybook/controls/date_config.rb
203
+ - lib/view_component/storybook/controls/multi_options_config.rb
187
204
  - lib/view_component/storybook/controls/number_config.rb
188
205
  - lib/view_component/storybook/controls/object_config.rb
189
206
  - lib/view_component/storybook/controls/options_config.rb
190
207
  - lib/view_component/storybook/controls/simple_control_config.rb
191
208
  - lib/view_component/storybook/controls/text_config.rb
192
209
  - lib/view_component/storybook/dsl.rb
193
- - lib/view_component/storybook/dsl/controls_dsl.rb
194
210
  - lib/view_component/storybook/dsl/legacy_controls_dsl.rb
195
- - lib/view_component/storybook/dsl/story_dsl.rb
196
211
  - lib/view_component/storybook/engine.rb
197
212
  - lib/view_component/storybook/method_args.rb
198
213
  - lib/view_component/storybook/method_args/control_method_args.rb
199
214
  - lib/view_component/storybook/method_args/method_args.rb
200
215
  - lib/view_component/storybook/method_args/method_parameters_names.rb
201
- - lib/view_component/storybook/method_args/validatable_method_args.rb
216
+ - lib/view_component/storybook/slots.rb
217
+ - lib/view_component/storybook/slots/slot.rb
218
+ - lib/view_component/storybook/slots/slot_config.rb
202
219
  - lib/view_component/storybook/stories.rb
220
+ - lib/view_component/storybook/story.rb
203
221
  - lib/view_component/storybook/story_config.rb
204
222
  - lib/view_component/storybook/tasks/write_stories_json.rake
205
223
  - lib/view_component/storybook/version.rb
@@ -225,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
243
  - !ruby/object:Gem::Version
226
244
  version: '0'
227
245
  requirements: []
228
- rubygems_version: 3.0.6
246
+ rubygems_version: 3.1.6
229
247
  signing_key:
230
248
  specification_version: 4
231
249
  summary: Storybook for Rails View Components
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ViewComponent
4
- module Storybook
5
- module Controls
6
- class ArrayConfig < SimpleControlConfig
7
- attr_reader :separator
8
-
9
- validates :separator, presence: true
10
-
11
- def initialize(default_value, separator = ",", param: nil, name: nil)
12
- super(default_value, param: param, name: name)
13
- @separator = separator
14
- end
15
-
16
- def type
17
- :array
18
- end
19
-
20
- def value_from_params(params)
21
- params_value = super(params)
22
- if params_value.is_a?(String)
23
- params_value.split(separator)
24
- else
25
- params_value
26
- end
27
- end
28
-
29
- private
30
-
31
- def csf_control_params
32
- super.merge(separator: separator)
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ViewComponent
4
- module Storybook
5
- module Dsl
6
- class StoryDsl
7
- include ControlsDsl
8
-
9
- def self.evaluate!(story_config, &block)
10
- new(story_config).instance_eval(&block)
11
- end
12
-
13
- def parameters(**params)
14
- story_config.parameters = params
15
- end
16
-
17
- def controls(&block)
18
- ActiveSupport::Deprecation.warn("`controls` will be removed in v1.0.0. Use `#constructor` instead.")
19
- controls_dsl = LegacyControlsDsl.new
20
- controls_dsl.instance_eval(&block)
21
-
22
- controls_hash = controls_dsl.controls.index_by(&:param)
23
- story_config.constructor_args(**controls_hash)
24
- end
25
-
26
- def layout(layout)
27
- story_config.layout = layout
28
- end
29
-
30
- def content(&block)
31
- story_config.content_block = block
32
- end
33
-
34
- def constructor(*args, **kwargs, &block)
35
- story_config.constructor_args(*args, **kwargs)
36
- story_config.content_block = block
37
- end
38
-
39
- delegate :component, to: :story_config
40
-
41
- private
42
-
43
- attr_reader :story_config
44
-
45
- def initialize(story_config)
46
- @story_config = story_config
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ViewComponent
4
- module Storybook
5
- module MethodArgs
6
- ##
7
- # Class representing arguments passed to a method which can be validated
8
- # against the args of the target method
9
- class ValidatableMethodArgs < MethodArgs
10
- include ActiveModel::Validations
11
-
12
- attr_reader :target_method_params_names
13
-
14
- validate :validate_args, :validate_kwargs
15
-
16
- def initialize(target_method, *args, **kwargs, &block)
17
- @target_method_params_names = MethodParametersNames.new(target_method)
18
- super(args, kwargs, block)
19
- end
20
-
21
- private
22
-
23
- def validate_args
24
- arg_count = args.count
25
-
26
- if arg_count > target_method_params_names.max_arg_count
27
- errors.add(:args, :too_many, max: target_method_params_names.max_arg_count, count: arg_count)
28
- elsif arg_count < target_method_params_names.min_arg_count
29
- errors.add(:args, :too_few, min: target_method_params_names.min_arg_count, count: arg_count)
30
- end
31
- end
32
-
33
- def validate_kwargs
34
- kwargs.each_key do |kwarg|
35
- unless target_method_params_names.include_kwarg?(kwarg)
36
- errors.add(:kwargs, :invalid_arg, kwarg: kwarg)
37
- end
38
- end
39
-
40
- return if target_method_params_names.covers_required_kwargs?(kwargs.keys)
41
-
42
- expected_keys = target_method_params_names.req_kwarg_names.join(', ')
43
- actual_keys = kwargs.keys.join(', ')
44
-
45
- errors.add(:kwargs, :invalid, expected_keys: expected_keys, actual_keys: actual_keys)
46
- end
47
- end
48
- end
49
- end
50
- end