diff --git a/lib/vident/root_component.rb b/lib/vident/root_component.rb index 4122e77..d4aa9a2 100644 --- a/lib/vident/root_component.rb +++ b/lib/vident/root_component.rb @@ -70,7 +70,8 @@ def target_data_attribute(name) end def build_outlet_selector(outlet_selector) - "##{@id} [data-controller~=#{outlet_selector}]" + prefix = @id ? "##{@id} " : "" + "#{prefix}[data-controller~=#{outlet_selector}]" end def outlet(css_selector: nil) @@ -108,6 +109,13 @@ def with_actions(*actions_to_set) end alias_method :with_action, :with_actions + # Return the HTML `data-` attribute for the given outlets + def with_outlets(*outlets) + attrs = build_outlet_data_attributes(outlets) + attrs.map { |dt, n| "data-#{dt}=\"#{n}\"" }.join(" ").html_safe + end + alias_method :with_outlet, :with_outlets + private # An implicit Stimulus controller name is built from the implicit controller path @@ -154,19 +162,29 @@ def tag_data_attributes def outlet_list return {} unless @outlets&.size&.positive? + build_outlet_data_attributes(@outlets) + end + + def parse_outlet(outlet_config) + if outlet_config.is_a?(String) + [outlet_config, build_outlet_selector(outlet_config)] + elsif outlet_config.is_a?(Symbol) + outlet_config = outlet_config.to_s.tr("_", "-") + [outlet_config, build_outlet_selector(outlet_config)] + elsif outlet_config.is_a?(Array) + outlet_config[..1] + elsif outlet_config.respond_to?(:stimulus_identifier) # Is a Component + [outlet_config.stimulus_identifier, build_outlet_selector(outlet_config.stimulus_identifier)] + elsif outlet_config.send(:implied_controller_name) # Is a RootComponent ? + [outlet_config.send(:implied_controller_name), build_outlet_selector(outlet_config.send(:implied_controller_name))] + else + raise ArgumentError, "Invalid outlet config: #{outlet_config}" + end + end - @outlets.each_with_object({}) do |outlet_config, obj| - identifier, css_selector = if outlet_config.is_a?(String) - [outlet_config, build_outlet_selector(outlet_config)] - elsif outlet_config.is_a?(Array) - outlet_config[..1] - elsif outlet_config.respond_to?(:stimulus_identifier) # Is a Component - [outlet_config.stimulus_identifier, build_outlet_selector(outlet_config.stimulus_identifier)] - elsif outlet_config.send(:implied_controller_name) # Is a RootComponent ? - [outlet_config.send(:implied_controller_name), build_outlet_selector(outlet_config.send(:implied_controller_name))] - else - raise ArgumentError, "Invalid outlet config: #{outlet_config}" - end + def build_outlet_data_attributes(outlets) + outlets.each_with_object({}) do |outlet_config, obj| + identifier, css_selector = parse_outlet(outlet_config) obj[:"#{implied_controller_name}-#{identifier}-outlet"] = css_selector end end diff --git a/test/vident/root_component_test.rb b/test/vident/root_component_test.rb index 21ac48e..c087795 100644 --- a/test/vident/root_component_test.rb +++ b/test/vident/root_component_test.rb @@ -3,14 +3,14 @@ module Vident class RootComponentTest < Minitest::Test def setup - component = Class.new do + @component = Class.new do include Vident::RootComponent def get_all_data_attrs tag_data_attributes end end - @root_component = component.new(controllers: ["foo/my_controller"]) + @root_component = @component.new(controllers: ["foo/my_controller"]) end def test_action @@ -26,8 +26,8 @@ def test_target end def test_named_classes - @root_component.instance_variable_set(:@named_classes, {my_class: "my-class"}) - assert_equal "my-class", @root_component.named_classes(:my_class) + root = @component.new(controllers: ["foo/my_controller"], named_classes: {my_class: "my-class"}) + assert_equal "my-class", root.named_classes(:my_class) end def test_action_data_attribute @@ -51,27 +51,39 @@ def test_with_actions assert_equal "data-action='foo--my-controller#myAction'", @root_component.with_actions(:my_action) end + def test_outlet_selector_when_no_id + root_component = @component.new(controllers: ["foo/my_controller"], id: "the-id") + assert_equal "data-foo--my-controller-my-outlet-outlet=\"#the-id [data-controller~=my-outlet]\"", root_component.with_outlets(:my_outlet) + end + + def test_with_outlets_no_id + assert_equal "data-foo--my-controller-my-outlet-outlet=\"[data-controller~=my-outlet]\"", @root_component.with_outlets(:my_outlet) + end + def test_get_all_data_attrs - # Setup - @root_component.instance_variable_set(:@named_classes, {my_class: "my-class"}) - @root_component.instance_variable_set(:@outlets, ["my-outlet", ["other-component", "#my_id"]]) - @root_component.instance_variable_set(:@data_maps, [{my_key: "my-value"}]) - @root_component.instance_variable_set(:@actions, [:my_action]) - @root_component.instance_variable_set(:@targets, [:my_target]) + root_component = @component.new( + id: "the-id", + controllers: ["foo/my_controller"], + named_classes: {my_class: "my-class"}, + outlets: ["my-outlet", ["other-component", ".custom-selector"]], + data_maps: [{my_key: "my-value"}], + actions: [:my_action], + targets: [:my_target] + ) # Expected result expected_result = { controller: "foo--my-controller", action: "foo--my-controller#myAction", "foo--my-controller-target": "myTarget", - "foo--my-controller-my-outlet-outlet": "[data-controller~=my-outlet]", - "foo--my-controller-other-component-outlet": "#my_id", + "foo--my-controller-my-outlet-outlet": "#the-id [data-controller~=my-outlet]", + "foo--my-controller-other-component-outlet": ".custom-selector", "foo--my-controller-my-class-class": "my-class", "foo--my-controller-my_key": "my-value" } # Test - assert_equal expected_result, @root_component.get_all_data_attrs + assert_equal expected_result, root_component.get_all_data_attrs end end end