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.
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