-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge primer/rails_forms into primer/view_components (#1238)
- Loading branch information
Showing
119 changed files
with
2,899 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@primer/view-components': patch | ||
--- | ||
|
||
Merge primer/rails_forms into primer/view_components |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# frozen_string_literal: true | ||
|
||
module Primer | ||
module Alpha | ||
TextField = Primer::FormComponents.from_input(Primer::Forms::Dsl::TextFieldInput) | ||
|
||
# A text field suitable for use outside a form. For a text field input suitable for use | ||
# within an HTML form, see the `Primer::Forms` namespace. | ||
class TextField | ||
status :alpha | ||
|
||
# @!method initialize | ||
# | ||
# @example Default | ||
# <%= render(Primer::Alpha::TextField.new(name: :first_name, label: "First name")) %> | ||
# | ||
# @example With a clear button | ||
# <%= render( | ||
# Primer::Alpha::TextField.new( | ||
# name: :first_name, | ||
# label: "First name", | ||
# show_clear_button: true | ||
# ) | ||
# ) %> | ||
# | ||
# @example Full width | ||
# <%= render( | ||
# Primer::Alpha::TextField.new( | ||
# name: :first_name, | ||
# label: "First name", | ||
# full_width: true | ||
# ) | ||
# ) %> | ||
# | ||
# @example Disabled | ||
# <%= render( | ||
# Primer::Alpha::TextField.new( | ||
# name: :first_name, | ||
# label: "First name", | ||
# disabled: true | ||
# ) | ||
# ) %> | ||
# | ||
# @example Invalid | ||
# <%= render( | ||
# Primer::Alpha::TextField.new( | ||
# name: :first_name, | ||
# label: "First name", | ||
# invalid: true | ||
# ) | ||
# ) %> | ||
# | ||
# @example With a leading visual | ||
# <%= render( | ||
# Primer::Alpha::TextField.new( | ||
# name: :first_name, | ||
# label: "First name", | ||
# leading_visual: { | ||
# icon: :person | ||
# } | ||
# ) | ||
# ) %> | ||
# | ||
# @example With a caption | ||
# <%= render( | ||
# Primer::Alpha::TextField.new( | ||
# name: :first_name, | ||
# label: "First name", | ||
# caption: "What your friends call you" | ||
# ) | ||
# ) %> | ||
# | ||
# @example With a validation message | ||
# <%= render( | ||
# Primer::Alpha::TextField.new( | ||
# name: :first_name, | ||
# label: "First name", | ||
# validation_message: "Hmm, that doesn't look right" | ||
# ) | ||
# ) %> | ||
# | ||
# @param name [String] Value for the HTML name attribute. | ||
# @param id [String] Value for the HTML id attribute. | ||
# @param class [String] A list of CSS classes to add to the input. Exists for compatibility with Rails form builders. | ||
# @param classes [String] A list of CSS classes to add to the input. Combined with the `:class` argument. | ||
# @param caption [String] Caption text to render below the input. | ||
# @param label [String] Label text displayed above the input. | ||
# @param visually_hide_label [Boolean] Whether or not to visually hide the label. If `true` the label will be hidden visually but still available to screen readers. | ||
# @param size [Symbol] The size of the field. <%= one_of(Primer::Forms::Dsl::Input::SIZE_OPTIONS) %> | ||
# @param show_clear_button [Boolean] Whether or not to include a clear button inside the input that clears the input's contents when clicked. | ||
# @param clear_button_id [String] The HTML id attribute of the clear button. | ||
# @param full_width [Boolean] Controls whether or not the input takes the full width of its container. | ||
# @param disabled [Boolean] Whether or not the input should accept keyboard and mouse input. | ||
# @param invalid [Boolean] If `true`, renders the input in a visually invalid state. | ||
# @param placeholder [String] Placeholder text. | ||
# @param inset [Boolean] If `true`, renders the input in a visually inset state. | ||
# @param monospace [Boolean] If `true`, uses a monospace font for the input field. | ||
# @param leading_visual [Hash] Renders a leading visual icon before the text field's cursor. The hash will be passed to Primer's [Octicon component](https://primer.style/view-components/components/octicon). | ||
# @param validation_message [String] A validation message to display beneath the input. Implicitly sets `invalid` to `true`. | ||
# @param label_arguments [Hash] System arugments passed to the Rails builder's `#label` method. These arguments will appear as HTML attributes on the `<label>` tag. | ||
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %> | ||
# @param block [Proc] Unused. | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# frozen_string_literal: true | ||
|
||
module Primer | ||
# :nodoc: | ||
class FormComponents | ||
def self.from_input(input_klass) | ||
Class.new(Primer::Component) do | ||
@input_klass = input_klass | ||
|
||
class << self | ||
attr_reader :input_klass | ||
end | ||
|
||
def initialize(**system_arguments, &block) | ||
@system_arguments = system_arguments | ||
@block = block | ||
end | ||
|
||
def call | ||
builder = ActionView::Helpers::FormBuilder.new( | ||
nil, nil, __vc_original_view_context, {} | ||
) | ||
|
||
input = self.class.input_klass.new( | ||
builder: builder, | ||
form: nil, | ||
**@system_arguments, | ||
&@block | ||
) | ||
|
||
input.render_in(__vc_original_view_context) { content } | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# frozen_string_literal: true | ||
|
||
require "pathname" | ||
|
||
module Primer | ||
module Forms | ||
# :nodoc: | ||
module ActsAsComponent | ||
# :nodoc: | ||
module InstanceMethods | ||
delegate :render, :content_tag, :output_buffer, :capture, to: :@view_context | ||
|
||
def render_in(view_context, &block) | ||
@view_context = view_context | ||
before_render | ||
perform_render(&block) | ||
end | ||
|
||
# :nocov: | ||
def perform_render(&_block) | ||
raise NotImplementedError, "subclasses must implement ##{__method__}." | ||
end | ||
# :nocov: | ||
|
||
def before_render; end | ||
|
||
# :nocov: | ||
# rubocop:disable Naming/AccessorMethodName | ||
def set_original_view_context(view_context) | ||
@view_context = view_context | ||
end | ||
# rubocop:enable Naming/AccessorMethodName | ||
# :nocov: | ||
end | ||
|
||
def self.extended(base) | ||
base.include(InstanceMethods) | ||
end | ||
|
||
TemplateGlob = Struct.new(:glob_pattern, :method_name, :on_compile_callback) | ||
TemplateParams = Struct.new(:source, :identifier, :type, :format) | ||
|
||
attr_accessor :template_root_path | ||
|
||
def renders_templates(glob_pattern, method_name = nil, &block) | ||
template_globs << TemplateGlob.new(glob_pattern, method_name, block) | ||
end | ||
alias renders_template renders_templates | ||
|
||
def compile! | ||
# always recompile in dev | ||
return if defined?(@compiled) && @compiled && !Rails.env.development? | ||
|
||
template_globs.each do |template_glob| | ||
compile_templates_in(template_glob) | ||
end | ||
|
||
@compiled = true | ||
end | ||
|
||
private | ||
|
||
def template_globs | ||
@template_globs ||= [] | ||
end | ||
|
||
def compile_templates_in(template_glob) | ||
pattern = if Pathname(template_glob.glob_pattern).absolute? | ||
template_glob.glob_pattern | ||
else | ||
# skip compilation for anonymous form classes, as in tests | ||
return unless template_root_path | ||
|
||
File.join(template_root_path, template_glob.glob_pattern) | ||
end | ||
|
||
template_paths = Dir.glob(pattern) | ||
|
||
raise "Cannot compile multiple templates with the same method name." if template_paths.size > 1 && template_glob.method_name | ||
|
||
template_paths.each do |template_path| | ||
method_name = template_glob.method_name | ||
method_name ||= "render_#{File.basename(template_path).chomp('.html.erb')}" | ||
define_template_method(template_path, method_name) | ||
template_glob&.on_compile_callback&.call(template_path) | ||
end | ||
end | ||
|
||
def define_template_method(template_path, method_name) | ||
# rubocop:disable Style/DocumentDynamicEvalDefinition | ||
# rubocop:disable Style/EvalWithLocation | ||
class_eval <<-RUBY, template_path, 0 | ||
private def #{method_name} | ||
capture { #{compile_template(template_path)} } | ||
end | ||
RUBY | ||
# rubocop:enable Style/EvalWithLocation | ||
# rubocop:enable Style/DocumentDynamicEvalDefinition | ||
end | ||
|
||
def compile_template(path) | ||
handler = ActionView::Template.handler_for_extension("erb") | ||
template = File.read(path) | ||
template_params = TemplateParams.new({ | ||
source: template, | ||
identifier: __FILE__, | ||
type: "text/html", | ||
format: "text/html" | ||
}) | ||
|
||
# change @output_buffer ivar to output_buffer method call | ||
BufferRewriter.rewrite( | ||
handler.call(template_params, template) | ||
) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<%= render(SpacingWrapper.new) do %> | ||
<% inputs.each do |input| %> | ||
<%= render(input) %> | ||
<% end %> | ||
<% end %> | ||
<% if after_content? %> | ||
<%= render_after_content %> | ||
<% end %> |
Oops, something went wrong.