view_component_storybook 0.3.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.
- checksums.yaml +4 -4
- data/README.md +22 -11
- data/app/controllers/view_component/storybook/stories_controller.rb +3 -2
- data/app/views/view_component/storybook/stories/show.html.erb +3 -1
- data/config/locales/en.yml +32 -0
- data/lib/view_component/storybook.rb +2 -1
- data/lib/view_component/storybook/controls.rb +5 -1
- data/lib/view_component/storybook/controls/array_config.rb +9 -8
- data/lib/view_component/storybook/controls/boolean_config.rb +31 -0
- data/lib/view_component/storybook/controls/color_config.rb +26 -0
- data/lib/view_component/storybook/controls/control_config.rb +22 -26
- data/lib/view_component/storybook/controls/custom_config.rb +54 -0
- data/lib/view_component/storybook/controls/date_config.rb +14 -13
- data/lib/view_component/storybook/controls/number_config.rb +17 -16
- data/lib/view_component/storybook/controls/object_config.rb +11 -7
- data/lib/view_component/storybook/controls/options_config.rb +29 -7
- data/lib/view_component/storybook/controls/simple_control_config.rb +48 -0
- data/lib/view_component/storybook/controls/text_config.rb +13 -0
- data/lib/view_component/storybook/dsl.rb +1 -0
- data/lib/view_component/storybook/dsl/controls_dsl.rb +36 -46
- data/lib/view_component/storybook/dsl/legacy_controls_dsl.rb +98 -0
- data/lib/view_component/storybook/dsl/story_dsl.rb +17 -5
- data/lib/view_component/storybook/method_args.rb +16 -0
- data/lib/view_component/storybook/method_args/control_method_args.rb +88 -0
- data/lib/view_component/storybook/method_args/method_args.rb +19 -0
- data/lib/view_component/storybook/method_args/method_parameters_names.rb +97 -0
- data/lib/view_component/storybook/method_args/validatable_method_args.rb +50 -0
- data/lib/view_component/storybook/stories.rb +44 -7
- data/lib/view_component/storybook/story_config.rb +43 -9
- data/lib/view_component/storybook/tasks/write_stories_json.rake +6 -0
- data/lib/view_component/storybook/version.rb +1 -1
- metadata +43 -18
- data/lib/view_component/storybook/controls/simple_config.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8ab883711eb6c712fcb3f654fdcb80586091f6da753e311f2609347d600391e
|
4
|
+
data.tar.gz: 65ba53ff31930882f7c2a30492e5444ed438be268cfbb24291160ff38f712964
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24bf672f2214344c1d5b9e1af7bbe8ef51ddb8ba6065eca4b12b14e0c8b73f69381168b39d3f4339e953a086ec11d69f716f3fce7dfb8acd7efaabd3678cb578
|
7
|
+
data.tar.gz: 0df0d064cccf1ed1d625ba1d9cb439d8c3fb65f56a7671e3d65650dc85604ea89990e578ca4550863388d7cfc201b7cd33b7add24f294deb6646b1753062c03a
|
data/README.md
CHANGED
@@ -1,21 +1,34 @@
|
|
1
1
|
# ViewComponent::Storybook
|
2
2
|
|
3
|
-
The ViewComponent::Storybook gem provides Ruby api for writing stories describing [View Components](https://github.com/github/view_component) and allowing them to be previewed and tested in [Storybook](https://github.com/storybookjs/storybook/)
|
3
|
+
The ViewComponent::Storybook gem provides Ruby api for writing stories describing [View Components](https://github.com/github/view_component) and allowing them to be previewed and tested in [Storybook](https://github.com/storybookjs/storybook/) via its [Server](https://github.com/storybookjs/storybook/tree/next/app/server) support.
|
4
4
|
|
5
5
|
## Features
|
6
6
|
* A Ruby DSL for writing Stories describing View Components
|
7
|
-
* A Rails controller backend for Storybook Server compatible with
|
7
|
+
* A Rails controller backend for Storybook Server compatible with Storybook Controls Addon parameters
|
8
8
|
* Coming Soon: Rake tasks to watch View Components and Stories and trigger Storybook hot reloading
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
12
|
-
###
|
12
|
+
### Rails Installation
|
13
13
|
|
14
14
|
1. Add the `view_component_storybook` gem, to your Gemfile: `gem 'view_component_storybook'`
|
15
15
|
2. Run `bundle install`.
|
16
|
-
3. Add `require "
|
16
|
+
3. Add `require "view_component/storybook/engine"` to `config/application.rb`
|
17
17
|
4. Add `**/*.stories.json` to `.gitignore`
|
18
18
|
|
19
|
+
#### Configure Asset Hosts
|
20
|
+
|
21
|
+
If your view components depend on Javascript, CSS or other assets served by the Rails application you will need to configure `asset_hosts`
|
22
|
+
apporpriately for your various environments. For local development this is a simple as adding to `config/development.rb`:
|
23
|
+
```ruby
|
24
|
+
Rails.application.configure do
|
25
|
+
...
|
26
|
+
config.action_controller.asset_host = 'http://localhost:3000'
|
27
|
+
...
|
28
|
+
end
|
29
|
+
```
|
30
|
+
Equivalent configuration will be necessary in `config/production.rb` or `application.rb` based you your deployment environments.
|
31
|
+
|
19
32
|
### Storybook Installation
|
20
33
|
|
21
34
|
1. Add Storybook server as a dev dependedncy. The Storybook Controls addon isn't needed but is strongly recommended
|
@@ -29,7 +42,7 @@ The ViewComponent::Storybook gem provides Ruby api for writing stories describin
|
|
29
42
|
"storybook": "start-storybook"
|
30
43
|
}
|
31
44
|
}
|
32
|
-
```
|
45
|
+
```
|
33
46
|
3. Create the .storybook/main.js file to configure Storybook to find the json stories the gem creates. Also configure the Controls addon:
|
34
47
|
```javascript
|
35
48
|
module.exports = {
|
@@ -48,9 +61,7 @@ The ViewComponent::Storybook gem provides Ruby api for writing stories describin
|
|
48
61
|
},
|
49
62
|
};
|
50
63
|
```
|
51
|
-
|
52
64
|
|
53
|
-
Note: `@storybook/server` will be part of the upcoming Storybook 6.0 release. Until that is released you'll need to use an [rc release](https://github.com/storybookjs/storybook/releases/tag/v6.0.0-rc.14)
|
54
65
|
|
55
66
|
## Usage
|
56
67
|
|
@@ -68,7 +79,7 @@ class ButtonComponent < ViewComponent::Base
|
|
68
79
|
end
|
69
80
|
```
|
70
81
|
|
71
|
-
We can write a stories
|
82
|
+
We can write a stories describing the `ButtonComponent`
|
72
83
|
|
73
84
|
```ruby
|
74
85
|
class ButtonComponentStories < ViewComponent::Storybook::Stories
|
@@ -88,7 +99,7 @@ end
|
|
88
99
|
|
89
100
|
### Generating Storybook Stories JSON
|
90
101
|
|
91
|
-
Generate the Storybook JSON stories by
|
102
|
+
Generate the Storybook JSON stories by running the rake task:
|
92
103
|
```sh
|
93
104
|
rake view_component_storybook:write_stories_json
|
94
105
|
```
|
@@ -108,7 +119,7 @@ Alternatively you can use tools like [Foreman](https://github.com/ddollar/forema
|
|
108
119
|
|
109
120
|
### Configuration
|
110
121
|
|
111
|
-
By Default ViewComponent::Storybook expects to find stories in the folder `test/components/stories`. This can be configured
|
122
|
+
By Default ViewComponent::Storybook expects to find stories in the folder `test/components/stories`. This can be configured by setting `stories_path` in `config/application.rb`. For example if you're using RSpec you might set the following configuration:
|
112
123
|
|
113
124
|
```ruby
|
114
125
|
config.view_component_storybook.stories_path = Rails.root.join("spec/components/stories")
|
@@ -131,7 +142,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
131
142
|
|
132
143
|
## Contributing
|
133
144
|
|
134
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/jonspalmer/
|
145
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jonspalmer/view_component_storybook. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
135
146
|
|
136
147
|
## License
|
137
148
|
|
@@ -14,11 +14,12 @@ module ViewComponent
|
|
14
14
|
content_security_policy(false) if respond_to?(:content_security_policy)
|
15
15
|
|
16
16
|
def show
|
17
|
-
|
17
|
+
params_hash = params.permit!.to_h
|
18
|
+
method_args = @story.constructor_args.resolve_method_args(params_hash)
|
18
19
|
|
19
20
|
@content_block = @story.content_block
|
20
21
|
|
21
|
-
@component = @story.component.new(**
|
22
|
+
@component = @story.component.new(*method_args.args, **method_args.kwargs)
|
22
23
|
|
23
24
|
layout = @story.layout
|
24
25
|
render layout: layout unless layout.nil?
|
@@ -0,0 +1,32 @@
|
|
1
|
+
en:
|
2
|
+
activemodel:
|
3
|
+
errors:
|
4
|
+
models:
|
5
|
+
view_component/storybook/method_args/validatable_method_args:
|
6
|
+
attributes:
|
7
|
+
args:
|
8
|
+
too_few: expected at least %{min} but found %{count}
|
9
|
+
too_many: expected no more than %{max} but found %{count}
|
10
|
+
kwargs:
|
11
|
+
invalid_arg: "'%{kwarg}' is invalid"
|
12
|
+
invalid: expected keys [%{expected_keys}] but found [%{actual_keys}]
|
13
|
+
view_component/storybook/method_args/control_method_args:
|
14
|
+
attributes:
|
15
|
+
controls:
|
16
|
+
invalid_control: "'%{control_name}' is invalid: (%{control_errors})"
|
17
|
+
view_component/storybook/stories:
|
18
|
+
attributes:
|
19
|
+
story_configs:
|
20
|
+
invalid_story: "'%{story_name}' is invalid: (%{story_errors})"
|
21
|
+
duplicate_stories:
|
22
|
+
one: duplicate story name %{duplicate_names}
|
23
|
+
other: duplicate story names %{duplicate_names}
|
24
|
+
view_component/storybook/story_config:
|
25
|
+
attributes:
|
26
|
+
constructor_args:
|
27
|
+
invalid: "invalid: (%{errors})"
|
28
|
+
view_component/storybook/controls/custom_config:
|
29
|
+
attributes:
|
30
|
+
value_method_args:
|
31
|
+
invalid: "invalid: (%{errors})"
|
32
|
+
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "active_model"
|
4
4
|
require "active_support/dependencies/autoload"
|
5
|
-
require "view_component/storybook/engine"
|
6
5
|
|
7
6
|
module ViewComponent
|
8
7
|
module Storybook
|
@@ -11,7 +10,9 @@ module ViewComponent
|
|
11
10
|
autoload :Controls
|
12
11
|
autoload :Stories
|
13
12
|
autoload :StoryConfig
|
13
|
+
autoload :ControlMethodArgs
|
14
14
|
autoload :Dsl
|
15
|
+
autoload :MethodArgs
|
15
16
|
|
16
17
|
include ActiveSupport::Configurable
|
17
18
|
# Set the location of component previews through app configuration:
|
@@ -8,12 +8,16 @@ module ViewComponent
|
|
8
8
|
extend ActiveSupport::Autoload
|
9
9
|
|
10
10
|
autoload :ControlConfig
|
11
|
-
autoload :
|
11
|
+
autoload :SimpleControlConfig
|
12
|
+
autoload :TextConfig
|
13
|
+
autoload :BooleanConfig
|
14
|
+
autoload :ColorConfig
|
12
15
|
autoload :NumberConfig
|
13
16
|
autoload :OptionsConfig
|
14
17
|
autoload :ArrayConfig
|
15
18
|
autoload :DateConfig
|
16
19
|
autoload :ObjectConfig
|
20
|
+
autoload :CustomConfig
|
17
21
|
end
|
18
22
|
end
|
19
23
|
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
module ViewComponent
|
4
4
|
module Storybook
|
5
5
|
module Controls
|
6
|
-
class ArrayConfig <
|
6
|
+
class ArrayConfig < SimpleControlConfig
|
7
7
|
attr_reader :separator
|
8
8
|
|
9
|
-
validates :
|
9
|
+
validates :separator, presence: true
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
super(
|
11
|
+
def initialize(default_value, separator = ",", param: nil, name: nil)
|
12
|
+
super(default_value, param: param, name: name)
|
13
13
|
@separator = separator
|
14
14
|
end
|
15
15
|
|
@@ -17,11 +17,12 @@ module ViewComponent
|
|
17
17
|
:array
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
20
|
+
def value_from_params(params)
|
21
|
+
params_value = super(params)
|
22
|
+
if params_value.is_a?(String)
|
23
|
+
params_value.split(separator)
|
23
24
|
else
|
24
|
-
|
25
|
+
params_value
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Storybook
|
5
|
+
module Controls
|
6
|
+
class BooleanConfig < SimpleControlConfig
|
7
|
+
BOOLEAN_VALUES = [true, false].freeze
|
8
|
+
|
9
|
+
validates :default_value, inclusion: { in: BOOLEAN_VALUES }, unless: -> { default_value.nil? }
|
10
|
+
|
11
|
+
def type
|
12
|
+
:boolean
|
13
|
+
end
|
14
|
+
|
15
|
+
def value_from_params(params)
|
16
|
+
params_value = super(params)
|
17
|
+
if params_value.is_a?(String) && params_value.present?
|
18
|
+
case params_value
|
19
|
+
when "true"
|
20
|
+
true
|
21
|
+
when "false"
|
22
|
+
false
|
23
|
+
end
|
24
|
+
else
|
25
|
+
params_value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Storybook
|
5
|
+
module Controls
|
6
|
+
class ColorConfig < SimpleControlConfig
|
7
|
+
attr_reader :preset_colors
|
8
|
+
|
9
|
+
def initialize(default_value, preset_colors: nil, param: nil, name: nil)
|
10
|
+
super(default_value, param: param, name: name)
|
11
|
+
@preset_colors = preset_colors
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
:color
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def csf_control_params
|
21
|
+
super.merge(presetColors: preset_colors).compact
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -6,43 +6,39 @@ module ViewComponent
|
|
6
6
|
class ControlConfig
|
7
7
|
include ActiveModel::Validations
|
8
8
|
|
9
|
-
|
9
|
+
validates :param, presence: true
|
10
10
|
|
11
|
-
|
12
|
-
validates :param, inclusion: { in: ->(knob_config) { knob_config.component_params } }, unless: -> { component.nil? }
|
13
|
-
|
14
|
-
def initialize(component, param, value, name: nil)
|
15
|
-
@component = component
|
11
|
+
def initialize(param: nil, name: nil)
|
16
12
|
@param = param
|
17
|
-
@
|
18
|
-
@name = name || param.to_s.humanize.titlecase
|
13
|
+
@name = name
|
19
14
|
end
|
20
15
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
16
|
+
def name(new_name = nil)
|
17
|
+
if new_name.nil?
|
18
|
+
@name ||= param.to_s.humanize.titlecase
|
19
|
+
else
|
20
|
+
@name = new_name
|
21
|
+
self
|
22
|
+
end
|
27
23
|
end
|
28
24
|
|
29
|
-
def
|
30
|
-
param
|
31
|
-
end
|
25
|
+
def param(new_param = nil)
|
26
|
+
return @param if new_param.nil?
|
32
27
|
|
33
|
-
|
34
|
-
|
28
|
+
@param = new_param
|
29
|
+
self
|
35
30
|
end
|
36
31
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
value
|
32
|
+
def to_csf_params
|
33
|
+
# :nocov:
|
34
|
+
raise NotImplementedError
|
35
|
+
# :nocov:
|
42
36
|
end
|
43
37
|
|
44
|
-
def
|
45
|
-
|
38
|
+
def value_from_params(params)
|
39
|
+
# :nocov:
|
40
|
+
raise NotImplementedError
|
41
|
+
# :nocov:
|
46
42
|
end
|
47
43
|
end
|
48
44
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module Storybook
|
5
|
+
module Controls
|
6
|
+
class CustomConfig < ControlConfig
|
7
|
+
attr_reader :value_method_args
|
8
|
+
|
9
|
+
validate :validate_value_method_args
|
10
|
+
|
11
|
+
def with_value(*args, **kwargs, &block)
|
12
|
+
@value_method_args = ViewComponent::Storybook::MethodArgs::ControlMethodArgs.new(block, *args, **kwargs, &block)
|
13
|
+
@value_method_args.with_param_prefix(param)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def param(new_param = nil)
|
18
|
+
value_method_args.with_param_prefix(new_param) unless new_param.nil?
|
19
|
+
super(new_param)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_csf_params
|
23
|
+
validate!
|
24
|
+
# TODO: Figure out if we can use 'category' with the args table
|
25
|
+
# export default {
|
26
|
+
# argTypes: {
|
27
|
+
# foo: {
|
28
|
+
# table: { category: 'cat', subcategory: 'sub' }
|
29
|
+
# }
|
30
|
+
# }
|
31
|
+
# }
|
32
|
+
value_method_args.controls.reduce({}) do |csf_params, control|
|
33
|
+
csf_params.deep_merge(control.to_csf_params)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def value_from_params(params)
|
38
|
+
method_args = value_method_args.resolve_method_args(params)
|
39
|
+
|
40
|
+
method_args.block.call(*method_args.args, **method_args.kwargs)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def validate_value_method_args
|
46
|
+
return if value_method_args.valid?
|
47
|
+
|
48
|
+
value_method_args_errors = value_method_args.errors.full_messages.join(', ')
|
49
|
+
errors.add(:value_method_args, :invalid, errors: value_method_args_errors)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -3,32 +3,33 @@
|
|
3
3
|
module ViewComponent
|
4
4
|
module Storybook
|
5
5
|
module Controls
|
6
|
-
class DateConfig <
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(component, param, value, name: nil)
|
10
|
-
super(component, param, value, name: name)
|
6
|
+
class DateConfig < SimpleControlConfig
|
7
|
+
def initialize(default_value, param: nil, name: nil)
|
8
|
+
super(default_value, param: param, name: name)
|
11
9
|
end
|
12
10
|
|
13
11
|
def type
|
14
12
|
:date
|
15
13
|
end
|
16
14
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
15
|
+
def value_from_params(params)
|
16
|
+
params_value = super(params)
|
17
|
+
if params_value.is_a?(String)
|
18
|
+
DateTime.iso8601(params_value)
|
20
19
|
else
|
21
|
-
|
20
|
+
params_value
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
25
24
|
private
|
26
25
|
|
27
26
|
def csf_value
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
case default_value
|
28
|
+
when Date
|
29
|
+
default_value.in_time_zone
|
30
|
+
when Time
|
31
|
+
default_value.iso8601
|
32
|
+
end
|
32
33
|
end
|
33
34
|
end
|
34
35
|
end
|