Skip to content

Commit

Permalink
feature: assign stimulus to actions (#2079)
Browse files Browse the repository at this point in the history
* feature: assign stimulus to actions

* Update spec/dummy/app/javascript/avo.custom.js

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix query assign

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
Paul-Bob and github-actions[bot] authored Dec 5, 2023
1 parent a94ebe4 commit 0445220
Show file tree
Hide file tree
Showing 16 changed files with 159 additions and 33 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
avo (3.0.6)
avo (3.0.7)
actionview (>= 6.1)
active_link_to
activerecord (>= 6.1)
Expand Down
17 changes: 14 additions & 3 deletions app/components/avo/field_wrapper_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def initialize(
@full_width = full_width
@label = label
@resource = resource
@action = field.action
@short = short
@stacked = stacked
@style = style
Expand Down Expand Up @@ -73,9 +74,11 @@ def data

# Add the built-in stimulus integration data tags.
if @resource.present?
@resource.get_stimulus_controllers.split(" ").each do |controller|
attributes["#{controller}-target"] = "#{@field.id.to_s.underscore}_#{@field.type.to_s.underscore}_wrapper".camelize(:lower)
end
add_stimulus_attributes_for(@resource, attributes)
end

if @action.present?
add_stimulus_attributes_for(@action, attributes)
end

# Fetch the data attributes off the html option
Expand Down Expand Up @@ -109,4 +112,12 @@ def short?
def full_width?
@full_width
end

private

def add_stimulus_attributes_for(entity, attributes)
entity.get_stimulus_controllers.split(" ").each do |controller|
attributes["#{controller}-target"] = "#{@field.id.to_s.underscore}_#{@field.type.to_s.underscore}_wrapper".camelize(:lower)
end
end
end
3 changes: 2 additions & 1 deletion app/controllers/avo/actions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ def handle
fields: action_params[:fields].except(:avo_resource_ids, :avo_selected_query),
current_user: _current_user,
resource: resource,
query: decrypted_query || @resource.find_record(resource_ids, params: params)
query: decrypted_query ||
(resource_ids.any? ? @resource.find_record(resource_ids, params: params) : [])
)

respond performed_action.response
Expand Down
4 changes: 2 additions & 2 deletions app/views/avo/actions/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<%= turbo_frame_tag "actions_show" do %>
<div
data-controller="action"
data-controller="<%= ["action", @action.get_stimulus_controllers].join(" ") %>"
data-no-confirmation="<%= @action.no_confirmation %>"
data-action-target="controllerDiv"
data-resource-name="<%= @resource.model_key %>"
Expand Down Expand Up @@ -28,7 +28,7 @@
<div class="mt-4 -mx-6">
<% @action.get_fields.each_with_index do |field, index| %>
<%= render field
.hydrate(resource: @resource, record: @resource.record, user: @resource.user, view: @view)
.hydrate(resource: @resource, record: @resource.record, user: @resource.user, view: @view, action: @action)
.component_for_view(@view)
.new(field: field, resource: @resource, index: index, form: form, compact: true)
%>
Expand Down
1 change: 1 addition & 0 deletions lib/avo/base_action.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Avo
class BaseAction
include Avo::Concerns::HasItems
include Avo::Concerns::HasActionStimulusControllers

class_attribute :name, default: nil
class_attribute :message
Expand Down
2 changes: 1 addition & 1 deletion lib/avo/base_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class BaseResource
include Avo::Concerns::HasItems
include Avo::Concerns::CanReplaceItems
include Avo::Concerns::HasControls
include Avo::Concerns::HasStimulusControllers
include Avo::Concerns::HasResourceStimulusControllers
include Avo::Concerns::ModelClassConstantized
include Avo::Concerns::HasDescription
include Avo::Concerns::HasHelpers
Expand Down
15 changes: 15 additions & 0 deletions lib/avo/concerns/has_action_stimulus_controllers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Avo
module Concerns
module HasActionStimulusControllers
extend ActiveSupport::Concern

included do
class_attribute :stimulus_controllers, default: ""
end

def get_stimulus_controllers
self.class.stimulus_controllers
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Avo
module Concerns
module HasStimulusControllers
module HasResourceStimulusControllers
extend ActiveSupport::Concern

included do
Expand Down
1 change: 1 addition & 0 deletions lib/avo/fields/base_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def initialize(id, **args, &block)
@stacked = args[:stacked] || nil
@for_presentation_only = args[:for_presentation_only] || false
@resource = args[:resource]
@action = args[:action]
@components = args[:components] || {}

@args = args
Expand Down
39 changes: 27 additions & 12 deletions lib/avo/fields/concerns/has_html_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ def get_html(name = nil, element:, view:)
default_attribute_value name
end

add_default_data_attributes attributes, name, element, view
add_action_data_attributes(attributes, name, element)
add_resource_data_attributes(attributes, name, element, view)

attributes
end

private
Expand All @@ -50,18 +53,17 @@ def default_attribute_value(name)
name == :data ? {} : ""
end

def add_default_data_attributes(attributes, name, element, view)
if !attributes.nil? && name == :data && element == :input && view.in?([:edit, :new]) && resource.present? && resource.respond_to?(:get_stimulus_controllers)
extra_attributes = resource.get_stimulus_controllers
.split(" ")
.map do |controller|
[:"#{controller}-target", "#{id.to_s.underscore}_#{type.to_s.underscore}_input".camelize(:lower)]
end
.to_h
def add_action_data_attributes(attributes, name, element)
if can_add_stimulus_attributes_for?(action, attributes, name, element)
attributes.merge!(stimulus_attributes_for(action))
end
end

attributes.merge extra_attributes
else
attributes
def add_resource_data_attributes(attributes, name, element, view)
if can_add_stimulus_attributes_for?(resource, attributes, name, element) && view.in?([:edit, :new])
resource_stimulus_attributes = stimulus_attributes_for(resource)

attributes.merge!(resource_stimulus_attributes)
end
end

Expand Down Expand Up @@ -106,6 +108,19 @@ def merge_values_as(as: :array, values: [])

result if result.present?
end

def can_add_stimulus_attributes_for?(entity, attributes, name, element)
!attributes.nil? && name == :data && element == :input && entity.present? && entity.respond_to?(:get_stimulus_controllers)
end

def stimulus_attributes_for(entity)
entity.get_stimulus_controllers
.split(" ")
.map do |controller|
[:"#{controller}-target", "#{id.to_s.underscore}_#{type.to_s.underscore}_input".camelize(:lower)]
end
.to_h
end
end
end
end
Expand Down
39 changes: 39 additions & 0 deletions spec/dummy/app/avo/actions/show_current_time.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class Avo::Actions::ShowCurrentTime < Avo::BaseAction
self.name = "Show current time"
self.standalone = true
self.stimulus_controllers = "city-in-country"

def fields
field :country,
as: :select,
name: "Country",
options: Course.countries.map { |country| [country, country] }.prepend(["-", nil]).to_h,
html: {
edit: {
input: {
data: {
action: "city-in-country#onCountryChange"
}
}
}
}
field :city,
as: :select,
name: "City",
options: Course.cities.values.flatten.map { |city| [city, city] }.to_h,
display_value: false
end

def handle(**args)
city = args.dig(:fields, :city)
timezone_id = Course.timezones.find { |_, cities| cities.include?(city) }&.first

if timezone_id
formatted_current_time = TZInfo::Timezone.get(timezone_id).now.strftime('%H:%M:%S')

succeed "In #{city} it's now #{formatted_current_time}."
else
warn "No city chosen"
end
end
end
8 changes: 6 additions & 2 deletions spec/dummy/app/avo/resources/course.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class Avo::Resources::Course < Avo::BaseResource
query: -> { query.ransack(id_eq: params[:q], name_cont: params[:q], m: "or").result(distinct: false) }
}
self.keep_filters_panel_open = true
self.stimulus_controllers = "course-resource toggle-fields"
self.stimulus_controllers = "city-in-country toggle-fields"

def fields

Expand Down Expand Up @@ -61,7 +61,7 @@ def fields
edit: {
input: {
data: {
action: "course-resource#onCountryChange"
action: "city-in-country#onCountryChange"
}
}
}
Expand All @@ -79,4 +79,8 @@ def filters
filter Avo::Filters::CourseCountryFilter
filter Avo::Filters::CourseCityFilter
end

def actions
action Avo::Actions::ShowCurrentTime
end
end
4 changes: 2 additions & 2 deletions spec/dummy/app/javascript/avo.custom.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Application } from '@hotwired/stimulus'
import CourseController from './avo_custom/course_resource_controller'
import CityInCountryController from './avo_custom/city_in_country_controller'
import NestedForm from 'stimulus-rails-nested-form'

// Use you own stimulus install
Expand All @@ -9,7 +9,7 @@ const application = Application.start()

// Configure Stimulus development experience
application.debug = window?.localStorage.getItem('avo.debug')
application.register('course-resource', CourseController)
application.register('city-in-country', CityInCountryController)
application.register('nested-form', NestedForm)

// eslint-disable-next-line no-console
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class extends Controller {
static initialValue

get placeholder() {
return this.citySelectInputTarget.ariaPlaceholder
return this.citySelectInputTarget.getAttribute('aria-placeholder')
}

set loading(isLoading) {
Expand All @@ -34,13 +34,10 @@ export default class extends Controller {
}

async connect() {
// Add the controller functionality only on forms
if (['edit', 'new'].includes(this.viewValue)) {
this.captureTheInitialValue()
this.captureTheInitialValue()

// Trigger the change on load
await this.onCountryChange()
}
// Trigger the change on load
await this.onCountryChange()
}

// Read the country select.
Expand Down
10 changes: 10 additions & 0 deletions spec/dummy/app/models/course.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ def self.cities
}
end

def self.timezones
{
"America/New_York" => ["New York", "Boston", "Philadelphia"],
"America/Los_Angeles" => ["Los Angeles", "San Francisco"],
"Asia/Tokyo" => ["Tokyo", "Osaka", "Kyoto", "Hiroshima", "Yokohama", "Nagoya", "Kobe"],
"Europe/Madrid" => ["Madrid", "Valencia", "Barcelona"],
"Asia/Bangkok" => ["Chiang Mai", "Bangkok", "Phuket"]
}
end

def self.ransackable_attributes(auth_object = nil)
["city", "country", "created_at", "id", "name", "skills", "starting_at", "updated_at"]
end
Expand Down
34 changes: 33 additions & 1 deletion spec/system/avo/stimulus_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
end
end

describe "course_controller#onCountryChange" do
describe "city-in-country#onCountryChange" do
it "changes the cities" do
visit "/admin/resources/courses/#{course.id}/edit"

Expand All @@ -38,4 +38,36 @@
expect(page).to have_select "course_city", selected: "Kyoto", options: ["Choose an option", "Tokyo", "Osaka", "Kyoto", "Hiroshima", "Yokohama", "Nagoya", "Kobe"]
end
end

describe "city-in-country#onCountryChange (attached to ShowCurrentTime Action)" do
it "filters cities based on a given country" do
visit "/admin/resources/courses"

click_on "Actions"
click_on "Show current time"

expect(page).to have_css "[data-field-id='country']"
expect(page).to have_css "[data-field-id='city']"
expect(page).to have_select "fields_country"

select "Spain", from: "fields_country"

expect(page).to have_select "fields_country", selected: "Spain"
expect(page).to have_select "fields_city", selected: "Choose an option", options: ["Choose an option", "Madrid", "Valencia", "Barcelona"]

select "Thailand", from: "fields_country"

expect(page).to have_select "fields_country", selected: "Thailand"
expect(page).to have_select "fields_city", selected: "Choose an option", options: ["Choose an option", "Chiang Mai", "Bangkok", "Phuket"]

select "Japan", from: "fields_country"
expect(page).to have_select "fields_country", selected: "Japan"
select "Kyoto", from: "fields_city"
expect(page).to have_select "fields_city", selected: "Kyoto", options: ["Choose an option", "Tokyo", "Osaka", "Kyoto", "Hiroshima", "Yokohama", "Nagoya", "Kobe"]

click_on "Run"

expect(page).to have_text(/In Kyoto it's now \d\d:\d\d:\d\d./)
end
end
end

0 comments on commit 0445220

Please sign in to comment.