view_component_storybook 0.6.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
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