From c617ee4538efbdf8edd2901c651a48fbb1d2e368 Mon Sep 17 00:00:00 2001 From: Joel Warrington Date: Mon, 8 Apr 2024 14:01:45 -0600 Subject: [PATCH] Add gem configuration, with base component customization (#160) * Add gem configuration, with base component customization * Add documentation for parent_component configuration * Remove unnecessary relative require, Zeitwerk autoloads Configuration constant * Remove unescessary implementation examples in configuration README example * Update parent_component configuration to be easier to grok Co-authored-by: Hans Lemuet * Add changelog entry for #160 --------- Co-authored-by: Hans Lemuet --- CHANGELOG.md | 2 ++ README.md | 22 +++++++++++++++++++ .../view_component/form/base_component.rb | 2 +- lib/view_component/form.rb | 9 ++++++++ lib/view_component/form/configuration.rb | 13 +++++++++++ .../components/application_form_component.rb | 4 ++++ .../initializers/view_component_form.rb | 5 +++++ .../form/base_component_spec.rb | 8 +++++++ spec/view_component/form/builder_spec.rb | 8 +++++++ .../view_component/form/configuration_spec.rb | 11 ++++++++++ spec/view_component/form_spec.rb | 4 ++++ 11 files changed, 87 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/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) diff --git a/README.md b/README.md index 6c44807b..c69605ae 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` (string) | Parent class for 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,22 @@ 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 +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. 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..01030c59 100644 --- a/lib/view_component/form.rb +++ b/lib/view_component/form.rb @@ -5,6 +5,15 @@ 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")