diff --git a/app/components/blacklight/constraint_component.rb b/app/components/blacklight/constraint_component.rb
index de36d61116..3bde99b50f 100644
--- a/app/components/blacklight/constraint_component.rb
+++ b/app/components/blacklight/constraint_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class ConstraintComponent < ::ViewComponent::Base
+ class ConstraintComponent < Blacklight::Component
with_collection_parameter :facet_item_presenter
def initialize(facet_item_presenter:, classes: 'filter', layout: Blacklight::ConstraintLayoutComponent)
diff --git a/app/components/blacklight/constraint_layout_component.rb b/app/components/blacklight/constraint_layout_component.rb
index eb5b797ae5..f1e1387a56 100644
--- a/app/components/blacklight/constraint_layout_component.rb
+++ b/app/components/blacklight/constraint_layout_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class ConstraintLayoutComponent < ::ViewComponent::Base
+ class ConstraintLayoutComponent < Blacklight::Component
def initialize(value:, label: nil, remove_path: nil, classes: nil, search_state: nil)
@value = value
@label = label
diff --git a/app/components/blacklight/constraints_component.rb b/app/components/blacklight/constraints_component.rb
index b00747ed43..4b9c836430 100644
--- a/app/components/blacklight/constraints_component.rb
+++ b/app/components/blacklight/constraints_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class ConstraintsComponent < ::ViewComponent::Base
+ class ConstraintsComponent < Blacklight::Component
include Blacklight::ContentAreasShim
renders_many :query_constraints_area
diff --git a/app/components/blacklight/document/action_component.rb b/app/components/blacklight/document/action_component.rb
index fef81e2a0e..61ba315ba7 100644
--- a/app/components/blacklight/document/action_component.rb
+++ b/app/components/blacklight/document/action_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Document
# Render a bookmark widget to bookmark / unbookmark a document
- class ActionComponent < ::ViewComponent::Base
+ class ActionComponent < Blacklight::Component
with_collection_parameter :action
# @param [Blacklight::Document] document
diff --git a/app/components/blacklight/document/actions_component.rb b/app/components/blacklight/document/actions_component.rb
index 749e0140a4..791a4a56e8 100644
--- a/app/components/blacklight/document/actions_component.rb
+++ b/app/components/blacklight/document/actions_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Document
# Render a bookmark widget to bookmark / unbookmark a document
- class ActionsComponent < ::ViewComponent::Base
+ class ActionsComponent < Blacklight::Component
renders_many :actions, (lambda do |action:, component: nil, **kwargs|
component ||= action.component || Blacklight::Document::ActionComponent
component.new(action: action, document: @document, options: @options, url_opts: @url_opts, link_classes: @link_classes, **kwargs)
diff --git a/app/components/blacklight/document/bookmark_component.rb b/app/components/blacklight/document/bookmark_component.rb
index 410f9c3453..d901bbb407 100644
--- a/app/components/blacklight/document/bookmark_component.rb
+++ b/app/components/blacklight/document/bookmark_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Document
# Render a bookmark widget to bookmark / unbookmark a document
- class BookmarkComponent < ::ViewComponent::Base
+ class BookmarkComponent < Blacklight::Component
# @param [Blacklight::Document] document
# @param [Boolean] checked
# @param [Object] bookmark_path the rails route to use for bookmarks
diff --git a/app/components/blacklight/document/citation_component.rb b/app/components/blacklight/document/citation_component.rb
index fed84c9a64..173cd59613 100644
--- a/app/components/blacklight/document/citation_component.rb
+++ b/app/components/blacklight/document/citation_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Document
# Render citations for the document
- class CitationComponent < ::ViewComponent::Base
+ class CitationComponent < Blacklight::Component
DEFAULT_FORMATS = {
'blacklight.citation.mla': :export_as_mla_citation_txt,
'blacklight.citation.apa': :export_as_apa_citation_txt,
diff --git a/app/components/blacklight/document/group_component.rb b/app/components/blacklight/document/group_component.rb
index 734c70a285..8253f42014 100644
--- a/app/components/blacklight/document/group_component.rb
+++ b/app/components/blacklight/document/group_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Document
# Render the 'more like this' results from the response
- class GroupComponent < ::ViewComponent::Base
+ class GroupComponent < Blacklight::Component
with_collection_parameter :group
# @param [Blacklight::Solr::Response::Group] group
diff --git a/app/components/blacklight/document/more_like_this_component.rb b/app/components/blacklight/document/more_like_this_component.rb
index 0b388a3384..8bff9c122f 100644
--- a/app/components/blacklight/document/more_like_this_component.rb
+++ b/app/components/blacklight/document/more_like_this_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Document
# Render the 'more like this' results from the response
- class MoreLikeThisComponent < ::ViewComponent::Base
+ class MoreLikeThisComponent < Blacklight::Component
with_collection_parameter :document
# @param [Blacklight::Document] document
diff --git a/app/components/blacklight/document/thumbnail_component.rb b/app/components/blacklight/document/thumbnail_component.rb
index f7363e26cc..8809cb59b8 100644
--- a/app/components/blacklight/document/thumbnail_component.rb
+++ b/app/components/blacklight/document/thumbnail_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Document
# Render the thumbnail for the document
- class ThumbnailComponent < ::ViewComponent::Base
+ class ThumbnailComponent < Blacklight::Component
with_collection_parameter :presenter
# @param [Blacklight::DocumentPresenter] presenter
diff --git a/app/components/blacklight/document_component.rb b/app/components/blacklight/document_component.rb
index b0162266b0..a7241958b2 100644
--- a/app/components/blacklight/document_component.rb
+++ b/app/components/blacklight/document_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class DocumentComponent < ::ViewComponent::Base
+ class DocumentComponent < Blacklight::Component
include Blacklight::ContentAreasShim
# Content appearing before the document
diff --git a/app/components/blacklight/document_metadata_component.rb b/app/components/blacklight/document_metadata_component.rb
index 03378d27f5..2545dfdf3e 100644
--- a/app/components/blacklight/document_metadata_component.rb
+++ b/app/components/blacklight/document_metadata_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class DocumentMetadataComponent < ::ViewComponent::Base
+ class DocumentMetadataComponent < Blacklight::Component
renders_many :fields, (lambda do |component: nil, **kwargs|
(component || Blacklight::MetadataFieldComponent).new(**kwargs)
end)
diff --git a/app/components/blacklight/document_title_component.rb b/app/components/blacklight/document_title_component.rb
index 147e7ad093..077e068ce9 100644
--- a/app/components/blacklight/document_title_component.rb
+++ b/app/components/blacklight/document_title_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class DocumentTitleComponent < ::ViewComponent::Base
+ class DocumentTitleComponent < Blacklight::Component
renders_many :before_title
renders_many :after_title
renders_many :actions
diff --git a/app/components/blacklight/facet_field_checkboxes_component.rb b/app/components/blacklight/facet_field_checkboxes_component.rb
index 7210fd1f6d..168a8e4199 100644
--- a/app/components/blacklight/facet_field_checkboxes_component.rb
+++ b/app/components/blacklight/facet_field_checkboxes_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetFieldCheckboxesComponent < ::ViewComponent::Base
+ class FacetFieldCheckboxesComponent < Blacklight::Component
def initialize(facet_field:, layout: nil)
@facet_field = facet_field
@layout = layout == false ? FacetFieldNoLayoutComponent : Blacklight::FacetFieldComponent
diff --git a/app/components/blacklight/facet_field_component.rb b/app/components/blacklight/facet_field_component.rb
index a74c2af646..13756d3480 100644
--- a/app/components/blacklight/facet_field_component.rb
+++ b/app/components/blacklight/facet_field_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetFieldComponent < ::ViewComponent::Base
+ class FacetFieldComponent < Blacklight::Component
include Blacklight::ContentAreasShim
renders_one :label
diff --git a/app/components/blacklight/facet_field_filter_component.rb b/app/components/blacklight/facet_field_filter_component.rb
index 59324a1963..91abe91953 100644
--- a/app/components/blacklight/facet_field_filter_component.rb
+++ b/app/components/blacklight/facet_field_filter_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetFieldFilterComponent < ::ViewComponent::Base
+ class FacetFieldFilterComponent < Blacklight::Component
def initialize(facet_field:)
@facet_field = facet_field
end
diff --git a/app/components/blacklight/facet_field_inclusive_constraint_component.rb b/app/components/blacklight/facet_field_inclusive_constraint_component.rb
index bd990142bb..62d2d1be23 100644
--- a/app/components/blacklight/facet_field_inclusive_constraint_component.rb
+++ b/app/components/blacklight/facet_field_inclusive_constraint_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetFieldInclusiveConstraintComponent < ::ViewComponent::Base
+ class FacetFieldInclusiveConstraintComponent < Blacklight::Component
with_collection_parameter :facet_field
def initialize(facet_field:, values: nil)
diff --git a/app/components/blacklight/facet_field_list_component.rb b/app/components/blacklight/facet_field_list_component.rb
index 463acecd1a..2a8b19e161 100644
--- a/app/components/blacklight/facet_field_list_component.rb
+++ b/app/components/blacklight/facet_field_list_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetFieldListComponent < ::ViewComponent::Base
+ class FacetFieldListComponent < Blacklight::Component
def initialize(facet_field:, layout: nil)
@facet_field = facet_field
@layout = layout == false ? FacetFieldNoLayoutComponent : Blacklight::FacetFieldComponent
diff --git a/app/components/blacklight/facet_field_no_layout_component.rb b/app/components/blacklight/facet_field_no_layout_component.rb
index c77c89c236..c99f72c3b1 100644
--- a/app/components/blacklight/facet_field_no_layout_component.rb
+++ b/app/components/blacklight/facet_field_no_layout_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetFieldNoLayoutComponent < ::ViewComponent::Base
+ class FacetFieldNoLayoutComponent < Blacklight::Component
include Blacklight::ContentAreasShim
renders_one :label
diff --git a/app/components/blacklight/facet_field_pagination_component.rb b/app/components/blacklight/facet_field_pagination_component.rb
index 93a719ccf7..c4e7cedd6f 100644
--- a/app/components/blacklight/facet_field_pagination_component.rb
+++ b/app/components/blacklight/facet_field_pagination_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetFieldPaginationComponent < ::ViewComponent::Base
+ class FacetFieldPaginationComponent < Blacklight::Component
def initialize(facet_field:)
@facet_field = facet_field
end
diff --git a/app/components/blacklight/facet_item_component.rb b/app/components/blacklight/facet_item_component.rb
index 566a70f42a..e9f8618485 100644
--- a/app/components/blacklight/facet_item_component.rb
+++ b/app/components/blacklight/facet_item_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class FacetItemComponent < ::ViewComponent::Base
+ class FacetItemComponent < Blacklight::Component
extend Deprecation
with_collection_parameter :facet_item
diff --git a/app/components/blacklight/facet_item_pivot_component.rb b/app/components/blacklight/facet_item_pivot_component.rb
index 6c91bdb4ce..3687549259 100644
--- a/app/components/blacklight/facet_item_pivot_component.rb
+++ b/app/components/blacklight/facet_item_pivot_component.rb
@@ -2,7 +2,7 @@
module Blacklight
# Render facet items and any subtree
- class FacetItemPivotComponent < ::ViewComponent::Base
+ class FacetItemPivotComponent < Blacklight::Component
# Somewhat arbitrary number; the only important thing is that
# it is bigger than the number of leaf nodes in any collapsing
# pivot facet on the page.
diff --git a/app/components/blacklight/hidden_search_state_component.rb b/app/components/blacklight/hidden_search_state_component.rb
index 4bda030dcb..eb3dd224b1 100644
--- a/app/components/blacklight/hidden_search_state_component.rb
+++ b/app/components/blacklight/hidden_search_state_component.rb
@@ -4,7 +4,7 @@ module Blacklight
# Writes out zero or more elements, completely
# representing a hash passed in using Rails-style request parameters
# for hashes nested with arrays and other hashes.
- class HiddenSearchStateComponent < ::ViewComponent::Base
+ class HiddenSearchStateComponent < Blacklight::Component
# @param [Hash] params
def initialize(params:)
Deprecation.warn(self, "Passing page as a parameter to HiddenSearchStateComponent is deprecated and will not be supported in Blacklight 8") if params.key?(:page)
diff --git a/app/components/blacklight/metadata_field_component.rb b/app/components/blacklight/metadata_field_component.rb
index f128ef37ee..47c3ef311b 100644
--- a/app/components/blacklight/metadata_field_component.rb
+++ b/app/components/blacklight/metadata_field_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class MetadataFieldComponent < ::ViewComponent::Base
+ class MetadataFieldComponent < Blacklight::Component
with_collection_parameter :field
# @param field [Blacklight::FieldPresenter]
diff --git a/app/components/blacklight/metadata_field_layout_component.rb b/app/components/blacklight/metadata_field_layout_component.rb
index c332493135..51acc5f221 100644
--- a/app/components/blacklight/metadata_field_layout_component.rb
+++ b/app/components/blacklight/metadata_field_layout_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class MetadataFieldLayoutComponent < ::ViewComponent::Base
+ class MetadataFieldLayoutComponent < Blacklight::Component
include Blacklight::ContentAreasShim
with_collection_parameter :field
diff --git a/app/components/blacklight/response/facet_group_component.rb b/app/components/blacklight/response/facet_group_component.rb
index d5fa9034fc..270987898f 100644
--- a/app/components/blacklight/response/facet_group_component.rb
+++ b/app/components/blacklight/response/facet_group_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Response
# Render a group of facet fields
- class FacetGroupComponent < ::ViewComponent::Base
+ class FacetGroupComponent < Blacklight::Component
# @param [Blacklight::Response] response
# @param [Array] fields facet fields to render
# @param [String] title the title of the facet group section
diff --git a/app/components/blacklight/response/pagination_component.rb b/app/components/blacklight/response/pagination_component.rb
index 4c35f1b501..f0038fad36 100644
--- a/app/components/blacklight/response/pagination_component.rb
+++ b/app/components/blacklight/response/pagination_component.rb
@@ -3,7 +3,7 @@
module Blacklight
module Response
# Render a pagination widget for search results
- class PaginationComponent < ::ViewComponent::Base
+ class PaginationComponent < Blacklight::Component
# @param [Blacklight::Response] response
# @param [Hash] html html options for the pagination container
def initialize(response:, html: {}, **pagination_args)
diff --git a/app/components/blacklight/search_bar_component.rb b/app/components/blacklight/search_bar_component.rb
index 998d81a439..d5b73b07e5 100644
--- a/app/components/blacklight/search_bar_component.rb
+++ b/app/components/blacklight/search_bar_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class SearchBarComponent < ::ViewComponent::Base
+ class SearchBarComponent < Blacklight::Component
include Blacklight::ContentAreasShim
renders_one :append
diff --git a/app/components/blacklight/search_context_component.rb b/app/components/blacklight/search_context_component.rb
index 46a8a43fe0..210b51363e 100644
--- a/app/components/blacklight/search_context_component.rb
+++ b/app/components/blacklight/search_context_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class SearchContextComponent < ::ViewComponent::Base
+ class SearchContextComponent < Blacklight::Component
with_collection_parameter :search_context
def initialize(search_context:, search_session:)
diff --git a/app/components/blacklight/start_over_button_component.rb b/app/components/blacklight/start_over_button_component.rb
index cb0dcfa0fb..3385cbfe77 100644
--- a/app/components/blacklight/start_over_button_component.rb
+++ b/app/components/blacklight/start_over_button_component.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Blacklight
- class StartOverButtonComponent < ::ViewComponent::Base
+ class StartOverButtonComponent < Blacklight::Component
def call
link_to t('blacklight.search.start_over'), start_over_path, class: 'catalog_startOverLink btn btn-primary'
end
diff --git a/lib/blacklight.rb b/lib/blacklight.rb
index 9709668ed2..4e280b7389 100644
--- a/lib/blacklight.rb
+++ b/lib/blacklight.rb
@@ -7,6 +7,7 @@
module Blacklight
autoload :AbstractRepository, 'blacklight/abstract_repository'
+ autoload :Component, 'blacklight/component'
autoload :Configuration, 'blacklight/configuration'
autoload :Exceptions, 'blacklight/exceptions'
autoload :Parameters, 'blacklight/parameters'
diff --git a/lib/blacklight/component.rb b/lib/blacklight/component.rb
new file mode 100644
index 0000000000..8aab8e112f
--- /dev/null
+++ b/lib/blacklight/component.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Blacklight
+ class Component < ViewComponent::Base
+ class << self
+ # rubocop:disable Naming/MemoizedInstanceVariableName
+ def compiler
+ @__vc_compiler ||= EngineCompiler.new(self)
+ end
+ # rubocop:enable Naming/MemoizedInstanceVariableName
+ end
+
+ class EngineCompiler < ::ViewComponent::Compiler
+ # ViewComponent::Compiler locates and caches templates from sidecar files to the component source file.
+ # While this is sensible in a Rails application, it prevents component templates defined in an Engine
+ # from being overridden by an installing application without subclassing the component, which may also
+ # require modifying any partials rendering the component. This subclass of compiler overrides the template
+ # location algorithm to take the sidecar file names from the Engine, but look to see if a file of the
+ # same name existing in the installing application (ie, under Rails.root). If the latter exists, this
+ # compiler will cache that template instead of the engine-defined file; if not, the compiler will fall
+ # back to the engine-defined file.
+ def templates
+ @templates ||= begin
+ extensions = ActionView::Template.template_handler_extensions
+
+ component_class._sidecar_files(extensions).each_with_object([]) do |path, memo|
+ pieces = File.basename(path).split(".")
+ app_path = "#{Rails.root}/#{path.slice(path.index(component_class.view_component_path)..-1)}"
+
+ memo << {
+ path: File.exist?(app_path) ? app_path : path,
+ variant: pieces.second.split("+").second&.to_sym,
+ handler: pieces.last
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/blacklight/component_spec.rb b/spec/lib/blacklight/component_spec.rb
new file mode 100644
index 0000000000..12c2bbc6eb
--- /dev/null
+++ b/spec/lib/blacklight/component_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.describe Blacklight::Component do
+ let(:component_class) { Blacklight::DocumentTitleComponent }
+
+ context "subclassed" do
+ it "returns our Compiler implementation" do
+ expect(component_class.ancestors).to include described_class
+ expect(component_class.compiler).to be_a Blacklight::Component::EngineCompiler
+ end
+ end
+
+ describe Blacklight::Component::EngineCompiler do
+ subject(:compiler) { described_class.new(component_class) }
+
+ let(:original_compiler) { ViewComponent::Compiler.new(component_class) }
+ let(:original_path) { original_compiler.send(:templates).first[:path] }
+ let(:resolved_path) { compiler.templates.first[:path] }
+
+ context "without overrides" do
+ it "links to engine template" do
+ expect(resolved_path).not_to include(".internal_test_app")
+ expect(resolved_path).to eql(original_path)
+ end
+ end
+
+ context "with overrides" do
+ let(:path_match) do
+ Regexp.new(Regexp.escape(File.join(".internal_test_app", component_class.view_component_path)))
+ end
+
+ before do
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(path_match).and_return(true)
+ end
+
+ it "links to application template" do
+ expect(resolved_path).to include(".internal_test_app")
+ expect(resolved_path).not_to eql(original_path)
+ end
+ end
+ end
+end