view_component_storybook 0.9.0 → 0.10.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 +4 -4
- data/README.md +6 -138
- data/app/controllers/view_component/storybook/stories_controller.rb +9 -11
- data/app/views/view_component/storybook/stories/show.html.erb +8 -3
- data/config/locales/en.yml +1 -1
- data/lib/view_component/storybook.rb +14 -2
- data/lib/view_component/storybook/content_concern.rb +42 -0
- data/lib/view_component/storybook/controls.rb +3 -1
- data/lib/view_component/storybook/controls/base_options_config.rb +41 -0
- data/lib/view_component/storybook/controls/control_config.rb +4 -0
- data/lib/view_component/storybook/{dsl/controls_dsl.rb → controls/controls_helpers.rb} +21 -16
- data/lib/view_component/storybook/controls/custom_config.rb +2 -4
- data/lib/view_component/storybook/controls/multi_options_config.rb +46 -0
- data/lib/view_component/storybook/controls/options_config.rb +12 -26
- data/lib/view_component/storybook/controls/simple_control_config.rb +1 -1
- data/lib/view_component/storybook/dsl.rb +0 -2
- data/lib/view_component/storybook/dsl/legacy_controls_dsl.rb +2 -2
- data/lib/view_component/storybook/engine.rb +2 -1
- data/lib/view_component/storybook/method_args/control_method_args.rb +15 -12
- data/lib/view_component/storybook/method_args/method_args.rb +37 -4
- data/lib/view_component/storybook/slots.rb +14 -0
- data/lib/view_component/storybook/slots/slot.rb +24 -0
- data/lib/view_component/storybook/slots/slot_config.rb +44 -0
- data/lib/view_component/storybook/stories.rb +16 -7
- data/lib/view_component/storybook/story.rb +18 -0
- data/lib/view_component/storybook/story_config.rb +115 -24
- data/lib/view_component/storybook/tasks/write_stories_json.rake +1 -1
- data/lib/view_component/storybook/version.rb +1 -1
- metadata +27 -9
- data/lib/view_component/storybook/controls/array_config.rb +0 -37
- data/lib/view_component/storybook/dsl/story_dsl.rb +0 -51
- data/lib/view_component/storybook/method_args/validatable_method_args.rb +0 -50
@@ -9,7 +9,7 @@ module ViewComponent
|
|
9
9
|
validate :validate_value_method_args
|
10
10
|
|
11
11
|
def with_value(*args, **kwargs, &block)
|
12
|
-
@value_method_args =
|
12
|
+
@value_method_args = MethodArgs::ControlMethodArgs.new(block, *args, **kwargs)
|
13
13
|
@value_method_args.with_param_prefix(param)
|
14
14
|
self
|
15
15
|
end
|
@@ -35,9 +35,7 @@ module ViewComponent
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def value_from_params(params)
|
38
|
-
|
39
|
-
|
40
|
-
method_args.block.call(*method_args.args, **method_args.kwargs)
|
38
|
+
value_method_args.call(params)
|
41
39
|
end
|
42
40
|
|
43
41
|
private
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Storybook
|
5
|
+
module Controls
|
6
|
+
class MultiOptionsConfig < BaseOptionsConfig
|
7
|
+
TYPES = %i[multi-select check inline-check].freeze
|
8
|
+
|
9
|
+
validates :type, inclusion: { in: TYPES }, unless: -> { type.nil? }
|
10
|
+
validate :validate_default_value, unless: -> { options.nil? || default_value.nil? }
|
11
|
+
|
12
|
+
def initialize(type, options, default_value, labels: nil, param: nil, name: nil)
|
13
|
+
super(type, options, Array.wrap(default_value), labels: labels, param: param, name: name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def value_from_params(params)
|
17
|
+
params_value = super(params)
|
18
|
+
|
19
|
+
if params_value.is_a?(String)
|
20
|
+
params_value = params_value.split(',')
|
21
|
+
params_value = params_value.map(&:to_sym) if symbol_values
|
22
|
+
end
|
23
|
+
params_value
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_csf_params
|
27
|
+
super.deep_merge(argTypes: { param => { options: options } })
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def csf_control_params
|
33
|
+
labels.nil? ? super : super.merge(labels: labels)
|
34
|
+
end
|
35
|
+
|
36
|
+
def symbol_values
|
37
|
+
@symbol_values ||= default_value.first.is_a?(Symbol)
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_default_value
|
41
|
+
errors.add(:default_value, :inclusion) unless default_value.to_set <= options.to_set
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -3,33 +3,11 @@
|
|
3
3
|
module ViewComponent
|
4
4
|
module Storybook
|
5
5
|
module Controls
|
6
|
-
class OptionsConfig <
|
7
|
-
|
8
|
-
# support the options being a Hash or an Array. Storybook supports either.
|
9
|
-
def inclusion_in(config)
|
10
|
-
case config.options
|
11
|
-
when Hash
|
12
|
-
config.options.values
|
13
|
-
when Array
|
14
|
-
config.options
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
TYPES = %i[select multi-select radio inline-radio check inline-check].freeze
|
6
|
+
class OptionsConfig < BaseOptionsConfig
|
7
|
+
TYPES = %i[select radio inline-radio].freeze
|
20
8
|
|
21
|
-
attr_reader :type, :options, :symbol_value
|
22
|
-
|
23
|
-
validates :type, :options, presence: true
|
24
9
|
validates :type, inclusion: { in: TYPES }, unless: -> { type.nil? }
|
25
|
-
validates :default_value, inclusion: { in:
|
26
|
-
|
27
|
-
def initialize(type, options, default_value, param: nil, name: nil)
|
28
|
-
super(default_value, param: param, name: name)
|
29
|
-
@type = type
|
30
|
-
@options = options
|
31
|
-
@symbol_value = default_value.is_a?(Symbol)
|
32
|
-
end
|
10
|
+
validates :default_value, inclusion: { in: ->(config) { config.options } }, unless: -> { options.nil? || default_value.nil? }
|
33
11
|
|
34
12
|
def value_from_params(params)
|
35
13
|
params_value = super(params)
|
@@ -40,10 +18,18 @@ module ViewComponent
|
|
40
18
|
end
|
41
19
|
end
|
42
20
|
|
21
|
+
def to_csf_params
|
22
|
+
super.deep_merge(argTypes: { param => { options: options } })
|
23
|
+
end
|
24
|
+
|
43
25
|
private
|
44
26
|
|
45
27
|
def csf_control_params
|
46
|
-
super.merge(
|
28
|
+
labels.nil? ? super : super.merge(labels: labels)
|
29
|
+
end
|
30
|
+
|
31
|
+
def symbol_value
|
32
|
+
@symbol_value ||= default_value.is_a?(Symbol)
|
47
33
|
end
|
48
34
|
end
|
49
35
|
end
|
@@ -56,8 +56,8 @@ module ViewComponent
|
|
56
56
|
controls << Controls::OptionsConfig.new(:'inline-check', options, value, param: param, name: name)
|
57
57
|
end
|
58
58
|
|
59
|
-
def array(param, value,
|
60
|
-
controls << Controls::
|
59
|
+
def array(param, value, _separator = nil, name: nil)
|
60
|
+
controls << Controls::ObjectConfig.new(value, param: param, name: name)
|
61
61
|
end
|
62
62
|
|
63
63
|
def date(param, value, name: nil)
|
@@ -12,6 +12,7 @@ module ViewComponent
|
|
12
12
|
options = app.config.view_component_storybook
|
13
13
|
|
14
14
|
options.show_stories = Rails.env.development? if options.show_stories.nil?
|
15
|
+
options.stories_route ||= ViewComponent::Storybook.stories_route
|
15
16
|
|
16
17
|
if options.show_stories
|
17
18
|
options.stories_path ||= defined?(Rails.root) ? Rails.root.join("test/components/stories") : nil
|
@@ -33,7 +34,7 @@ module ViewComponent
|
|
33
34
|
|
34
35
|
if options.show_stories
|
35
36
|
app.routes.prepend do
|
36
|
-
get "
|
37
|
+
get "#{options.stories_route}/*stories/:story" => "view_component/storybook/stories#show", :internal => true
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
@@ -6,9 +6,9 @@ module ViewComponent
|
|
6
6
|
##
|
7
7
|
# Class representing arguments passed to a method which can be validated
|
8
8
|
# against the args of the target method
|
9
|
-
#
|
9
|
+
# In addition the args and kwargs can contain Controls the values of which can
|
10
10
|
# be resolved from a params hash
|
11
|
-
class ControlMethodArgs <
|
11
|
+
class ControlMethodArgs < MethodArgs
|
12
12
|
include ActiveModel::Validations::Callbacks
|
13
13
|
|
14
14
|
attr_reader :param_prefix
|
@@ -21,6 +21,15 @@ module ViewComponent
|
|
21
21
|
self
|
22
22
|
end
|
23
23
|
|
24
|
+
##
|
25
|
+
# resolve the controls values from the params
|
26
|
+
# call the target method or block with those values
|
27
|
+
def call(params, &target_block)
|
28
|
+
method_args = resolve_method_args(params)
|
29
|
+
|
30
|
+
(target_block || target_method).call(*method_args.args, **method_args.kwargs)
|
31
|
+
end
|
32
|
+
|
24
33
|
def resolve_method_args(params)
|
25
34
|
assign_control_params
|
26
35
|
|
@@ -31,7 +40,7 @@ module ViewComponent
|
|
31
40
|
value_from_params(arg, params)
|
32
41
|
end
|
33
42
|
|
34
|
-
MethodArgs.new(args_from_params, kwargs_from_params
|
43
|
+
MethodArgs.new(target_method, *args_from_params, **kwargs_from_params)
|
35
44
|
end
|
36
45
|
|
37
46
|
def controls
|
@@ -59,21 +68,15 @@ module ViewComponent
|
|
59
68
|
|
60
69
|
arg.param(param) if arg.param.nil? # don't overrite if set
|
61
70
|
# Always add prefix
|
62
|
-
arg.
|
71
|
+
arg.prefix_param(param_prefix) if param_prefix.present?
|
63
72
|
end
|
64
73
|
|
65
74
|
def value_from_params(arg, params)
|
66
|
-
|
67
|
-
value = arg.value_from_params(params)
|
68
|
-
value = arg.default_value if value.nil? # nil only not falsey
|
69
|
-
value
|
70
|
-
else
|
71
|
-
arg
|
72
|
-
end
|
75
|
+
control?(arg) ? arg.value_from_params(params) : arg
|
73
76
|
end
|
74
77
|
|
75
78
|
def control?(arg)
|
76
|
-
arg.is_a?(
|
79
|
+
arg.is_a?(Controls::ControlConfig)
|
77
80
|
end
|
78
81
|
|
79
82
|
def validate_controls
|
@@ -4,14 +4,47 @@ module ViewComponent
|
|
4
4
|
module Storybook
|
5
5
|
module MethodArgs
|
6
6
|
##
|
7
|
-
#
|
7
|
+
# Class representing arguments passed to a method which can be validated
|
8
|
+
# against the args of the target method
|
8
9
|
class MethodArgs
|
9
|
-
|
10
|
+
include ActiveModel::Validations
|
10
11
|
|
11
|
-
|
12
|
+
attr_reader :args, :kwargs, :target_method, :target_method_params_names
|
13
|
+
|
14
|
+
validate :validate_args, :validate_kwargs
|
15
|
+
|
16
|
+
def initialize(target_method, *args, **kwargs)
|
17
|
+
@target_method = target_method
|
12
18
|
@args = args
|
13
19
|
@kwargs = kwargs
|
14
|
-
@
|
20
|
+
@target_method_params_names = MethodParametersNames.new(target_method)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def validate_args
|
26
|
+
arg_count = args.count
|
27
|
+
|
28
|
+
if arg_count > target_method_params_names.max_arg_count
|
29
|
+
errors.add(:args, :too_many, max: target_method_params_names.max_arg_count, count: arg_count)
|
30
|
+
elsif arg_count < target_method_params_names.min_arg_count
|
31
|
+
errors.add(:args, :too_few, min: target_method_params_names.min_arg_count, count: arg_count)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_kwargs
|
36
|
+
kwargs.each_key do |kwarg|
|
37
|
+
unless target_method_params_names.include_kwarg?(kwarg)
|
38
|
+
errors.add(:kwargs, :invalid_arg, kwarg: kwarg)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
return if target_method_params_names.covers_required_kwargs?(kwargs.keys)
|
43
|
+
|
44
|
+
expected_keys = target_method_params_names.req_kwarg_names.join(', ')
|
45
|
+
actual_keys = kwargs.keys.join(', ')
|
46
|
+
|
47
|
+
errors.add(:kwargs, :invalid, expected_keys: expected_keys, actual_keys: actual_keys)
|
15
48
|
end
|
16
49
|
end
|
17
50
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Storybook
|
5
|
+
module Slots
|
6
|
+
class Slot
|
7
|
+
attr_reader :component, :slot_name, :slot_method_args, :content_block
|
8
|
+
|
9
|
+
# delegate :args, :kwargs, :controls, to: :slot_method_args
|
10
|
+
|
11
|
+
def initialize(component, slot_name, slot_method_args, content_block)
|
12
|
+
@component = component
|
13
|
+
@slot_name = slot_name
|
14
|
+
@slot_method_args = slot_method_args
|
15
|
+
@content_block = content_block
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(&block)
|
19
|
+
component.send(slot_name, *slot_method_args.args, **slot_method_args.kwargs, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Storybook
|
5
|
+
module Slots
|
6
|
+
class SlotConfig
|
7
|
+
include ContentConcern
|
8
|
+
|
9
|
+
attr_reader :slot_name, :slot_method_args, :param, :content_block
|
10
|
+
|
11
|
+
def initialize(slot_name, slot_method_args, param, content_block)
|
12
|
+
@slot_name = slot_name
|
13
|
+
@slot_method_args = slot_method_args
|
14
|
+
@param = param
|
15
|
+
@content_block = content_block
|
16
|
+
end
|
17
|
+
|
18
|
+
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)
|
24
|
+
end
|
25
|
+
|
26
|
+
def slot(componeont, params)
|
27
|
+
resolved_method_args = slot_method_args.resolve_method_args(params)
|
28
|
+
story_content_block = resolve_content_block(params)
|
29
|
+
Slot.new(componeont, slot_name, resolved_method_args, story_content_block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def controls
|
33
|
+
list = slot_method_args.controls.dup
|
34
|
+
list << content_control if content_control
|
35
|
+
list
|
36
|
+
end
|
37
|
+
|
38
|
+
def content_param
|
39
|
+
"#{param}__content".to_sym
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -7,19 +7,28 @@ module ViewComponent
|
|
7
7
|
include ActiveModel::Validations
|
8
8
|
|
9
9
|
class_attribute :story_configs, default: []
|
10
|
-
class_attribute :
|
10
|
+
class_attribute :stories_parameters, :stories_title, :stories_layout
|
11
11
|
|
12
12
|
validate :validate_story_configs
|
13
13
|
|
14
14
|
class << self
|
15
|
+
def title(title = nil)
|
16
|
+
# if no argument is passed act like a getter
|
17
|
+
self.stories_title = title unless title.nil?
|
18
|
+
stories_title
|
19
|
+
end
|
20
|
+
|
15
21
|
def story(name, component = default_component, &block)
|
16
|
-
story_config = StoryConfig.
|
22
|
+
story_config = StoryConfig.new(story_id(name), name, component, layout, &block)
|
23
|
+
story_config.instance_eval(&block)
|
17
24
|
story_configs << story_config
|
18
25
|
story_config
|
19
26
|
end
|
20
27
|
|
21
|
-
def parameters(
|
22
|
-
|
28
|
+
def parameters(params = nil)
|
29
|
+
# if no argument is passed act like a getter
|
30
|
+
self.stories_parameters = params unless params.nil?
|
31
|
+
stories_parameters
|
23
32
|
end
|
24
33
|
|
25
34
|
def layout(layout = nil)
|
@@ -60,7 +69,7 @@ module ViewComponent
|
|
60
69
|
end
|
61
70
|
|
62
71
|
# Find a component stories by its underscored class name.
|
63
|
-
def
|
72
|
+
def find_story_configs(stories_name)
|
64
73
|
all.find { |stories| stories.stories_name == stories_name }
|
65
74
|
end
|
66
75
|
|
@@ -70,7 +79,7 @@ module ViewComponent
|
|
70
79
|
end
|
71
80
|
|
72
81
|
# find the story by name
|
73
|
-
def
|
82
|
+
def find_story_config(name)
|
74
83
|
story_configs.find { |config| config.name == name.to_sym }
|
75
84
|
end
|
76
85
|
|
@@ -92,7 +101,7 @@ module ViewComponent
|
|
92
101
|
def inherited(other)
|
93
102
|
super(other)
|
94
103
|
# setup class defaults
|
95
|
-
other.
|
104
|
+
other.stories_title = other.stories_name.humanize.titlecase
|
96
105
|
other.story_configs = []
|
97
106
|
end
|
98
107
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Storybook
|
5
|
+
class Story
|
6
|
+
include ActiveModel::Validations
|
7
|
+
|
8
|
+
attr_reader :component, :content_block, :slots, :layout
|
9
|
+
|
10
|
+
def initialize(component, content_block, slots, layout)
|
11
|
+
@component = component
|
12
|
+
@content_block = content_block
|
13
|
+
@slots = slots
|
14
|
+
@layout = layout
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|