diff --git a/Gemfile.lock b/Gemfile.lock
index bbf3dcc197..bedb36e2a1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- avo (3.0.6)
+ avo (3.0.7)
actionview (>= 6.1)
active_link_to
activerecord (>= 6.1)
diff --git a/app/components/avo/field_wrapper_component.rb b/app/components/avo/field_wrapper_component.rb
index fe1a4a8163..f7088feb34 100644
--- a/app/components/avo/field_wrapper_component.rb
+++ b/app/components/avo/field_wrapper_component.rb
@@ -36,6 +36,7 @@ def initialize(
@full_width = full_width
@label = label
@resource = resource
+ @action = field.action
@short = short
@stacked = stacked
@style = style
@@ -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
@@ -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
diff --git a/app/controllers/avo/actions_controller.rb b/app/controllers/avo/actions_controller.rb
index 3f4184f291..bdec1d4047 100644
--- a/app/controllers/avo/actions_controller.rb
+++ b/app/controllers/avo/actions_controller.rb
@@ -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
diff --git a/app/views/avo/actions/show.html.erb b/app/views/avo/actions/show.html.erb
index 256ea5133c..ac7807fe37 100644
--- a/app/views/avo/actions/show.html.erb
+++ b/app/views/avo/actions/show.html.erb
@@ -1,6 +1,6 @@
<%= turbo_frame_tag "actions_show" do %>
"
data-no-confirmation="<%= @action.no_confirmation %>"
data-action-target="controllerDiv"
data-resource-name="<%= @resource.model_key %>"
@@ -28,7 +28,7 @@
<% @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)
%>
diff --git a/lib/avo/base_action.rb b/lib/avo/base_action.rb
index 2996a3116a..cd64140dcd 100644
--- a/lib/avo/base_action.rb
+++ b/lib/avo/base_action.rb
@@ -1,6 +1,7 @@
module Avo
class BaseAction
include Avo::Concerns::HasItems
+ include Avo::Concerns::HasActionStimulusControllers
class_attribute :name, default: nil
class_attribute :message
diff --git a/lib/avo/base_resource.rb b/lib/avo/base_resource.rb
index db2de57530..6fd9a93b24 100644
--- a/lib/avo/base_resource.rb
+++ b/lib/avo/base_resource.rb
@@ -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
diff --git a/lib/avo/concerns/has_action_stimulus_controllers.rb b/lib/avo/concerns/has_action_stimulus_controllers.rb
new file mode 100644
index 0000000000..029b123cb9
--- /dev/null
+++ b/lib/avo/concerns/has_action_stimulus_controllers.rb
@@ -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
diff --git a/lib/avo/concerns/has_stimulus_controllers.rb b/lib/avo/concerns/has_resource_stimulus_controllers.rb
similarity index 95%
rename from lib/avo/concerns/has_stimulus_controllers.rb
rename to lib/avo/concerns/has_resource_stimulus_controllers.rb
index 89847da804..f08ac0cfc4 100644
--- a/lib/avo/concerns/has_stimulus_controllers.rb
+++ b/lib/avo/concerns/has_resource_stimulus_controllers.rb
@@ -1,6 +1,6 @@
module Avo
module Concerns
- module HasStimulusControllers
+ module HasResourceStimulusControllers
extend ActiveSupport::Concern
included do
diff --git a/lib/avo/fields/base_field.rb b/lib/avo/fields/base_field.rb
index a2ce2e90c3..43f80c91f5 100644
--- a/lib/avo/fields/base_field.rb
+++ b/lib/avo/fields/base_field.rb
@@ -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
diff --git a/lib/avo/fields/concerns/has_html_attributes.rb b/lib/avo/fields/concerns/has_html_attributes.rb
index 796fbcc147..6065e49ffc 100644
--- a/lib/avo/fields/concerns/has_html_attributes.rb
+++ b/lib/avo/fields/concerns/has_html_attributes.rb
@@ -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
@@ -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
@@ -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
diff --git a/spec/dummy/app/avo/actions/show_current_time.rb b/spec/dummy/app/avo/actions/show_current_time.rb
new file mode 100644
index 0000000000..2b6b510a7e
--- /dev/null
+++ b/spec/dummy/app/avo/actions/show_current_time.rb
@@ -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
diff --git a/spec/dummy/app/avo/resources/course.rb b/spec/dummy/app/avo/resources/course.rb
index 6feca9abb3..19c5cd9b57 100644
--- a/spec/dummy/app/avo/resources/course.rb
+++ b/spec/dummy/app/avo/resources/course.rb
@@ -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
@@ -61,7 +61,7 @@ def fields
edit: {
input: {
data: {
- action: "course-resource#onCountryChange"
+ action: "city-in-country#onCountryChange"
}
}
}
@@ -79,4 +79,8 @@ def filters
filter Avo::Filters::CourseCountryFilter
filter Avo::Filters::CourseCityFilter
end
+
+ def actions
+ action Avo::Actions::ShowCurrentTime
+ end
end
diff --git a/spec/dummy/app/javascript/avo.custom.js b/spec/dummy/app/javascript/avo.custom.js
index c1e75e336a..6eb2cbf039 100644
--- a/spec/dummy/app/javascript/avo.custom.js
+++ b/spec/dummy/app/javascript/avo.custom.js
@@ -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
@@ -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
diff --git a/spec/dummy/app/javascript/avo_custom/course_resource_controller.js b/spec/dummy/app/javascript/avo_custom/city_in_country_controller.js
similarity index 90%
rename from spec/dummy/app/javascript/avo_custom/course_resource_controller.js
rename to spec/dummy/app/javascript/avo_custom/city_in_country_controller.js
index ed90f0b72e..c1224dad67 100644
--- a/spec/dummy/app/javascript/avo_custom/course_resource_controller.js
+++ b/spec/dummy/app/javascript/avo_custom/city_in_country_controller.js
@@ -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) {
@@ -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.
diff --git a/spec/dummy/app/models/course.rb b/spec/dummy/app/models/course.rb
index 915b806e17..0751b95844 100644
--- a/spec/dummy/app/models/course.rb
+++ b/spec/dummy/app/models/course.rb
@@ -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
diff --git a/spec/system/avo/stimulus_spec.rb b/spec/system/avo/stimulus_spec.rb
index f4d470264f..86938fdf01 100644
--- a/spec/system/avo/stimulus_spec.rb
+++ b/spec/system/avo/stimulus_spec.rb
@@ -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"
@@ -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