Skip to content

Commit

Permalink
Merge primer/rails_forms into primer/view_components (#1238)
Browse files Browse the repository at this point in the history
  • Loading branch information
camertron authored Aug 1, 2022
1 parent 3b2bec4 commit 204a8e4
Show file tree
Hide file tree
Showing 119 changed files with 2,899 additions and 51 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-zoos-hang.md
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
42 changes: 4 additions & 38 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,25 +58,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rails_version: [5.2.3, 6.1.1, main]
ruby_version: [2.6.x, 2.7.x, 3.0.x, 3.1.x]
exclude:
- rails_version: 5.2.3
ruby_version: 2.6.x
- rails_version: 5.2.3
ruby_version: 2.7.x
- rails_version: 5.2.3
ruby_version: 3.0.x
- rails_version: 5.2.3
ruby_version: 3.1.x
- rails_version: 6.1.1
ruby_version: 2.7.x
- rails_version: 6.1.1
ruby_version: 3.0.x
- rails_version: 6.1.1
ruby_version: 3.1.x
- rails_version: main
ruby_version: 2.6.x
rails_version: [6.1.1, 7.0.3, main]
ruby_version: [2.7.x, 3.0.x, 3.1.x]
steps:
- uses: actions/checkout@master
- name: Setup Ruby
Expand Down Expand Up @@ -123,25 +106,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rails_version: [5.2.3, 6.1.1, main]
ruby_version: [2.6.x, 2.7.x, 3.0.x, 3.1.x]
exclude:
- rails_version: 5.2.3
ruby_version: 2.6.x
- rails_version: 5.2.3
ruby_version: 2.7.x
- rails_version: 5.2.3
ruby_version: 3.0.x
- rails_version: 5.2.3
ruby_version: 3.1.x
- rails_version: 6.1.1
ruby_version: 2.7.x
- rails_version: 6.1.1
ruby_version: 3.0.x
- rails_version: 6.1.1
ruby_version: 3.1.x
- rails_version: main
ruby_version: 2.6.x
rails_version: [6.1.1, 7.0.3, main]
ruby_version: [2.7.x, 3.0.x, 3.1.x]
steps:
- uses: actions/checkout@master
- name: Setup Ruby
Expand Down
105 changes: 105 additions & 0 deletions app/components/primer/alpha/text_field.rb
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
3 changes: 2 additions & 1 deletion demo/config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@
config.primer_view_components.silence_deprecations = true
config.primer_view_components.raise_on_invalid_options = false

config.view_component.preview_paths << Rails.root.join("../test/previews")
config.autoload_paths << Rails.root.join("..", "test", "forms")
config.view_component.preview_paths << Rails.root.join("..", "test", "previews")
end
2 changes: 2 additions & 0 deletions docs/src/@primer/gatsby-theme-doctocat/nav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
url: "/components/alpha/tabpanels"
- title: Text
url: "/components/beta/text"
- title: TextField
url: "/components/alpha/textfield"
- title: TimeAgo
url: "/components/timeago"
- title: TimelineItem
Expand Down
36 changes: 36 additions & 0 deletions lib/primer/form_components.rb
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
118 changes: 118 additions & 0 deletions lib/primer/forms/acts_as_component.rb
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
8 changes: 8 additions & 0 deletions lib/primer/forms/base.html.erb
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 %>
Loading

0 comments on commit 204a8e4

Please sign in to comment.