1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
# frozen_string_literal: true
require "active_support/descendants_tracker"
module ViewComponent # :nodoc:
class Preview
include ActionView::Helpers::TagHelper
include ActionView::Helpers::AssetTagHelper
extend ActiveSupport::DescendantsTracker
def render(component, **args, &block)
{
args: args,
block: block,
component: component,
locals: {},
template: "view_components/preview"
}
end
def render_with_template(template: nil, locals: {})
{
template: template,
locals: locals
}
end
alias_method :render_component, :render
class << self
# Returns all component preview classes.
def all
load_previews
descendants
end
# Returns the arguments for rendering of the component in its layout
def render_args(example, params: {})
example_params_names = instance_method(example).parameters.map(&:last)
provided_params = params.slice(*example_params_names).to_h.symbolize_keys
result = provided_params.empty? ? new.public_send(example) : new.public_send(example, **provided_params)
result ||= {}
result[:template] = preview_example_template_path(example) if result[:template].nil?
@layout = nil unless defined?(@layout)
result.merge(layout: @layout)
end
# Returns all of the available examples for the component preview.
def examples
public_instance_methods(false).map(&:to_s).sort
end
# Returns +true+ if the preview exists.
def exists?(preview)
all.any? { |p| p.preview_name == preview }
end
# Find a component preview by its underscored class name.
def find(preview)
all.find { |p| p.preview_name == preview }
end
# Returns the underscored name of the component preview without the suffix.
def preview_name
name.chomp("Preview").underscore
end
# rubocop:disable Style/TrivialAccessors
# Setter for layout name.
def layout(layout_name)
@layout = layout_name
end
# rubocop:enable Style/TrivialAccessors
# Returns the relative path (from preview_path) to the preview example template if the template exists
def preview_example_template_path(example)
preview_path =
Array(preview_paths).detect do |path|
Dir["#{path}/#{preview_name}_preview/#{example}.html.*"].first
end
if preview_path.nil?
raise(
PreviewTemplateError,
"A preview template for example #{example} doesn't exist.\n\n" \
"To fix this issue, create a template for the example."
)
end
path = Dir["#{preview_path}/#{preview_name}_preview/#{example}.html.*"].first
Pathname.new(path)
.relative_path_from(Pathname.new(preview_path))
.to_s
.sub(/\..*$/, "")
end
# Returns the method body for the example from the preview file.
def preview_source(example)
source = instance_method(example.to_sym).source.split("\n")
source[1...(source.size - 1)].join("\n")
end
def load_previews
Array(preview_paths).each do |preview_path|
Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
end
end
private
def preview_paths
Base.preview_paths
end
end
end
end
|