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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8ab883711eb6c712fcb3f654fdcb80586091f6da753e311f2609347d600391e
4
- data.tar.gz: 65ba53ff31930882f7c2a30492e5444ed438be268cfbb24291160ff38f712964
3
+ metadata.gz: 71e48c5a57bcaf3c754570c01203b0953e0a93ae6a88bfaa62c7db065535e112
4
+ data.tar.gz: ae5ab22539296310681f81b6efe156bda605c5472c41f2f5f803676664df71c2
5
5
  SHA512:
6
- metadata.gz: 24bf672f2214344c1d5b9e1af7bbe8ef51ddb8ba6065eca4b12b14e0c8b73f69381168b39d3f4339e953a086ec11d69f716f3fce7dfb8acd7efaabd3678cb578
7
- data.tar.gz: 0df0d064cccf1ed1d625ba1d9cb439d8c3fb65f56a7671e3d65650dc85604ea89990e578ca4550863388d7cfc201b7cd33b7add24f294deb6646b1753062c03a
6
+ metadata.gz: e4ac764fd6a7fc4f4d10e2693f05056ef2441ab564d48411a30eea93dd5b843e947e8470b3743c4124ad41d9eff33b1fc2830debcd8e9dbbd12002d9ff5590e7
7
+ data.tar.gz: 77f2e1d325cb1c1ca4cf2e7ef2ab35ebb7230559d959f5385a95cc40f8bf3496f98e2ef715fea3a50fda3413755d1e18349df81e6510ac3de544bd47354a3ef7
data/README.md CHANGED
@@ -3,151 +3,19 @@
3
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
- * A Ruby DSL for writing Stories describing View Components
7
- * A Rails controller backend for Storybook Server compatible with Storybook Controls Addon parameters
8
- * Coming Soon: Rake tasks to watch View Components and Stories and trigger Storybook hot reloading
9
6
 
10
- ## Installation
7
+ - A Ruby DSL for writing Stories describing View Components
8
+ - A Rails controller backend for Storybook Server compatible with Storybook Controls Addon parameters
9
+ - Coming Soon: Rake tasks to watch View Components and Stories and trigger Storybook hot reloading
11
10
 
12
- ### Rails Installation
11
+ ## Documentation
13
12
 
14
- 1. Add the `view_component_storybook` gem, to your Gemfile: `gem 'view_component_storybook'`
15
- 2. Run `bundle install`.
16
- 3. Add `require "view_component/storybook/engine"` to `config/application.rb`
17
- 4. Add `**/*.stories.json` to `.gitignore`
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
-
32
- ### Storybook Installation
33
-
34
- 1. Add Storybook server as a dev dependedncy. The Storybook Controls addon isn't needed but is strongly recommended
35
- ```sh
36
- yarn add @storybook/server @storybook/addon-controls --dev
37
- ```
38
- 2. Add an NPM script to your package.json in order to start the storybook later in this guide
39
- ```json
40
- {
41
- "scripts": {
42
- "storybook": "start-storybook"
43
- }
44
- }
45
- ```
46
- 3. Create the .storybook/main.js file to configure Storybook to find the json stories the gem creates. Also configure the Controls addon:
47
- ```javascript
48
- module.exports = {
49
- stories: ['../test/components/**/*.stories.json'],
50
- addons: [
51
- '@storybook/addon-controls',
52
- ],
53
- };
54
- ```
55
- 4. Create the .storybook/preview.js file to configure Storybook with the Rails application url to call for the html content of the stories
56
- ```javascript
57
-
58
- export const parameters = {
59
- server: {
60
- url: `http://localhost:3000/rails/stories`,
61
- },
62
- };
63
- ```
64
-
65
-
66
- ## Usage
67
-
68
- ### Writing Stories
69
-
70
- `ViewComponent::Storybook::Stories` provides a way to preview components in Storybook.
71
-
72
- Suppose our app has a `ButtonComponent` that takes a `button_text` parameter:
73
-
74
- ```ruby
75
- class ButtonComponent < ViewComponent::Base
76
- def initialize(button_text:)
77
- @button_text = button_text
78
- end
79
- end
80
- ```
81
-
82
- We can write a stories describing the `ButtonComponent`
83
-
84
- ```ruby
85
- class ButtonComponentStories < ViewComponent::Storybook::Stories
86
- story(:with_short_text) do
87
- controls do
88
- text(:button_text, 'OK')
89
- end
90
- end
91
-
92
- story(:with_long_text) do
93
- controls do
94
- text(:button_text, 'Push Me Please!')
95
- end
96
- end
97
- end
98
- ```
99
-
100
- ### Generating Storybook Stories JSON
101
-
102
- Generate the Storybook JSON stories by running the rake task:
103
- ```sh
104
- rake view_component_storybook:write_stories_json
105
- ```
106
-
107
- ### Start the Rails app and Storybook
108
-
109
- In separate shells start the Rails app and Storybook
110
-
111
- ```sh
112
- rails s
113
- ```
114
- ```sh
115
- yarn storybook
116
- ```
117
-
118
- Alternatively you can use tools like [Foreman](https://github.com/ddollar/foreman) to start both Rails and Storybook with one command.
119
-
120
- ### Configuration
121
-
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:
123
-
124
- ```ruby
125
- config.view_component_storybook.stories_path = Rails.root.join("spec/components/stories")
126
- ```
127
-
128
- ### The Story DSL
129
-
130
- Coming Soon
131
-
132
- #### Parameters
133
- #### Layout
134
- #### Controls
135
-
136
-
137
- ## Development
138
-
139
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
140
-
141
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
13
+ See [jonspalmer.github.io/view_component_storybook](https://jonspalmer.github.io/view_component_storybook) for documentation.
142
14
 
143
15
  ## Contributing
144
16
 
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.
17
+ This project is intended to be a safe, welcoming space for collaboration. Contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. We recommend reading the [contributing guide](./docs/CONTRIBUTING.md) as well.
146
18
 
147
19
  ## License
148
20
 
149
21
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
150
-
151
- ## Code of Conduct
152
-
153
- Everyone interacting in the ViewComponent::Storybook project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/jonspalmer/view_component_storybook/blob/master/CODE_OF_CONDUCT.md).
@@ -8,20 +8,18 @@ module ViewComponent
8
8
  prepend_view_path File.expand_path("../../../views", __dir__)
9
9
  prepend_view_path Rails.root.join("app/views") if defined?(Rails.root)
10
10
 
11
- before_action :find_stories, :find_story, only: :show
11
+ before_action :find_story_configs, :find_story_config, only: :show
12
12
  before_action :require_local!, unless: :show_stories?
13
13
 
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)
19
18
 
20
- @content_block = @story.content_block
19
+ @story = @story_config.story(params_hash)
21
20
 
22
- @component = @story.component.new(*method_args.args, **method_args.kwargs)
21
+ layout = @story_config.layout
23
22
 
24
- layout = @story.layout
25
23
  render layout: layout unless layout.nil?
26
24
  end
27
25
 
@@ -31,17 +29,17 @@ module ViewComponent
31
29
  ViewComponent::Storybook.show_stories
32
30
  end
33
31
 
34
- def find_stories
32
+ def find_story_configs
35
33
  stories_name = params[:stories]
36
- @stories = ViewComponent::Storybook::Stories.find_stories(stories_name)
34
+ @story_configs = Stories.find_story_configs(stories_name)
37
35
 
38
- head :not_found unless @stories
36
+ head :not_found unless @story_configs
39
37
  end
40
38
 
41
- def find_story
39
+ def find_story_config
42
40
  story_name = params[:story]
43
- @story = @stories.find_story(story_name)
44
- head :not_found unless @story
41
+ @story_config = @story_configs.find_story_config(story_name)
42
+ head :not_found unless @story_config
45
43
  end
46
44
  end
47
45
  end
@@ -1,3 +1,8 @@
1
- <%= render(@component) do -%>
2
- <%= instance_eval(&@content_block) -%>
3
- <% end %>
1
+ <%= render(@story.component) do |c| -%>
2
+ <% @story.slots.each do |slot| -%>
3
+ <%- slot.call do -%>
4
+ <%= instance_eval(&slot.content_block) if slot.content_block -%>
5
+ <%- end -%>
6
+ <%- end -%>
7
+ <%- instance_eval(&@story.content_block) if @story.content_block -%>
8
+ <%- end -%>
@@ -2,7 +2,7 @@ en:
2
2
  activemodel:
3
3
  errors:
4
4
  models:
5
- view_component/storybook/method_args/validatable_method_args:
5
+ view_component/storybook/method_args/method_args:
6
6
  attributes:
7
7
  args:
8
8
  too_few: expected at least %{min} but found %{count}
@@ -10,9 +10,11 @@ module ViewComponent
10
10
  autoload :Controls
11
11
  autoload :Stories
12
12
  autoload :StoryConfig
13
- autoload :ControlMethodArgs
14
- autoload :Dsl
13
+ autoload :Story
14
+ autoload :Slots
15
+ autoload :ContentConcern
15
16
  autoload :MethodArgs
17
+ autoload :Dsl
16
18
 
17
19
  include ActiveSupport::Configurable
18
20
  # Set the location of component previews through app configuration:
@@ -29,6 +31,16 @@ module ViewComponent
29
31
  #
30
32
  mattr_accessor :show_stories, instance_writer: false
31
33
 
34
+ # Set the entry route for component stories:
35
+ #
36
+ # config.view_component_storybook.stories_route = "/stories"
37
+ #
38
+ # Defaults to `/rails/stories` when `show_stories` is enabled.
39
+ #
40
+ mattr_accessor :stories_route, instance_writer: false do
41
+ "/rails/stories"
42
+ end
43
+
32
44
  ActiveSupport.run_load_hooks(:view_component_storybook, self)
33
45
  end
34
46
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Storybook
5
+ module ContentConcern
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_reader :content_control, :content_block
10
+ end
11
+
12
+ def content(content = nil, &block)
13
+ case content
14
+ when Storybook::Controls::ControlConfig
15
+ @content_control = content.param(content_param)
16
+ @content_block = nil
17
+ when String
18
+ @content_control = nil
19
+ @content_block = proc { content }
20
+ else
21
+ @content_control = nil
22
+ @content_block = block
23
+ end
24
+ end
25
+
26
+ def resolve_content_block(params)
27
+ if content_control
28
+ content = content_control.value_from_params(params)
29
+ proc { content }
30
+ else
31
+ content_block
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def content_param
38
+ :content
39
+ end
40
+ end
41
+ end
42
+ end
@@ -13,11 +13,13 @@ module ViewComponent
13
13
  autoload :BooleanConfig
14
14
  autoload :ColorConfig
15
15
  autoload :NumberConfig
16
+ autoload :BaseOptionsConfig
16
17
  autoload :OptionsConfig
17
- autoload :ArrayConfig
18
+ autoload :MultiOptionsConfig
18
19
  autoload :DateConfig
19
20
  autoload :ObjectConfig
20
21
  autoload :CustomConfig
22
+ autoload :ControlsHelpers
21
23
  end
22
24
  end
23
25
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Storybook
5
+ module Controls
6
+ class BaseOptionsConfig < SimpleControlConfig
7
+ attr_reader :type, :options, :labels
8
+
9
+ validates :type, :options, presence: true
10
+
11
+ def initialize(type, options, default_value, labels: nil, param: nil, name: nil)
12
+ super(default_value, param: param, name: name)
13
+ @type = type
14
+ @options = options
15
+ @labels = labels
16
+ normalize_options
17
+ end
18
+
19
+ def to_csf_params
20
+ super.deep_merge(argTypes: { param => { options: options } })
21
+ end
22
+
23
+ private
24
+
25
+ def csf_control_params
26
+ labels.nil? ? super : super.merge(labels: labels)
27
+ end
28
+
29
+ def normalize_options
30
+ return unless options.is_a?(Hash)
31
+
32
+ warning = "Hash options is deprecated and will be removed in v1.0.0. Use array options and `labels` instead."
33
+ ActiveSupport::Deprecation.warn(warning)
34
+
35
+ @labels = options.invert
36
+ @options = options.values
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -29,6 +29,10 @@ module ViewComponent
29
29
  self
30
30
  end
31
31
 
32
+ def prefix_param(prefix)
33
+ param("#{prefix}__#{@param}".to_sym)
34
+ end
35
+
32
36
  def to_csf_params
33
37
  # :nocov:
34
38
  raise NotImplementedError
@@ -2,8 +2,8 @@
2
2
 
3
3
  module ViewComponent
4
4
  module Storybook
5
- module Dsl
6
- module ControlsDsl
5
+ module Controls
6
+ module ControlsHelpers
7
7
  def text(default_value)
8
8
  Controls::TextConfig.new(default_value)
9
9
  end
@@ -28,32 +28,33 @@ module ViewComponent
28
28
  Controls::ObjectConfig.new(default_value)
29
29
  end
30
30
 
31
- def select(options, default_value)
32
- Controls::OptionsConfig.new(:select, options, default_value)
31
+ def select(options, default_value, labels: nil)
32
+ Controls::OptionsConfig.new(:select, options, default_value, labels: labels)
33
33
  end
34
34
 
35
- def multi_select(options, default_value)
36
- Controls::OptionsConfig.new(:'multi-select', options, default_value)
35
+ def multi_select(options, default_value, labels: nil)
36
+ Controls::MultiOptionsConfig.new(:'multi-select', options, default_value, labels: labels)
37
37
  end
38
38
 
39
- def radio(options, default_value)
40
- Controls::OptionsConfig.new(:radio, options, default_value)
39
+ def radio(options, default_value, labels: nil)
40
+ Controls::OptionsConfig.new(:radio, options, default_value, labels: labels)
41
41
  end
42
42
 
43
- def inline_radio(options, default_value)
44
- Controls::OptionsConfig.new(:'inline-radio', options, default_value)
43
+ def inline_radio(options, default_value, labels: nil)
44
+ Controls::OptionsConfig.new(:'inline-radio', options, default_value, labels: labels)
45
45
  end
46
46
 
47
- def check(options, default_value)
48
- Controls::OptionsConfig.new(:check, options, default_value)
47
+ def check(options, default_value, labels: nil)
48
+ Controls::MultiOptionsConfig.new(:check, options, default_value, labels: labels)
49
49
  end
50
50
 
51
51
  def inline_check(options, default_value)
52
- Controls::OptionsConfig.new(:'inline-check', options, default_value)
52
+ Controls::MultiOptionsConfig.new(:'inline-check', options, default_value)
53
53
  end
54
54
 
55
- def array(default_value, separator = ",")
56
- Controls::ArrayConfig.new(default_value, separator)
55
+ def array(default_value, separator = nil)
56
+ ActiveSupport::Deprecation.warn("`array` `separator` argument will be removed in v1.0.0.") if separator
57
+ Controls::ObjectConfig.new(default_value)
57
58
  end
58
59
 
59
60
  def date(default_value)
@@ -64,7 +65,11 @@ module ViewComponent
64
65
  Controls::CustomConfig.new.with_value(*args, **kwargs, &block)
65
66
  end
66
67
 
67
- Controls = ViewComponent::Storybook::Controls
68
+ def klazz(value_class, *args, **kwargs)
69
+ Controls::CustomConfig.new.with_value(*args, **kwargs) do |*a, **kwa|
70
+ value_class.new(*a, **kwa)
71
+ end
72
+ end
68
73
  end
69
74
  end
70
75
  end