view_component_storybook 0.8.0 → 0.9.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -9
  3. data/app/controllers/view_component/storybook/stories_controller.rb +3 -2
  4. data/app/views/view_component/storybook/stories/show.html.erb +3 -1
  5. data/config/locales/en.yml +32 -0
  6. data/lib/view_component/storybook.rb +2 -0
  7. data/lib/view_component/storybook/controls.rb +2 -0
  8. data/lib/view_component/storybook/controls/array_config.rb +8 -7
  9. data/lib/view_component/storybook/controls/boolean_config.rb +7 -6
  10. data/lib/view_component/storybook/controls/color_config.rb +4 -5
  11. data/lib/view_component/storybook/controls/control_config.rb +22 -38
  12. data/lib/view_component/storybook/controls/custom_config.rb +54 -0
  13. data/lib/view_component/storybook/controls/date_config.rb +14 -11
  14. data/lib/view_component/storybook/controls/number_config.rb +9 -9
  15. data/lib/view_component/storybook/controls/object_config.rb +11 -5
  16. data/lib/view_component/storybook/controls/options_config.rb +9 -8
  17. data/lib/view_component/storybook/controls/simple_control_config.rb +48 -0
  18. data/lib/view_component/storybook/controls/text_config.rb +1 -1
  19. data/lib/view_component/storybook/dsl.rb +1 -0
  20. data/lib/view_component/storybook/dsl/controls_dsl.rb +31 -61
  21. data/lib/view_component/storybook/dsl/legacy_controls_dsl.rb +98 -0
  22. data/lib/view_component/storybook/dsl/story_dsl.rb +17 -5
  23. data/lib/view_component/storybook/method_args.rb +16 -0
  24. data/lib/view_component/storybook/method_args/control_method_args.rb +88 -0
  25. data/lib/view_component/storybook/method_args/method_args.rb +19 -0
  26. data/lib/view_component/storybook/method_args/method_parameters_names.rb +97 -0
  27. data/lib/view_component/storybook/method_args/validatable_method_args.rb +50 -0
  28. data/lib/view_component/storybook/stories.rb +43 -0
  29. data/lib/view_component/storybook/story_config.rb +43 -9
  30. data/lib/view_component/storybook/tasks/write_stories_json.rake +6 -0
  31. data/lib/view_component/storybook/version.rb +1 -1
  32. metadata +24 -15
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module Storybook
5
5
  module Controls
6
- class NumberConfig < ControlConfig
6
+ class NumberConfig < SimpleControlConfig
7
7
  TYPES = %i[number range].freeze
8
8
 
9
9
  attr_reader :type, :min, :max, :step
@@ -11,27 +11,27 @@ module ViewComponent
11
11
  validates :type, presence: true
12
12
  validates :type, inclusion: { in: TYPES }, unless: -> { type.nil? }
13
13
 
14
- def initialize(type, component, param, value, min: nil, max: nil, step: nil, name: nil)
15
- super(component, param, value, name: name)
14
+ def initialize(type, default_value, min: nil, max: nil, step: nil, param: nil, name: nil)
15
+ super(default_value, param: param, name: name)
16
16
  @type = type
17
17
  @min = min
18
18
  @max = max
19
19
  @step = step
20
20
  end
21
21
 
22
- def value_from_param(param)
23
- if param.is_a?(String) && param.present?
24
- (param.to_f % 1) > 0 ? param.to_f : param.to_i
22
+ def value_from_params(params)
23
+ params_value = super(params)
24
+ if params_value.is_a?(String) && params_value.present?
25
+ (params_value.to_f % 1) > 0 ? params_value.to_f : params_value.to_i
25
26
  else
26
- super(param)
27
+ params_value
27
28
  end
28
29
  end
29
30
 
30
31
  private
31
32
 
32
33
  def csf_control_params
33
- params = super
34
- params.merge(min: min, max: max, step: step).compact
34
+ super.merge(min: min, max: max, step: step).compact
35
35
  end
36
36
  end
37
37
  end
@@ -3,16 +3,22 @@
3
3
  module ViewComponent
4
4
  module Storybook
5
5
  module Controls
6
- class ObjectConfig < ControlConfig
6
+ class ObjectConfig < SimpleControlConfig
7
7
  def type
8
8
  :object
9
9
  end
10
10
 
11
- def value_from_param(param)
12
- if param.is_a?(String)
13
- JSON.parse(param).deep_symbolize_keys
11
+ def value_from_params(params)
12
+ params_value = super(params)
13
+ if params_value.is_a?(String)
14
+ parsed_json = JSON.parse(params_value)
15
+ if parsed_json.is_a?(Array)
16
+ parsed_json.map(&:deep_symbolize_keys)
17
+ else
18
+ parsed_json.deep_symbolize_keys
19
+ end
14
20
  else
15
- super(param)
21
+ params_value
16
22
  end
17
23
  end
18
24
  end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module Storybook
5
5
  module Controls
6
- class OptionsConfig < ControlConfig
6
+ class OptionsConfig < SimpleControlConfig
7
7
  class << self
8
8
  # support the options being a Hash or an Array. Storybook supports either.
9
9
  def inclusion_in(config)
@@ -22,20 +22,21 @@ module ViewComponent
22
22
 
23
23
  validates :type, :options, presence: true
24
24
  validates :type, inclusion: { in: TYPES }, unless: -> { type.nil? }
25
- validates :value, inclusion: { in: method(:inclusion_in) }, unless: -> { options.nil? || value.nil? }
25
+ validates :default_value, inclusion: { in: method(:inclusion_in) }, unless: -> { options.nil? || default_value.nil? }
26
26
 
27
- def initialize(type, component, param, options, default_value, name: nil)
28
- super(component, param, default_value, name: name)
27
+ def initialize(type, options, default_value, param: nil, name: nil)
28
+ super(default_value, param: param, name: name)
29
29
  @type = type
30
30
  @options = options
31
31
  @symbol_value = default_value.is_a?(Symbol)
32
32
  end
33
33
 
34
- def value_from_param(param)
35
- if param.is_a?(String) && symbol_value
36
- param.to_sym
34
+ def value_from_params(params)
35
+ params_value = super(params)
36
+ if params_value.is_a?(String) && symbol_value
37
+ params_value.to_sym
37
38
  else
38
- super(param)
39
+ params_value
39
40
  end
40
41
  end
41
42
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Storybook
5
+ module Controls
6
+ ##
7
+ # A simple Control Config maps to one Storybook Control
8
+ # It has a value and pulls its value from params by key
9
+ class SimpleControlConfig < ControlConfig
10
+ attr_reader :default_value
11
+
12
+ def initialize(default_value, param: nil, name: nil)
13
+ super(param: param, name: name)
14
+ @default_value = default_value
15
+ end
16
+
17
+ def to_csf_params
18
+ validate!
19
+ {
20
+ args: { param => csf_value },
21
+ argTypes: { param => { control: csf_control_params, name: name } }
22
+ }
23
+ end
24
+
25
+ def value_from_params(params)
26
+ params[param]
27
+ end
28
+
29
+ private
30
+
31
+ # provide extension points for subclasses to vary the value
32
+ def type
33
+ # :nocov:
34
+ raise NotImplementedError
35
+ # :nocov:
36
+ end
37
+
38
+ def csf_value
39
+ default_value
40
+ end
41
+
42
+ def csf_control_params
43
+ { type: type }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module Storybook
5
5
  module Controls
6
- class TextConfig < ControlConfig
6
+ class TextConfig < SimpleControlConfig
7
7
  def type
8
8
  :text
9
9
  end
@@ -9,6 +9,7 @@ module ViewComponent
9
9
 
10
10
  autoload :StoryDsl
11
11
  autoload :ControlsDsl
12
+ autoload :LegacyControlsDsl
12
13
  end
13
14
  end
14
15
  end
@@ -3,95 +3,65 @@
3
3
  module ViewComponent
4
4
  module Storybook
5
5
  module Dsl
6
- class ControlsDsl
7
- attr_reader :component, :controls
8
-
9
- def initialize(component)
10
- @component = component
11
- @controls = []
12
- end
13
-
14
- def text(param, value, name: nil)
15
- controls << Controls::TextConfig.new(component, param, value, name: name)
16
- end
17
-
18
- def boolean(param, value, name: nil)
19
- controls << Controls::BooleanConfig.new(component, param, value, name: name)
6
+ module ControlsDsl
7
+ def text(default_value)
8
+ Controls::TextConfig.new(default_value)
20
9
  end
21
10
 
22
- def number(param, value, name: nil, min: nil, max: nil, step: nil)
23
- controls << Controls::NumberConfig.new(:number, component, param, value, name: name, min: min, max: max, step: step)
11
+ def boolean(default_value)
12
+ Controls::BooleanConfig.new(default_value)
24
13
  end
25
14
 
26
- def range(param, value, name: nil, min: nil, max: nil, step: nil)
27
- controls << Controls::NumberConfig.new(:range, component, param, value, name: name, min: min, max: max, step: step)
15
+ def number(default_value, min: nil, max: nil, step: nil)
16
+ Controls::NumberConfig.new(:number, default_value, min: min, max: max, step: step)
28
17
  end
29
18
 
30
- def color(param, value, name: nil, preset_colors: nil)
31
- controls << Controls::ColorConfig.new(component, param, value, name: name, preset_colors: preset_colors)
19
+ def range(default_value, min: nil, max: nil, step: nil)
20
+ Controls::NumberConfig.new(:range, default_value, min: min, max: max, step: step)
32
21
  end
33
22
 
34
- def object(param, value, name: nil)
35
- controls << Controls::ObjectConfig.new(component, param, value, name: name)
23
+ def color(default_value, preset_colors: nil)
24
+ Controls::ColorConfig.new(default_value, preset_colors: preset_colors)
36
25
  end
37
26
 
38
- def select(param, options, value, name: nil)
39
- controls << Controls::OptionsConfig.new(:select, component, param, options, value, name: name)
27
+ def object(default_value)
28
+ Controls::ObjectConfig.new(default_value)
40
29
  end
41
30
 
42
- def multi_select(param, options, value, name: nil)
43
- controls << Controls::OptionsConfig.new(:'multi-select', component, param, options, value, name: name)
31
+ def select(options, default_value)
32
+ Controls::OptionsConfig.new(:select, options, default_value)
44
33
  end
45
34
 
46
- def radio(param, options, value, name: nil)
47
- controls << Controls::OptionsConfig.new(:radio, component, param, options, value, name: name)
35
+ def multi_select(options, default_value)
36
+ Controls::OptionsConfig.new(:'multi-select', options, default_value)
48
37
  end
49
38
 
50
- def inline_radio(param, options, value, name: nil)
51
- controls << Controls::OptionsConfig.new(:'inline-radio', component, param, options, value, name: name)
39
+ def radio(options, default_value)
40
+ Controls::OptionsConfig.new(:radio, options, default_value)
52
41
  end
53
42
 
54
- def check(param, options, value, name: nil)
55
- controls << Controls::OptionsConfig.new(:check, component, param, options, value, name: name)
43
+ def inline_radio(options, default_value)
44
+ Controls::OptionsConfig.new(:'inline-radio', options, default_value)
56
45
  end
57
46
 
58
- def inline_check(param, options, value, name: nil)
59
- controls << Controls::OptionsConfig.new(:'inline-check', component, param, options, value, name: name)
47
+ def check(options, default_value)
48
+ Controls::OptionsConfig.new(:check, options, default_value)
60
49
  end
61
50
 
62
- def array(param, value, separator = ",", name: nil)
63
- controls << Controls::ArrayConfig.new(component, param, value, separator, name: name)
51
+ def inline_check(options, default_value)
52
+ Controls::OptionsConfig.new(:'inline-check', options, default_value)
64
53
  end
65
54
 
66
- def date(param, value, name: nil)
67
- controls << Controls::DateConfig.new(component, param, value, name: name)
55
+ def array(default_value, separator = ",")
56
+ Controls::ArrayConfig.new(default_value, separator)
68
57
  end
69
58
 
70
- def respond_to_missing?(_method, *)
71
- true
59
+ def date(default_value)
60
+ Controls::DateConfig.new(default_value)
72
61
  end
73
62
 
74
- def method_missing(method, *args)
75
- value = args.first
76
- control_method = case value
77
- when Date
78
- :date
79
- when Array
80
- :array
81
- when Hash
82
- :object
83
- when Numeric
84
- :number
85
- when TrueClass, FalseClass
86
- :boolean
87
- when String
88
- :text
89
- end
90
- if control_method
91
- send(control_method, method, *args)
92
- else
93
- super
94
- end
63
+ def custom(*args, **kwargs, &block)
64
+ Controls::CustomConfig.new.with_value(*args, **kwargs, &block)
95
65
  end
96
66
 
97
67
  Controls = ViewComponent::Storybook::Controls
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Storybook
5
+ module Dsl
6
+ class LegacyControlsDsl
7
+ def controls
8
+ @controls ||= []
9
+ end
10
+
11
+ def text(param, value, name: nil)
12
+ controls << Controls::TextConfig.new(value, param: param, name: name)
13
+ end
14
+
15
+ def boolean(param, value, name: nil)
16
+ controls << Controls::BooleanConfig.new(value, param: param, name: name)
17
+ end
18
+
19
+ def number(param, value, name: nil, min: nil, max: nil, step: nil)
20
+ controls << Controls::NumberConfig.new(:number, value, param: param, name: name, min: min, max: max, step: step)
21
+ end
22
+
23
+ def range(param, value, name: nil, min: nil, max: nil, step: nil)
24
+ controls << Controls::NumberConfig.new(:range, value, param: param, name: name, min: min, max: max, step: step)
25
+ end
26
+
27
+ def color(param, value, name: nil, preset_colors: nil)
28
+ controls << Controls::ColorConfig.new(value, param: param, name: name, preset_colors: preset_colors)
29
+ end
30
+
31
+ def object(param, value, name: nil)
32
+ controls << Controls::ObjectConfig.new(value, param: param, name: name)
33
+ end
34
+
35
+ def select(param, options, value, name: nil)
36
+ controls << Controls::OptionsConfig.new(:select, options, value, param: param, name: name)
37
+ end
38
+
39
+ def multi_select(param, options, value, name: nil)
40
+ controls << Controls::OptionsConfig.new(:'multi-select', options, value, param: param, name: name)
41
+ end
42
+
43
+ def radio(param, options, value, name: nil)
44
+ controls << Controls::OptionsConfig.new(:radio, options, value, param: param, name: name)
45
+ end
46
+
47
+ def inline_radio(param, options, value, name: nil)
48
+ controls << Controls::OptionsConfig.new(:'inline-radio', options, value, param: param, name: name)
49
+ end
50
+
51
+ def check(param, options, value, name: nil)
52
+ controls << Controls::OptionsConfig.new(:check, options, value, param: param, name: name)
53
+ end
54
+
55
+ def inline_check(param, options, value, name: nil)
56
+ controls << Controls::OptionsConfig.new(:'inline-check', options, value, param: param, name: name)
57
+ end
58
+
59
+ def array(param, value, separator = ",", name: nil)
60
+ controls << Controls::ArrayConfig.new(value, separator, param: param, name: name)
61
+ end
62
+
63
+ def date(param, value, name: nil)
64
+ controls << Controls::DateConfig.new(value, param: param, name: name)
65
+ end
66
+
67
+ def respond_to_missing?(_method, *)
68
+ true
69
+ end
70
+
71
+ def method_missing(method, *args)
72
+ value = args.first
73
+ control_method = case value
74
+ when Date
75
+ :date
76
+ when Array
77
+ :array
78
+ when Hash
79
+ :object
80
+ when Numeric
81
+ :number
82
+ when TrueClass, FalseClass
83
+ :boolean
84
+ when String
85
+ :text
86
+ end
87
+ if control_method
88
+ send(control_method, method, *args)
89
+ else
90
+ super
91
+ end
92
+ end
93
+
94
+ Controls = ViewComponent::Storybook::Controls
95
+ end
96
+ end
97
+ end
98
+ end
@@ -4,28 +4,40 @@ module ViewComponent
4
4
  module Storybook
5
5
  module Dsl
6
6
  class StoryDsl
7
+ include ControlsDsl
8
+
7
9
  def self.evaluate!(story_config, &block)
8
10
  new(story_config).instance_eval(&block)
9
11
  end
10
12
 
11
13
  def parameters(**params)
12
- @story_config.parameters = params
14
+ story_config.parameters = params
13
15
  end
14
16
 
15
17
  def controls(&block)
16
- controls_dsl = ControlsDsl.new(story_config.component)
18
+ ActiveSupport::Deprecation.warn("`controls` will be removed in v1.0.0. Use `#constructor` instead.")
19
+ controls_dsl = LegacyControlsDsl.new
17
20
  controls_dsl.instance_eval(&block)
18
- @story_config.controls = controls_dsl.controls
21
+
22
+ controls_hash = controls_dsl.controls.index_by(&:param)
23
+ story_config.constructor_args(**controls_hash)
19
24
  end
20
25
 
21
26
  def layout(layout)
22
- @story_config.layout = layout
27
+ story_config.layout = layout
23
28
  end
24
29
 
25
30
  def content(&block)
26
- @story_config.content_block = block
31
+ story_config.content_block = block
27
32
  end
28
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
+
29
41
  private
30
42
 
31
43
  attr_reader :story_config