lookbook 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +47 -14
  4. data/app/components/lookbook/code/component.html.erb +1 -1
  5. data/app/components/lookbook/inspector_panel/component.rb +3 -5
  6. data/app/components/lookbook/nav/item/component.html.erb +1 -1
  7. data/app/components/lookbook/params/editor/component.rb +3 -10
  8. data/app/components/lookbook/params/field/component.html.erb +8 -8
  9. data/app/components/lookbook/params/field/component.rb +21 -72
  10. data/app/controllers/concerns/lookbook/targetable_concern.rb +156 -0
  11. data/app/controllers/concerns/lookbook/with_preview_controller_concern.rb +13 -0
  12. data/app/controllers/lookbook/application_controller.rb +13 -3
  13. data/app/controllers/lookbook/inspector_controller.rb +45 -0
  14. data/app/controllers/lookbook/page_controller.rb +11 -7
  15. data/app/controllers/lookbook/previews_controller.rb +4 -210
  16. data/app/helpers/lookbook/output_helper.rb +5 -5
  17. data/app/views/layouts/lookbook/skeleton.html.erb +3 -3
  18. data/app/views/lookbook/index.html.erb +12 -1
  19. data/app/views/lookbook/{previews → inspector}/inputs/_color.html.erb +0 -0
  20. data/app/views/lookbook/{previews → inspector}/inputs/_range.html.erb +0 -0
  21. data/app/views/lookbook/{previews → inspector}/inputs/_select.html.erb +0 -0
  22. data/app/views/lookbook/{previews → inspector}/inputs/_text.html.erb +0 -0
  23. data/app/views/lookbook/{previews → inspector}/inputs/_textarea.html.erb +0 -0
  24. data/app/views/lookbook/{previews → inspector}/inputs/_toggle.html.erb +3 -3
  25. data/app/views/lookbook/{previews → inspector}/panels/_content.html.erb +0 -0
  26. data/app/views/lookbook/{previews → inspector}/panels/_notes.html.erb +0 -0
  27. data/app/views/lookbook/{previews → inspector}/panels/_output.html.erb +0 -0
  28. data/app/views/lookbook/{previews → inspector}/panels/_params.html.erb +4 -4
  29. data/app/views/lookbook/{previews → inspector}/panels/_preview.html.erb +0 -0
  30. data/app/views/lookbook/{previews → inspector}/panels/_source.html.erb +0 -0
  31. data/app/views/lookbook/{previews → inspector}/show.html.erb +4 -1
  32. data/config/app.yml +8 -1
  33. data/config/inputs.yml +12 -12
  34. data/config/panels.yml +5 -5
  35. data/config/routes.rb +5 -5
  36. data/config/tags.yml +4 -1
  37. data/lib/lookbook/engine.rb +101 -150
  38. data/lib/lookbook/file_watcher.rb +47 -0
  39. data/lib/lookbook/page.rb +15 -16
  40. data/lib/lookbook/param.rb +99 -0
  41. data/lib/lookbook/preview.rb +8 -1
  42. data/lib/lookbook/{preview_controller.rb → preview_actions.rb} +14 -3
  43. data/lib/lookbook/preview_example.rb +1 -1
  44. data/lib/lookbook/preview_group.rb +0 -4
  45. data/lib/lookbook/preview_parser.rb +53 -0
  46. data/lib/lookbook/process.rb +21 -0
  47. data/lib/lookbook/services/code/code_beautifier.rb +21 -0
  48. data/lib/lookbook/services/code/code_highlighter.rb +69 -0
  49. data/lib/lookbook/services/data/parsers/data_parser.rb +22 -0
  50. data/lib/lookbook/services/data/parsers/json_parser.rb +7 -0
  51. data/lib/lookbook/services/data/parsers/yaml_parser.rb +7 -0
  52. data/lib/lookbook/services/data/resolvers/data_resolver.rb +70 -0
  53. data/lib/lookbook/services/data/resolvers/eval_resolver.rb +10 -0
  54. data/lib/lookbook/services/data/resolvers/file_resolver.rb +28 -0
  55. data/lib/lookbook/services/data/resolvers/method_resolver.rb +10 -0
  56. data/lib/lookbook/services/data/resolvers/yaml_resolver.rb +18 -0
  57. data/lib/lookbook/services/markdown_renderer.rb +29 -0
  58. data/lib/lookbook/services/string_value_caster.rb +60 -0
  59. data/lib/lookbook/services/tags/tag_options_parser.rb +62 -0
  60. data/lib/lookbook/services/templates/action_view_annotations_handler.rb +21 -0
  61. data/lib/lookbook/services/templates/action_view_annotations_stripper.rb +15 -0
  62. data/lib/lookbook/services/templates/frontmatter_extractor.rb +28 -0
  63. data/lib/lookbook/services/templates/styles_extractor.rb +38 -0
  64. data/lib/lookbook/services/{search_param_builder.rb → urls/search_param_builder.rb} +1 -1
  65. data/lib/lookbook/services/{search_param_parser.rb → urls/search_param_parser.rb} +1 -1
  66. data/lib/lookbook/source_inspector.rb +26 -45
  67. data/lib/lookbook/stores/config_store.rb +7 -8
  68. data/lib/lookbook/stores/input_store.rb +7 -3
  69. data/lib/lookbook/stores/tag_store.rb +3 -5
  70. data/lib/lookbook/support/null_object.rb +10 -0
  71. data/lib/lookbook/support/service.rb +2 -2
  72. data/lib/lookbook/support/store.rb +1 -1
  73. data/lib/lookbook/support/utils/attribute_utils.rb +6 -1
  74. data/lib/lookbook/support/utils/path_utils.rb +6 -3
  75. data/lib/lookbook/tags/component_tag.rb +7 -0
  76. data/lib/lookbook/tags/custom_tag.rb +59 -0
  77. data/lib/lookbook/tags/display_tag.rb +15 -0
  78. data/lib/lookbook/tags/hidden_tag.rb +13 -0
  79. data/lib/lookbook/tags/id_tag.rb +7 -0
  80. data/lib/lookbook/tags/label_tag.rb +4 -0
  81. data/lib/lookbook/tags/logical_path.rb +4 -0
  82. data/lib/lookbook/tags/param_tag.rb +61 -0
  83. data/lib/lookbook/tags/position_tag.rb +16 -0
  84. data/lib/lookbook/tags/tag_provider.rb +18 -0
  85. data/lib/lookbook/tags/yard_tag.rb +62 -0
  86. data/lib/lookbook/utils.rb +0 -40
  87. data/lib/lookbook/version.rb +1 -1
  88. data/lib/lookbook/websocket.rb +60 -0
  89. data/lib/lookbook.rb +2 -1
  90. data/public/lookbook-assets/css/lookbook.css +30 -77
  91. data/public/lookbook-assets/css/lookbook.css.map +1 -1
  92. data/public/lookbook-assets/js/lookbook.js +7 -2
  93. data/public/lookbook-assets/js/lookbook.js.map +1 -1
  94. metadata +55 -26
  95. data/lib/lookbook/code_formatter.rb +0 -68
  96. data/lib/lookbook/markdown.rb +0 -22
  97. data/lib/lookbook/params.rb +0 -157
  98. data/lib/lookbook/parser.rb +0 -42
  99. data/lib/lookbook/tag.rb +0 -122
  100. data/lib/lookbook/tag_options.rb +0 -111
  101. data/lib/lookbook/tags.rb +0 -17
  102. data/lib/lookbook/template_parser.rb +0 -72
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 746247239c9d22b8537a0327dfa40e3d4a2b687a9d48e0a4b61819cba7766bec
4
- data.tar.gz: c288e6e752520bb2b4b90b5e1f682cd68c99961939496b5d698abf2604962efc
3
+ metadata.gz: d6235a52e2104d528b7c3d4a56fee91f3db334ef9ce3d9da43cc11e50792cf77
4
+ data.tar.gz: 5bd16de7e7dbe99e7552efb07d719230bd1c108d13340d4fcb790dd8e034ef1e
5
5
  SHA512:
6
- metadata.gz: 6b0aae039cf5d29f2d9f93aca8f2fd62996ecbce1ad9cc9ab8c8b19e82343f71a3dc6694406182e5ebd41b0a6ad97c160a7c156e51463cda51f522b186a25a1d
7
- data.tar.gz: cf44c51bfa10e5eabc5dc6c88931d3e1676e5b72599e00304c83aca343d6a080ef31afa0044e39383d1c4b4ccc179260832afb5951d5427858767b09ff749a4a
6
+ metadata.gz: 990f07de7b0ab03d9e9577d3c84768542ebc2af7e5d587fc85f95532ade53f88abc34b2f371a776a0f48b2a3c0db5a1be606e6a42653f57e92ae19e6735ac81c
7
+ data.tar.gz: a2e17e85420de7603fdd3277eebd473b3bcff413f9d8507b38600290d6d35c081a034c4f70c14d8e3be9075b6b22caade8d2d6610ab3d6a2fd5e863bf1555bc8
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021 allmarkedup
3
+ Copyright (c) 2021 Mark Perkins
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,30 +1,42 @@
1
- # Lookbook
2
-
3
- <div>
4
- <a href="https://rubygems.org/gems/lookbook"><img src="https://img.shields.io/gem/v/lookbook" alt="Gem version"></a>
5
- <a href="https://github.com/allmarkedup/lookbook/actions/workflows/ci.yml"><img src="https://github.com/allmarkedup/lookbook/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status"></a>
6
- </div>
7
1
  <br>
2
+ <img src=".github/assets/lookbook_logo.svg" width="180">
8
3
 
9
4
  A tool to help browse, develop, test & document [ViewComponents](https://viewcomponent.org/) in Ruby on Rails apps.
10
5
 
6
+ [![Gem version](https://img.shields.io/gem/v/lookbook)](https://rubygems.org/gems/lookbook)
7
+ [![CI status](https://github.com/allmarkedup/lookbook/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/allmarkedup/lookbook/actions/workflows/ci.yml)
8
+ <br>
9
+
11
10
  ## Documentation
12
11
 
13
- **Lookbook (v1.x)** documentation: **[https://lookbook.build](https://lookbook.build)**
12
+ **Lookbook guide and API docs**: [lookbook.build](https://lookbook.build)
14
13
 
15
- > _Looking for v0.9.x docs? [Head over here](https://github.com/allmarkedup/lookbook/tree/0.9.x)._
14
+ > _Looking for pre-v1.0 documentation? [Head over here](https://github.com/allmarkedup/lookbook/tree/0.9.x)._
16
15
 
17
16
 
18
17
  ## Demo
19
18
 
20
- **Online Demo:** [https://lookbook-demo-app.herokuapp.com/lookbook](https://lookbook-demo-app.herokuapp.com/lookbook)
19
+ **Online Demo**: [lookbook-demo-app.herokuapp.com/lookbook](https://lookbook-demo-app.herokuapp.com/lookbook)
20
+
21
+ ✨ **Demo repo**: [github.com/allmarkedup/lookbook-demo](https://github.com/allmarkedup/lookbook-demo)
21
22
 
22
23
  [![Lookbook UI](.github/assets/lookbook_screenshot_v1.0_beta.png)](https://lookbook-demo-app.herokuapp.com/lookbook/)
23
24
 
24
25
 
25
26
  ## Development
26
27
 
27
- Lookbook's UI is itself built using ViewComponents. To preview these components in a Lookbook instance you can run the included `workbench` app:
28
+ Lookbook is implemented as an isolated [Rails Engine](https://guides.rubyonrails.org/engines.html) and uses [ViewComponent](https://viewcomponent.org), [Tailwind](https://tailwindcss.com/) and [Alpine](https://alpinejs.dev/) for it's UI.
29
+
30
+ This repository contains:
31
+
32
+ * The Lookbook source code ([`/app`](https://github.com/allmarkedup/lookbook/tree/main/app), [`/lib`](https://github.com/allmarkedup/lookbook/tree/main/lib), [`/config`](https://github.com/allmarkedup/lookbook/tree/main/config), etc)
33
+ * A '[workbench](#workbench)' app used for Lookbook component development ([`/workbench`](https://github.com/allmarkedup/lookbook/tree/main/workbench)).
34
+ * The Lookbook [documentation site](#docs-site) source code and content ([`/docs`](https://github.com/allmarkedup/lookbook/tree/main/docs)).
35
+ * A [test suite](#testing) with a 'runable' dummy app ([`/spec`](https://github.com/allmarkedup/lookbook/tree/main/spec)).
36
+
37
+ ### Workbench
38
+
39
+ To preview the Lookbook components within a Lookbook instance you can run the included `workbench` app:
28
40
 
29
41
  1. Clone this repo
30
42
  2. Install dependencies: `bundle install & npm install`
@@ -33,21 +45,42 @@ Lookbook's UI is itself built using ViewComponents. To preview these components
33
45
 
34
46
  The `workbench` app will be started in development mode and any changes to Lookbook's views or assets will immediately be reflected in the UI.
35
47
 
36
- ### Docs site
48
+ ### Documentation site
37
49
 
38
50
  The [Lookbook docs site](https://lookbook.build) is built using [Bridgetown](https://www.bridgetownrb.com/) and the source files can be found in the `./docs` directory.
39
51
 
40
- To see a local version of the site run `bin/docs` from the root of this repo and then visit http://localhost:4000 in your browser.
52
+ To preview changes locally you can run a development version of the docs site:
53
+
54
+ 1. Clone this repo
55
+ 2. Install dependencies: `bundle install`
56
+ 3. Start the app: `bin/docs`
57
+ 4. Visit http://localhost:4000
41
58
 
42
59
  ### Testing
43
60
 
44
- Lookbook uses RSpec for testing.
61
+ Lookbook uses [RSpec](https://relishapp.com/rspec) for testing.
45
62
 
46
63
  Tests can be run using the `rake spec` or `bundle exec rspec` commands.
47
64
 
48
65
  The dummy app that the tests are being run against can be viewed by running the `bin/dummy` command and then browsing to http://localhost:9292/lookbook
49
66
 
50
67
 
68
+ ## Contributing
69
+
70
+ Lookbook is an un-funded open source project and contributions of all types and sizes are most welcome!
71
+
72
+ Please take the time to read over the [Contributing](./CONTRIBUTING.md) guide before making your first contribution and if anything isn't clear then [start a discussion](https://github.com/allmarkedup/lookbook/discussions) and we will do our best to help you out.
73
+
74
+ ## Contributors
75
+
76
+ Lookbook was created by [Mark Perkins](https://github.com/allmarkedup) and continues to grow
77
+ &amp; improve thanks to the ideas, suggestions and hard work of all of [these excellent humans](https://github.com/allmarkedup/lookbook/graphs/contributors):
78
+ <br>
79
+ <br>
80
+ <a href="https://github.com/allmarkedup/lookbook/graphs/contributors">
81
+ <img src="https://contrib.rocks/image?repo=allmarkedup/lookbook&columns=14" width="800" />
82
+ </a>
83
+
51
84
  ## License
52
85
 
53
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
86
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -6,5 +6,5 @@
6
6
  "h-full": full_height?
7
7
  }
8
8
  ] do %>
9
- <%= Lookbook::CodeFormatter.highlight(source, **@highlight_opts) %>
9
+ <%= Lookbook::CodeHighlighter.call(source, **@highlight_opts) %>
10
10
  <% end %>
@@ -1,5 +1,3 @@
1
- require "css_parser"
2
-
3
1
  module Lookbook
4
2
  class InspectorPanel::Component < Lookbook::BaseComponent
5
3
  attr_reader :panel_styles, :panel_html
@@ -14,9 +12,9 @@ module Lookbook
14
12
  end
15
13
 
16
14
  def before_render
17
- tpl = TemplateParser.new(content)
18
- @panel_styles = tpl.styles.map { |s| "##{id} #{s}" }.join("\n")
19
- @panel_html = tpl.content
15
+ styles, html = StylesExtractor.call(content)
16
+ @panel_styles = styles.map { |s| "##{id} #{s}" }.join("\n")
17
+ @panel_html = html.html_safe
20
18
  end
21
19
  end
22
20
  end
@@ -6,7 +6,7 @@
6
6
  "entity-type": item.type
7
7
  },
8
8
  cloak: true do %>
9
- <%= lookbook_tag href.present? ? :a : :div,
9
+ <%= lookbook_tag href.present? ? :a : :button,
10
10
  href: href,
11
11
  class: "flex items-center py-1 select-none cursor-pointer text-lookbook-nav-text hover:bg-lookbook-nav-item-hover transition",
12
12
  style: "padding-left: #{left_pad}px",
@@ -1,20 +1,13 @@
1
1
  module Lookbook
2
2
  module Params
3
3
  class Editor::Component < Lookbook::BaseComponent
4
- renders_many :fields, ->(input:, description: nil, **attrs) do
4
+ renders_many :fields, ->(**attrs) do
5
5
  @field_count += 1
6
- @descriptions = true if description.present?
7
- input_config = @inputs[input.tr("-", "_").to_sym]
8
- Lookbook::Params::Field::Component.new(input: input,
9
- description: description,
10
- index: @field_count,
11
- config: input_config, **attrs)
6
+ Lookbook::Params::Field::Component.new(**attrs, index: @field_count)
12
7
  end
13
8
 
14
- def initialize(inputs: nil, **html_attrs)
15
- @inputs = inputs.to_h
9
+ def initialize(**html_attrs)
16
10
  @field_count = -1
17
- @descriptions = false
18
11
  @@input_styles = {}
19
12
  super(**html_attrs)
20
13
  end
@@ -1,27 +1,27 @@
1
1
  <%= render_component_tag :tr, "@keydown.stop": true do %>
2
2
  <td class="param-label">
3
- <label for="param-<%= @name %>">
4
- <span class="mr-0.5"><%== @label %></span>
5
- <% if hint? %>
3
+ <label for="param-<%= param.name %>">
4
+ <span class="mr-0.5"><%== param.label %></span>
5
+ <% if param.hint.present? %>
6
6
  <span x-data="tooltipComponent" class="inline-block cursor-help relative top-[2px]">
7
7
  <%= icon :help_circle, size: 3.5, class: "opacity-40 hover:opacity-100 transition" %>
8
8
  <div class="hidden" x-ref="tooltip">
9
- <%= @hint %>
9
+ <%= param.hint %>
10
10
  </div>
11
11
  </span>
12
12
  <% end %>
13
13
  </label>
14
14
  </td>
15
- <td class="param-description <%= "param-description-empty" unless description? %>">
16
- <% if description? %>
17
- <p class="opacity-70"><%= @description %></p>
15
+ <td class="param-description <%= "param-description-empty" unless param.description %>">
16
+ <% if param.description %>
17
+ <p class="opacity-70"><%= param.description %></p>
18
18
  <% else %>
19
19
  <p class="italic opacity-40">&mdash;</p>
20
20
  <% end %>
21
21
  </td>
22
22
  <td class="param-input">
23
23
  <div class="param-input-wrapper">
24
- <%= input %>
24
+ <%= rendered_input %>
25
25
  </div>
26
26
  </td>
27
27
  <% end %>
@@ -1,92 +1,41 @@
1
1
  module Lookbook
2
2
  module Params
3
3
  class Field::Component < Lookbook::BaseComponent
4
- def initialize(name:, input:, index:, label: nil, hint: nil, description: nil, value: nil, value_default: nil, value_type: nil, input_options: {}, config: nil, **html_attrs)
5
- @input_name = input
6
- @name = name
7
- @label = label || name.titleize
8
- @hint = hint
9
- @description = description
10
- @value = value
4
+ attr_reader :param, :rendered_input
5
+
6
+ def initialize(param:, index:, **html_attrs)
7
+ @param = param
11
8
  @index = index
12
- @input_options = input_options
13
- @value_default = value_default
14
- @value_type = value_type
15
- @config = config || {}
16
9
  @rendered_input = nil
17
10
  super(**html_attrs)
18
11
  end
19
12
 
20
- def hint?
21
- @hint.present?
22
- end
23
-
24
- def description?
25
- @description.present?
26
- end
27
-
28
- def input
29
- @rendered_input
30
- end
31
-
32
13
  def before_render
33
- tpl = TemplateParser.new(render_input)
34
- Editor::Component.add_styles(@input_name, tpl.styles)
14
+ styles, html = StylesExtractor.call(render_input)
15
+ Editor::Component.add_styles(param.input, styles)
35
16
 
17
+ escaped_value = json_escape(param.value.to_json)
36
18
  wrapper_attrs = {
37
- data: {"param-input": @input_name},
38
- "x-data": "paramsInputComponent({name: '#{@name}', value: #{escaped_value}})"
19
+ data: {"param-input": param.input},
20
+ "x-data": "paramsInputComponent({name: '#{param.name}', value: #{escaped_value}})"
39
21
  }
40
-
41
- @rendered_input = tag.div(**wrapper_attrs) do
42
- tpl.content
43
- end
22
+ @rendered_input = tag.div(**wrapper_attrs) { html.html_safe }
44
23
  end
45
24
 
46
25
  protected
47
26
 
48
- def input_error(error)
49
- tag.div error, class: "p-2 text-red-500 italic"
50
- end
51
-
52
- def value
53
- val = @value.presence || @value_default
54
- @value_type.downcase == "boolean" ? val == "true" || val == true : val
55
- end
56
-
57
- def escaped_value
58
- json_escape(value.to_json)
59
- end
60
-
61
- def input_options
62
- config_options = @config.fetch(:opts, {})
63
- opts = config_options.merge(@input_options).symbolize_keys
64
- opts[:id] = "param-#{@name}"
65
- opts
66
- end
67
-
68
- def render_props
69
- {
70
- name: @name,
71
- input: @input_name,
72
- value: value,
73
- value_type: @value_type,
74
- value_default: @value_default,
75
- input_options: input_options.except(:choices),
76
- choices: input_options[:choices]
77
- }
78
- end
79
-
80
27
  def render_input
81
- target = @config[:partial]
82
- if target
83
- render(target, **render_props)
84
- else
85
- input_error "No param input defined for input type '#{@input_name}'."
86
- end
87
- rescue ::ActionView::MissingTemplate => exception
88
- Lookbook.logger.error exception
89
- input_error "Param input partial '#{@config[:partial]}' could not be found."
28
+ input_options = param.input_options.to_h
29
+ input_options[:id] = "param-#{param.name}"
30
+
31
+ render(param.input_partial,
32
+ name: param.name,
33
+ input: param.input,
34
+ value: param.value,
35
+ value_type: param.value_type,
36
+ value_default: param.value_default,
37
+ input_options: input_options.except(:choices),
38
+ choices: input_options[:choices])
90
39
  end
91
40
 
92
41
  def alpine_component
@@ -0,0 +1,156 @@
1
+ module Lookbook
2
+ module TargetableConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :lookup_entities, only: [:show]
7
+ before_action :set_title
8
+ before_action :set_display_options
9
+ before_action :set_params
10
+ end
11
+
12
+ def set_title
13
+ @title = @target.present? ? [@target&.label, @preview&.label].compact.join(" :: ") : "Not found"
14
+ end
15
+
16
+ def lookup_entities
17
+ @target = Lookbook.previews.find_example(params[:path])
18
+ if @target.present?
19
+ @preview = @target.preview
20
+ if params[:path] == @preview&.lookup_path
21
+ redirect_to lookbook_inspect_path "#{params[:path]}/#{@preview.default_example.name}"
22
+ end
23
+ else
24
+ @preview = Lookbook.previews.find(params[:path])
25
+ if @preview.present?
26
+ first_example = @preview.examples.first
27
+ redirect_to lookbook_inspect_path(first_example.lookup_path) if first_example
28
+ else
29
+ @preview = Lookbook.previews.find(path_segments.slice(0, path_segments.size - 1).join("/"))
30
+ end
31
+ end
32
+ end
33
+
34
+ def set_display_options
35
+ @dynamic_display_options = []
36
+ @static_display_options = []
37
+
38
+ if @target.present?
39
+ opts = @target.display_options
40
+ @dynamic_display_options = opts.select { _2.is_a?(Array) || _2.is_a?(Hash) }
41
+ @static_display_options = opts.except(*@dynamic_display_options.keys)
42
+
43
+ if params[:_display]
44
+ display_params = SearchParamParser.call(params[:_display])
45
+ display_params.each do |name, value|
46
+ if @dynamic_display_options.key?(name)
47
+ cookies["lookbook-display-#{name}"] = value
48
+ end
49
+ end
50
+ end
51
+
52
+ @dynamic_display_options.each do |name, opts|
53
+ choices = opts.is_a?(Hash) ? opts[:choices].to_a : opts
54
+ @static_display_options[name] ||= cookies.fetch("lookbook-display-#{name}", choices.first)
55
+ end
56
+
57
+ unless params[:_display]
58
+ display_params = @dynamic_display_options.map do |name, opts|
59
+ [name, @static_display_options[name]]
60
+ end.to_h
61
+ request.query_parameters[:_display] = SearchParamBuilder.call(display_params)
62
+ end
63
+ end
64
+ end
65
+
66
+ def set_params
67
+ @params = []
68
+
69
+ if @target
70
+ @params = @target.tags("param").map do |param_tag|
71
+ Param.from_tag(
72
+ param_tag,
73
+ value: preview_controller.params[param_tag.name]
74
+ )
75
+ end
76
+
77
+ # cast known param values to correct type
78
+ @params.each do |param|
79
+ if preview_controller.params.key?(param.name)
80
+ preview_controller.params[param.name] = param.cast_value
81
+ end
82
+ end
83
+
84
+ # set display and data params for use in preview layouts
85
+ preview_controller.params[:lookbook] = {
86
+ display: @static_display_options,
87
+ data: Lookbook.data
88
+ }
89
+ end
90
+
91
+ preview_controller.params.permit!
92
+ @preview_params = preview_controller.params.to_h.select do |key, value|
93
+ !!@params.find { |param_tag| param_tag.name == key.to_s }
94
+ end
95
+ end
96
+
97
+ def inspector_data
98
+ return @inspector_data if @inspector_data.present?
99
+
100
+ context_data = {
101
+ preview_params: @preview_params,
102
+ path: params[:path]
103
+ }
104
+
105
+ preview = @preview
106
+ target_examples = @target.type == :group ? @target.examples : [@target]
107
+
108
+ examples = target_examples.map do |example|
109
+ render_args = @preview.render_args(example.name, params: preview_controller.params)
110
+ has_template = render_args[:template] != "view_components/preview"
111
+ output = preview_controller.process(:render_example_to_string, @preview, example.name)
112
+ source = has_template ? example.template_source(render_args[:template]) : example.method_source
113
+ source_lang = has_template ? example.template_lang(render_args[:template]) : example.lang
114
+
115
+ example.define_singleton_method(:output, proc { output })
116
+ example.define_singleton_method(:source, proc { source })
117
+ example.define_singleton_method(:source_lang, proc { source_lang })
118
+ example
119
+ end
120
+
121
+ target = @target.type == :group ? @target : examples.find { |e| e.lookup_path == @target.lookup_path }
122
+
123
+ params_ref = @params
124
+ preview.define_singleton_method(:params, proc { params_ref })
125
+
126
+ @inspector_data ||= Lookbook::Store.new({
127
+ context: context_data,
128
+ preview: preview,
129
+ examples: examples,
130
+ example: examples.first,
131
+ target: target,
132
+ data: Lookbook.data,
133
+ app: Lookbook
134
+ })
135
+ end
136
+
137
+ def show_404(layout: nil)
138
+ locals = if @preview
139
+ {
140
+ message: "Example not found",
141
+ description: "The '#{@preview.label}' preview does not have an example named '#{path_segments.last}'."
142
+ }
143
+ else
144
+ {
145
+ message: "Not found",
146
+ description: "Looked for '#{params[:path]}'.<br>The preview may have been renamed or deleted."
147
+ }
148
+ end
149
+ render_in_layout "lookbook/404", layout: layout, **locals
150
+ end
151
+
152
+ def path_segments
153
+ params[:path].split("/")
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,13 @@
1
+ module Lookbook
2
+ module WithPreviewControllerConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ def preview_controller
6
+ return @preview_controller if @preview_controller
7
+ controller = Lookbook::Engine.preview_controller.new
8
+ controller.request = request
9
+ controller.response = response
10
+ @preview_controller ||= controller
11
+ end
12
+ end
13
+ end
@@ -1,8 +1,6 @@
1
1
  module Lookbook
2
2
  class ApplicationController < ActionController::Base
3
- if respond_to?(:content_security_policy)
4
- content_security_policy false, if: -> { Rails.env.development? }
5
- end
3
+ content_security_policy(false) if respond_to?(:content_security_policy)
6
4
 
7
5
  protect_from_forgery with: :exception
8
6
 
@@ -45,5 +43,17 @@ module Lookbook
45
43
  @error = locals[:error]
46
44
  render path, layout: layout.presence || (params[:lookbook_embed] ? "lookbook/basic" : "lookbook/application"), locals: locals
47
45
  end
46
+
47
+ def prettify_error(exception)
48
+ error_params = {}
49
+ if exception.is_a?(ViewComponent::PreviewTemplateError)
50
+ error_params = {
51
+ file_path: @preview&.full_path,
52
+ line_number: 0,
53
+ source_code: @target&.source
54
+ }
55
+ end
56
+ Lookbook::Error.new(exception, **error_params)
57
+ end
48
58
  end
49
59
  end
@@ -0,0 +1,45 @@
1
+ module Lookbook
2
+ class InspectorController < ApplicationController
3
+ include TargetableConcern
4
+ include WithPreviewControllerConcern
5
+
6
+ layout "lookbook/inspector"
7
+ helper Lookbook::PreviewHelper
8
+
9
+ def self.controller_path
10
+ "lookbook/inspector"
11
+ end
12
+
13
+ def show
14
+ if @target
15
+ begin
16
+ @main_panels = main_panels
17
+ @drawer_panels = drawer_panels
18
+ rescue => exception
19
+ render_in_layout "lookbook/error", layout: "lookbook/inspector", error: prettify_error(exception)
20
+ end
21
+ else
22
+ show_404
23
+ end
24
+ end
25
+
26
+ def show_legacy
27
+ Lookbook.logger.warn("Legacy URL path detected. These paths are deprecated and will be removed in a future version")
28
+ redirect_to lookbook_inspect_path params[:path]
29
+ end
30
+
31
+ private
32
+
33
+ def main_panels
34
+ Engine.panels.in_group(:main).map do |config|
35
+ PanelStore.resolve_config(config, inspector_data)
36
+ end
37
+ end
38
+
39
+ def drawer_panels
40
+ Engine.panels.in_group(:drawer).map do |config|
41
+ PanelStore.resolve_config(config, inspector_data)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -13,13 +13,17 @@ module Lookbook
13
13
  @pages = Lookbook.pages
14
14
  @next_page = @pages.find_next(@page)
15
15
  @previous_page = @pages.find_previous(@page)
16
- content = render_to_string inline: @page.content, locals: {
17
- page: @page,
18
- next_page: @next_page,
19
- previous_page: @previous_page,
20
- pages: @pages
21
- }
22
- @page.markdown? ? Lookbook::Markdown.render(content) : content
16
+
17
+ content = ActionViewAnnotationsHandler.call(disable_annotations: true) do
18
+ render_to_string inline: @page.content, locals: {
19
+ page: @page,
20
+ next_page: @next_page,
21
+ previous_page: @previous_page,
22
+ pages: @pages
23
+ }
24
+ end
25
+
26
+ @page.markdown? ? MarkdownRenderer.call(content, Lookbook.config.markdown_options) : content
23
27
  end
24
28
  end
25
29
  end