From 1514d60328af6c1f0bb4905b8a685de39910c74f Mon Sep 17 00:00:00 2001 From: Joel Warrington Date: Thu, 4 Apr 2024 21:04:09 -0600 Subject: [PATCH 1/6] Add gem configuration, with base component customization --- .../view_component/form/base_component.rb | 2 +- lib/view_component/form.rb | 10 ++++++++++ lib/view_component/form/configuration.rb | 13 +++++++++++++ .../app/components/application_form_component.rb | 4 ++++ .../config/initializers/view_component_form.rb | 5 +++++ spec/view_component/form/base_component_spec.rb | 8 ++++++++ spec/view_component/form/builder_spec.rb | 8 ++++++++ spec/view_component/form/configuration_spec.rb | 11 +++++++++++ spec/view_component/form_spec.rb | 4 ++++ 9 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 lib/view_component/form/configuration.rb create mode 100644 spec/internal/app/components/application_form_component.rb create mode 100644 spec/internal/config/initializers/view_component_form.rb create mode 100644 spec/view_component/form/configuration_spec.rb diff --git a/app/components/view_component/form/base_component.rb b/app/components/view_component/form/base_component.rb index b2206141..e2ad2961 100644 --- a/app/components/view_component/form/base_component.rb +++ b/app/components/view_component/form/base_component.rb @@ -2,7 +2,7 @@ module ViewComponent module Form - class BaseComponent < ViewComponent::Base + class BaseComponent < ViewComponent::Form.configuration.parent_component.constantize class << self attr_accessor :default_options end diff --git a/lib/view_component/form.rb b/lib/view_component/form.rb index 438d26b9..afe85ba3 100644 --- a/lib/view_component/form.rb +++ b/lib/view_component/form.rb @@ -1,10 +1,20 @@ # frozen_string_literal: true require "view_component" +require_relative "form/configuration" require "zeitwerk" module ViewComponent module Form + class << self + def configuration + @configuration ||= Configuration.new + end + + def configure + yield configuration + end + end end end diff --git a/lib/view_component/form/configuration.rb b/lib/view_component/form/configuration.rb new file mode 100644 index 00000000..9f9d0084 --- /dev/null +++ b/lib/view_component/form/configuration.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module ViewComponent + module Form + class Configuration + attr_accessor :parent_component + + def initialize + @parent_component = "ViewComponent::Base" + end + end + end +end diff --git a/spec/internal/app/components/application_form_component.rb b/spec/internal/app/components/application_form_component.rb new file mode 100644 index 00000000..b6ded24d --- /dev/null +++ b/spec/internal/app/components/application_form_component.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class ApplicationFormComponent < ViewComponent::Base +end diff --git a/spec/internal/config/initializers/view_component_form.rb b/spec/internal/config/initializers/view_component_form.rb new file mode 100644 index 00000000..36203470 --- /dev/null +++ b/spec/internal/config/initializers/view_component_form.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +ViewComponent::Form.configure do |config| + config.parent_component = "ApplicationFormComponent" +end diff --git a/spec/view_component/form/base_component_spec.rb b/spec/view_component/form/base_component_spec.rb index afb75929..c90de8fa 100644 --- a/spec/view_component/form/base_component_spec.rb +++ b/spec/view_component/form/base_component_spec.rb @@ -46,4 +46,12 @@ def name it { expect(component.object_errors?).to be(false) } end end + + describe "parent_component" do + subject { described_class } + + context "without configured parent_component" do + it { is_expected.to be < ViewComponent::Base } + end + end end diff --git a/spec/view_component/form/builder_spec.rb b/spec/view_component/form/builder_spec.rb index 3d1c2f6f..a912de81 100644 --- a/spec/view_component/form/builder_spec.rb +++ b/spec/view_component/form/builder_spec.rb @@ -192,4 +192,12 @@ it { expect(builder.send(:validation_context)).to eq(:create) } end end + + describe "base component parent" do + subject(:field) { described_class.new(object_name, object, template, options).send(:component_klass, :text_field) } + + it "is configured via initializer" do + expect(field.ancestors).to include(ApplicationFormComponent) + end + end end diff --git a/spec/view_component/form/configuration_spec.rb b/spec/view_component/form/configuration_spec.rb new file mode 100644 index 00000000..fb4b0387 --- /dev/null +++ b/spec/view_component/form/configuration_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +RSpec.describe ViewComponent::Form::Configuration do + subject(:configuration) { described_class.new } + + describe "defaults" do + it do + expect(configuration).to have_attributes(parent_component: "ViewComponent::Base") + end + end +end diff --git a/spec/view_component/form_spec.rb b/spec/view_component/form_spec.rb index 8ba29782..3a48a788 100644 --- a/spec/view_component/form_spec.rb +++ b/spec/view_component/form_spec.rb @@ -5,6 +5,10 @@ expect(ViewComponent::Form::VERSION).not_to be_nil end + it "is configurable" do + expect { |block| described_class.configure(&block) }.to yield_with_args(ViewComponent::Form::Configuration) + end + if ENV.fetch("VIEW_COMPONENT_FORM_USE_ACTIONTEXT", "false") == "true" it "loads ActionText" do expect(defined?(ActionView::Helpers::Tags::ActionText)).to eq("constant") From c9ba9cc624e337dbcb7b865f4d9f412311199a49 Mon Sep 17 00:00:00 2001 From: Joel Warrington Date: Fri, 5 Apr 2024 01:31:22 -0600 Subject: [PATCH 2/6] Add documentation for parent_component configuration --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 6c44807b..a5345644 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,12 @@ You can use the same approach to inject options, wrap the input in a `
`, et We'll add more use cases to the documentation soon. +### Configuration + +| Attribute | Purpose | Default | +| ------------------ | ----------------------------------------------- | --------------------- | +| `parent_component` | Inherited by all ViewComponent::Form components | `ViewComponent::Base` | + ### Building your own components When building your own ViewComponents for using in forms, it's recommended to inherit from `ViewComponent::Form::FieldComponent`, so you get access to the following helpers: @@ -339,6 +345,33 @@ def length_validator end ``` +### Setting up your own base component class + +1. Setup some base component from which the form components will inherit from +```rb +class ApplicationFormComponent < ViewComponent::Base + include Heroicon::Engine.helpers + + alias_method :icon, :heroicon + + def html_class + class_names( + "block w-full rounded-md border-0 py-1.5 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6", + "text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-indigo-600", + "pr-10 text-red-900 ring-red-300 placeholder:text-red-300 focus:ring-red-500": method_errors?, + ) + end +end +``` +2. Configure the parent component class +```rb +# config/initializers/vcf.rb + +ViewComponent::Form.configure do |config| + config.parent_component = 'ApplicationFormComponent' +end +``` + ### Using your form components without a backing model If you want to ensure that your fields display consistently across your app, you'll need to lean on Rails' own helpers. You may be used to using form tag helpers such as `text_field_tag` to generate tags, or even writing out plain HTML tags. These can't be integrated with a form builder, so they won't offer you the benefits of this gem. From d1e420cbbc1fefb48271741ea4b0b80bacf5e261 Mon Sep 17 00:00:00 2001 From: Joel Warrington Date: Fri, 5 Apr 2024 01:34:03 -0600 Subject: [PATCH 3/6] Remove unnecessary relative require, Zeitwerk autoloads Configuration constant --- lib/view_component/form.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/view_component/form.rb b/lib/view_component/form.rb index afe85ba3..01030c59 100644 --- a/lib/view_component/form.rb +++ b/lib/view_component/form.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "view_component" -require_relative "form/configuration" require "zeitwerk" module ViewComponent From 4cd23410769d2b89e6146e23d6f908ea7a240e64 Mon Sep 17 00:00:00 2001 From: Joel Warrington Date: Fri, 5 Apr 2024 01:36:44 -0600 Subject: [PATCH 4/6] Remove unescessary implementation examples in configuration README example --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index a5345644..9c3c2c85 100644 --- a/README.md +++ b/README.md @@ -350,17 +350,6 @@ end 1. Setup some base component from which the form components will inherit from ```rb class ApplicationFormComponent < ViewComponent::Base - include Heroicon::Engine.helpers - - alias_method :icon, :heroicon - - def html_class - class_names( - "block w-full rounded-md border-0 py-1.5 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6", - "text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-indigo-600", - "pr-10 text-red-900 ring-red-300 placeholder:text-red-300 focus:ring-red-500": method_errors?, - ) - end end ``` 2. Configure the parent component class From bd086fc1038dc111d4c113302f15c0b63109d3dd Mon Sep 17 00:00:00 2001 From: Joel Warrington Date: Fri, 5 Apr 2024 10:09:31 -0600 Subject: [PATCH 5/6] Update parent_component configuration to be easier to grok Co-authored-by: Hans Lemuet --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c3c2c85..c69605ae 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ We'll add more use cases to the documentation soon. | Attribute | Purpose | Default | | ------------------ | ----------------------------------------------- | --------------------- | -| `parent_component` | Inherited by all ViewComponent::Form components | `ViewComponent::Base` | +| `parent_component` (string) | Parent class for all `ViewComponent::Form` components | `"ViewComponent::Base"` | ### Building your own components From 847829a5b1296344f647571dc5bd32d2d493fc8f Mon Sep 17 00:00:00 2001 From: Joel Warrington Date: Fri, 5 Apr 2024 11:08:56 -0600 Subject: [PATCH 6/6] Add changelog entry for #160 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac876d41..d5f066b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Added parent_component configuration for field components (#160) + ## [0.2.6] - 2023-10-11 ### Added - Support for Rails 7.1 (#151)