view_component_storybook 0.6.0 → 0.10.1

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -128
  3. data/app/controllers/view_component/storybook/stories_controller.rb +10 -11
  4. data/app/views/view_component/storybook/stories/show.html.erb +8 -1
  5. data/config/locales/en.yml +32 -0
  6. data/lib/view_component/storybook.rb +14 -0
  7. data/lib/view_component/storybook/content_concern.rb +42 -0
  8. data/lib/view_component/storybook/controls.rb +5 -1
  9. data/lib/view_component/storybook/controls/base_options_config.rb +41 -0
  10. data/lib/view_component/storybook/controls/boolean_config.rb +7 -6
  11. data/lib/view_component/storybook/controls/color_config.rb +4 -6
  12. data/lib/view_component/storybook/controls/control_config.rb +25 -25
  13. data/lib/view_component/storybook/controls/controls_helpers.rb +76 -0
  14. data/lib/view_component/storybook/controls/custom_config.rb +52 -0
  15. data/lib/view_component/storybook/controls/date_config.rb +14 -13
  16. data/lib/view_component/storybook/controls/multi_options_config.rb +46 -0
  17. data/lib/view_component/storybook/controls/number_config.rb +13 -10
  18. data/lib/view_component/storybook/controls/object_config.rb +13 -7
  19. data/lib/view_component/storybook/controls/options_config.rb +17 -33
  20. data/lib/view_component/storybook/controls/simple_control_config.rb +48 -0
  21. data/lib/view_component/storybook/controls/text_config.rb +1 -3
  22. data/lib/view_component/storybook/dsl.rb +1 -2
  23. data/lib/view_component/storybook/dsl/{controls_dsl.rb → legacy_controls_dsl.rb} +19 -22
  24. data/lib/view_component/storybook/engine.rb +2 -1
  25. data/lib/view_component/storybook/method_args.rb +16 -0
  26. data/lib/view_component/storybook/method_args/control_method_args.rb +91 -0
  27. data/lib/view_component/storybook/method_args/method_args.rb +52 -0
  28. data/lib/view_component/storybook/method_args/method_parameters_names.rb +97 -0
  29. data/lib/view_component/storybook/slots.rb +14 -0
  30. data/lib/view_component/storybook/slots/slot.rb +24 -0
  31. data/lib/view_component/storybook/slots/slot_config.rb +44 -0
  32. data/lib/view_component/storybook/stories.rb +60 -14
  33. data/lib/view_component/storybook/story.rb +18 -0
  34. data/lib/view_component/storybook/story_config.rb +140 -15
  35. data/lib/view_component/storybook/tasks/write_stories_json.rake +6 -0
  36. data/lib/view_component/storybook/version.rb +1 -1
  37. metadata +64 -23
  38. data/lib/view_component/storybook/controls/array_config.rb +0 -36
  39. data/lib/view_component/storybook/dsl/story_dsl.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc377c2ee4e53057d77622560164f181fcaca50a95e4fcfc53ad78a3cb10a2ba
4
- data.tar.gz: b1137074ecbb7b5d44c38475774d06400b936f427dfec1bd1602fe4edb1696d7
3
+ metadata.gz: b6ef1bcb6cf8fd3beccec9242854aa913bd31fe1c7c44c44cdde6b59be8fd67f
4
+ data.tar.gz: de047bb6e4e453db106270ba7f28b4d252fed881816266e10b99bb8c2d4b93f0
5
5
  SHA512:
6
- metadata.gz: c8bbafc19737e00f631c0099affc6c616e327c4bfc60e9426fb74e55550701d915131f15d5d00d780dd3b6374c940edd17feebe24774c91e48db84accd46930d
7
- data.tar.gz: 2d992393f87285d057bedaffd9be722cd8d147a38f543b2a1afdb9c92d73e6ce26546744941826cd91517c2d1f30e27f782d8090e07d420343241c1a5bb20c8d
6
+ metadata.gz: 4d96299b59366d1f50d034399299209a7edc01aa99f3ff728b0e6ed1721b1f80679aa8f7bbb953be549baa59fd74d090eb8d5b7e615bdc7b26164ebb0f011373
7
+ data.tar.gz: d97204b485ac6d1b61c7848e6c2b68cec798328eef7e78738f3f675daa805dc6db73a0e6d1e6e7f17373de4b5ca90cfe000716bb4b25f29f8ae77d9985f0580e
data/README.md CHANGED
@@ -1,142 +1,21 @@
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
- * A Ruby DSL for writing Stories describing View Components
7
- * A Rails controller backend for Storybook Server compatible with Strobook 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
- ### Gem 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
- ### Storybook Installation
20
-
21
- 1. Add Storybook server as a dev dependedncy. The Storybook Controls addon isn't needed but is strongly recommended
22
- ```sh
23
- yarn add @storybook/server @storybook/addon-controls --dev
24
- ```
25
- 2. Add an NPM script to your package.json in order to start the storybook later in this guide
26
- ```json
27
- {
28
- "scripts": {
29
- "storybook": "start-storybook"
30
- }
31
- }
32
- ```
33
- 3. Create the .storybook/main.js file to configure Storybook to find the json stories the gem creates. Also configure the Controls addon:
34
- ```javascript
35
- module.exports = {
36
- stories: ['../test/components/**/*.stories.json'],
37
- addons: [
38
- '@storybook/addon-controls',
39
- ],
40
- };
41
- ```
42
- 4. Create the .storybook/preview.js file to configure Storybook with the Rails application url to call for the html content of the stories
43
- ```javascript
44
-
45
- export const parameters = {
46
- server: {
47
- url: `http://localhost:3000/rails/stories`,
48
- },
49
- };
50
- ```
51
-
52
-
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
-
55
- ## Usage
56
-
57
- ### Writing Stories
58
-
59
- `ViewComponent::Storybook::Stories` provides a way to preview components in Storybook.
60
-
61
- Suppose our app has a `ButtonComponent` that takes a `button_text` parameter:
62
-
63
- ```ruby
64
- class ButtonComponent < ViewComponent::Base
65
- def initialize(button_text:)
66
- @button_text = button_text
67
- end
68
- end
69
- ```
70
-
71
- We can write a stories desecibing the `ButtonComponent`
72
-
73
- ```ruby
74
- class ButtonComponentStories < ViewComponent::Storybook::Stories
75
- story(:with_short_text) do
76
- controls do
77
- text(:button_text, 'OK')
78
- end
79
- end
80
-
81
- story(:with_long_text) do
82
- controls do
83
- text(:button_text, 'Push Me Please!')
84
- end
85
- end
86
- end
87
- ```
88
-
89
- ### Generating Storybook Stories JSON
90
-
91
- Generate the Storybook JSON stories by tunning the rake task:
92
- ```sh
93
- rake view_component_storybook:write_stories_json
94
- ```
95
-
96
- ### Start the Rails app and Storybook
97
-
98
- In separate shells start the Rails app and Storybook
99
-
100
- ```sh
101
- rails s
102
- ```
103
- ```sh
104
- yarn storybook
105
- ```
106
-
107
- Alternatively you can use tools like [Foreman](https://github.com/ddollar/foreman) to start both Rails and Storybook with one command.
108
-
109
- ### Configuration
110
-
111
- By Default ViewComponent::Storybook expects to find stories in the folder `test/components/stories`. This can be configured but setting `stories_path` in `config/applicaion.rb`. For example is you're using RSpec you might set the following configuration:
112
-
113
- ```ruby
114
- config.view_component_storybook.stories_path = Rails.root.join("spec/components/stories")
115
- ```
116
-
117
- ### The Story DSL
118
-
119
- Coming Soon
120
-
121
- #### Parameters
122
- #### Layout
123
- #### Controls
124
-
125
-
126
- ## Development
127
-
128
- 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.
129
-
130
- 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.
131
14
 
132
15
  ## Contributing
133
16
 
134
- Bug reports and pull requests are welcome on GitHub at https://github.com/jonspalmer/actionview-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.
135
18
 
136
19
  ## License
137
20
 
138
21
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
139
-
140
- ## Code of Conduct
141
-
142
- 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,19 +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
- component_args = @story.values_from_params(params.permit!.to_h)
17
+ params_hash = params.permit!.to_h
18
18
 
19
- @content_block = @story.content_block
19
+ @story = @story_config.story(params_hash)
20
20
 
21
- @component = @story.component.new(**component_args)
21
+ layout = @story_config.layout
22
22
 
23
- layout = @story.layout
24
23
  render layout: layout unless layout.nil?
25
24
  end
26
25
 
@@ -30,17 +29,17 @@ module ViewComponent
30
29
  ViewComponent::Storybook.show_stories
31
30
  end
32
31
 
33
- def find_stories
32
+ def find_story_configs
34
33
  stories_name = params[:stories]
35
- @stories = ViewComponent::Storybook::Stories.find_stories(stories_name)
34
+ @story_configs = Stories.find_story_configs(stories_name)
36
35
 
37
- head :not_found unless @stories
36
+ head :not_found unless @story_configs
38
37
  end
39
38
 
40
- def find_story
39
+ def find_story_config
41
40
  story_name = params[:story]
42
- @story = @stories.find_story(story_name)
43
- head :not_found unless @story
41
+ @story_config = @story_configs.find_story_config(story_name)
42
+ head :not_found unless @story_config
44
43
  end
45
44
  end
46
45
  end
@@ -1 +1,8 @@
1
- <%= render(@component, &@content_block)%>
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 -%>
@@ -0,0 +1,32 @@
1
+ en:
2
+ activemodel:
3
+ errors:
4
+ models:
5
+ view_component/storybook/method_args/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
+
@@ -10,6 +10,10 @@ module ViewComponent
10
10
  autoload :Controls
11
11
  autoload :Stories
12
12
  autoload :StoryConfig
13
+ autoload :Story
14
+ autoload :Slots
15
+ autoload :ContentConcern
16
+ autoload :MethodArgs
13
17
  autoload :Dsl
14
18
 
15
19
  include ActiveSupport::Configurable
@@ -27,6 +31,16 @@ module ViewComponent
27
31
  #
28
32
  mattr_accessor :show_stories, instance_writer: false
29
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
+
30
44
  ActiveSupport.run_load_hooks(:view_component_storybook, self)
31
45
  end
32
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
@@ -8,14 +8,18 @@ module ViewComponent
8
8
  extend ActiveSupport::Autoload
9
9
 
10
10
  autoload :ControlConfig
11
+ autoload :SimpleControlConfig
11
12
  autoload :TextConfig
12
13
  autoload :BooleanConfig
13
14
  autoload :ColorConfig
14
15
  autoload :NumberConfig
16
+ autoload :BaseOptionsConfig
15
17
  autoload :OptionsConfig
16
- autoload :ArrayConfig
18
+ autoload :MultiOptionsConfig
17
19
  autoload :DateConfig
18
20
  autoload :ObjectConfig
21
+ autoload :CustomConfig
22
+ autoload :ControlsHelpers
19
23
  end
20
24
  end
21
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
@@ -3,25 +3,26 @@
3
3
  module ViewComponent
4
4
  module Storybook
5
5
  module Controls
6
- class BooleanConfig < ControlConfig
6
+ class BooleanConfig < SimpleControlConfig
7
7
  BOOLEAN_VALUES = [true, false].freeze
8
8
 
9
- validates :value, inclusion: { in: BOOLEAN_VALUES }
9
+ validates :default_value, inclusion: { in: BOOLEAN_VALUES }, unless: -> { default_value.nil? }
10
10
 
11
11
  def type
12
12
  :boolean
13
13
  end
14
14
 
15
- def value_from_param(param)
16
- if param.is_a?(String) && param.present?
17
- case param
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
18
19
  when "true"
19
20
  true
20
21
  when "false"
21
22
  false
22
23
  end
23
24
  else
24
- super(param)
25
+ params_value
25
26
  end
26
27
  end
27
28
  end
@@ -3,12 +3,11 @@
3
3
  module ViewComponent
4
4
  module Storybook
5
5
  module Controls
6
- class ColorConfig < ControlConfig
6
+ class ColorConfig < SimpleControlConfig
7
7
  attr_reader :preset_colors
8
- validates :value, presence: true
9
8
 
10
- def initialize(component, param, value, name: nil, preset_colors: nil)
11
- super(component, param, value, name: name)
9
+ def initialize(default_value, preset_colors: nil, param: nil, name: nil)
10
+ super(default_value, param: param, name: name)
12
11
  @preset_colors = preset_colors
13
12
  end
14
13
 
@@ -19,8 +18,7 @@ module ViewComponent
19
18
  private
20
19
 
21
20
  def csf_control_params
22
- params = super
23
- params.merge(presetColors: preset_colors).compact
21
+ super.merge(presetColors: preset_colors).compact
24
22
  end
25
23
  end
26
24
  end