Skip to content

Instantly share code, notes, and snippets.

@jeremysmithco
Last active November 28, 2024 07:30
Show Gist options
  • Save jeremysmithco/c47d788679131089bbc00d1fe1f77544 to your computer and use it in GitHub Desktop.
Save jeremysmithco/c47d788679131089bbc00d1fe1f77544 to your computer and use it in GitHub Desktop.
stimulus_data helper
# Example Usage
# data: stimulus_data(
# autosubmit: { controller: true },
# toggle: { controller: true, classes: { checked: "highlighted", unchecked: "unhighlighted" } },
# sortable: { target: "sortBy", actions: { click: "sort" },
# ).merge(other: "stuff")
class StimulusData
def initialize(**controllers)
@controllers = controllers
end
def call
{
controller: controller,
action: action
}.merge(
*targets,
*nested_attributes(:params),
*nested_attributes(:values),
*nested_attributes(:classes),
*nested_attributes(:outlets)
).compact_blank
end
private
attr_reader :controllers
def controller
controllers.select { |_, value| value[:controller] }.keys.map(&:to_s).map(&:dasherize).join(" ")
end
def action
controllers.select { |_, value| value.has_key?(:actions) }
.map { |key, value| extract_actions(key, value[:actions]) }
.join(" ")
end
def extract_actions(controller, actions)
actions.map { |key, value| "#{action_prefix(key)}#{controller.to_s.dasherize}##{value}" }
end
def action_prefix(action_key)
return "" if action_key == :default
"#{action_key}->"
end
def targets
controllers.select { |_, value| value.has_key?(:target) }
.map { |key, value| { "#{key}_target".to_sym => value[:target] } }
end
def nested_attributes(attribute)
controllers.select { |_, value| value.has_key?(attribute) }
.flat_map { |key, value| extract_attributes(key, attribute, value[attribute]) }
end
def extract_attributes(controller, attribute, attributes)
attributes.map { |key, value| { "#{controller}_#{key}_#{attribute.to_s.singularize}".to_sym => value } }
end
end
require "test_helper"
class StimulusDataTest < ActiveSupport::TestCase
context "#call" do
should "return empty hash when input empty" do
assert_equal({}, StimulusData.new.call)
end
should "return hash with proper output" do
assert_equal(
{ controller: "autosubmit" },
StimulusData.new(autosubmit: { controller: true }).call
)
assert_equal(
{ controller: "auto-submit", action: "input->auto-submit#submit resize@window->auto-submit#cancel" },
StimulusData.new(auto_submit: { controller: true, actions: { input: "submit", "resize@window": "cancel" } }).call
)
assert_equal(
{
action: "item#upvote spinner#start", item_id_param: "12345", item_url_param: "/votes",
item_payload_param: '{"value":"1234567"}', item_active_param: "true"
},
StimulusData.new(
item: { actions: { default: "upvote" }, params: { id: "12345", url: "/votes", payload: '{"value":"1234567"}', active: "true" } },
spinner: { actions: { default: "start" } }
).call
)
assert_equal(
{ autosubmit_target: "status" },
StimulusData.new(autosubmit: { target: "status" }).call
)
assert_equal(
{ controller: "tooltip", tooltip_content_value: "usernames…", tooltip_theme_value: "tooltip-sm" },
StimulusData.new(tooltip: { controller: true, values: { content: "usernames…", theme: "tooltip-sm" } }).call
)
assert_equal(
{ controller: "autosubmit checkbox-toggle", checkbox_toggle_checked_class: "highlighted", checkbox_toggle_unchecked_class: "un-highlight" },
StimulusData.new(autosubmit: { controller: true }, checkbox_toggle: { controller: true, classes: { checked: "highlighted", unchecked: "un-highlight" } }).call
)
assert_equal(
{ controller: "chat", chat_user_status_outlet: ".online-user" },
StimulusData.new(chat: { controller: true, outlets: { user_status: ".online-user" } }).call
)
assert_equal(
{ search_target: "projects", checkbox_target: "input" },
StimulusData.new(search: { target: "projects" }, checkbox: { target: "input" }).call
)
assert_equal(
{ controller: "search", search_loading_class: "bg-gray-500 animate-spinner cursor-busy" },
StimulusData.new(search: { controller: true, classes: { loading: "bg-gray-500 animate-spinner cursor-busy" } }).call
)
assert_equal(
{ controller: "search", search_loading_class: "search--busy", search_no_results_class: "search--empty" },
StimulusData.new(search: { controller: true, classes: { loading: "search--busy", no_results: "search--empty" } }).call
)
end
end
end
require 'stimulus_data'
module StimulusHelper
def stimulus_data(...)
StimulusData.new(...).call
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment