Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,6 +260,7 @@ Metrics/ClassLength:
- 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_action_field_widget.rb'
- 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_action.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/action/action_collection_decorator.rb'

Style/OpenStructUse:
Exclude:
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,7 @@ def self.make_form_data_from_fields(datasource, fields)
def self.make_form_data(datasource, raw_data, fields)
data = {}
raw_data.each do |key, value|
field = fields.find { |f| f.label == key }
field = fields.find { |f| f.id == key }
# Skip fields from the default form
next if Schema::GeneratorAction::DEFAULT_FIELDS.map { |f| f[:field] }.include?(key)

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ class GeneratorAction
DEFAULT_FIELDS = [
{
field: 'Loading...',
label: 'Loading...',
type: 'String',
isReadOnly: true,
defaultValue: 'Form is loading',
Expand DownExpand Up@@ -79,7 +80,8 @@ def self.build_field_schema(datasource, field)
description: field.description,
isRequired: field.is_required,
isReadOnly: field.is_read_only,
field: field.label,
field: field.id,
label: field.label,
value: ForestValueConverter.value_to_forest(field),
widgetEdit: GeneratorActionFieldWidget.build_widget_options(field)
}
Expand DownExpand Up@@ -146,7 +148,7 @@ def self.extract_fields_and_layout(form)
else
fields << element
# frontend rule
layout << Actions::ActionLayoutElement::InputElement.new(component: 'Input', field_id: element.label)
layout << Actions::ActionLayoutElement::InputElement.new(component: 'Input', field_id: element.id)
end
end

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ module Schema
},
get_form: [
ActionField.new(
id: 'label',
label: 'label',
description: 'email',
type: 'String',
Expand All@@ -74,7 +75,16 @@ module Schema
httpMethod: 'POST',
redirect: nil,
download: false,
fields: [{ description: 'email', isRequired: true, isReadOnly: false, field: 'label', widgetEdit: nil, type: 'String', defaultValue: '' }],
fields: [{
description: 'email',
isRequired: true,
isReadOnly: false,
field: 'label',
widgetEdit: nil,
type: 'String',
defaultValue: '',
label: 'label'
}],
hooks: { load: false, change: ['changeHook'] }
}
)
Expand DownExpand Up@@ -213,7 +223,7 @@ module Schema
}
},
get_form: [
ActionField.new(label: 'label', type: 'String'),
ActionField.new(id: 'label', label: 'label', type: 'String'),
ActionLayoutElement::SeparatorElement.new
]
)
Expand All@@ -237,6 +247,7 @@ module Schema
defaultValue: nil,
description: nil,
field: 'label',
label: 'label',
isReadOnly: false,
isRequired: false,
type: 'String',
Expand DownExpand Up@@ -264,7 +275,7 @@ module Schema
}
},
get_form: [
ActionField.new(label: 'label', type: 'String'),
ActionField.new(id: 'label_id', label: 'label', type: 'String'),
ActionLayoutElement::HtmlBlockElement.new(content: '<p>foo</p>')
]
)
Expand All@@ -287,7 +298,8 @@ module Schema
{
defaultValue: nil,
description: nil,
field: 'label',
field: 'label_id',
label: 'label',
isReadOnly: false,
isRequired: false,
type: 'String',
Expand DownExpand Up@@ -317,8 +329,8 @@ module Schema
get_form: [
ActionLayoutElement::RowElement.new(
fields: [
ActionField.new(label: 'label', type: 'String'),
ActionField.new(label: 'amount', type: 'String')
ActionField.new(id: 'label_id', label: 'label', type: 'String'),
ActionField.new(id: 'amount_id', label: 'amount', type: 'String')
]
)
]
Expand All@@ -340,7 +352,8 @@ module Schema
download: false,
fields: [
{
field: 'label',
field: 'label_id',
label: 'label',
type: 'String',
description: nil,
isRequired: false,
Expand All@@ -349,7 +362,8 @@ module Schema
defaultValue: nil
},
{
field: 'amount',
label: 'amount',
field: 'amount_id',
type: 'String',
description: nil,
isRequired: false,
Expand DownExpand Up@@ -378,7 +392,7 @@ module Schema
}
},
get_form: [
ActionField.new(label: 'label', type: 'String'),
ActionField.new(id: 'label_id', label: 'label', type: 'String'),
ActionLayoutElement::SeparatorElement.new
]
)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@ def initialize(child_collection, datasource)

def add_action(name, action)
action.build_elements
action.validate_fields_ids
@actions[name] = action

mark_schema_as_dirty
Expand DownExpand Up@@ -43,24 +44,14 @@ def get_form(caller, name, data = nil, filter = nil, metas = {})
if metas[:search_field]
# in the case of a search hook,
# we don't want to rebuild all the fields. only the one searched
dynamic_fields = dynamic_fields.select { |field| field.label == metas[:search_field] }
dynamic_fields = dynamic_fields.select { |field| field.id == metas[:search_field] }
end
dynamic_fields = drop_defaults(context, dynamic_fields, form_values)
dynamic_fields = drop_ifs(context, dynamic_fields) unless metas[:include_hidden_fields]

fields = drop_deferred(context, metas[:search_values], dynamic_fields).compact

fields.each do |field|
next if field.type == 'Layout'

if field.value.nil?
# customer did not define a handler to rewrite the previous value => reuse current one.
field.value = form_values[field.label]
end

# fields that were accessed through the context.get_form_value(x) getter should be watched.
field.watch_changes = used.include?(field.label)
end
set_watch_changes_on_fields(form_values, used, fields)

fields
end
Expand All@@ -73,9 +64,34 @@ def refine_schema(sub_schema)

private

def set_watch_changes_on_fields(form_values, used, fields)
fields.each do |field|
if field.type != 'Layout'

if field.value.nil?
# customer did not define a handler to rewrite the previous value => reuse current one.
field.value = form_values[field.id]
end

# fields that were accessed through the context.get_form_value(x) getter should be watched.
field.watch_changes = used.include?(field.id)
elsif field.component == 'Row'
set_watch_changes_on_fields(form_values, used, field.fields)
end
end
end

def execute_on_sub_fields(field)
return unless field.type == 'Layout' && field.component == 'Row'

field.fields = yield(field.fields)
end

def drop_defaults(context, fields, data)
fields.map do |field|
if field.type == 'Layout'
execute_on_sub_fields(field) { |sub_fields| drop_defaults(context, sub_fields, data) }

field
else
drop_default(context, field, data)
Expand All@@ -84,14 +100,25 @@ def drop_defaults(context, fields, data)
end

def drop_default(context, field, data)
data[field.label] = evaluate(context, field.default_value) unless data.key?(field.label)
data[field.id] = evaluate(context, field.default_value) unless data.key?(field.id)
field.default_value = nil

field
end

def drop_ifs(context, fields)
if_values = fields.map { |field| !field.if_condition || evaluate(context, field.if_condition) }
if_values = fields.map do |field|
if evaluate(context, field.if_condition) == false
false
elsif field.type == 'Layout' && field.component == 'Row'
field.fields = drop_ifs(context, field.fields || [])

true unless field.fields.empty?
else
true
end
end

new_fields = fields.select.with_index { |_field, index| if_values[index] }
new_fields.each do |field|
field = field.dup
Expand All@@ -105,6 +132,8 @@ def drop_deferred(context, search_values, fields)
new_fields = []
fields.each do |field|
field = field.dup
execute_on_sub_fields(field) { |sub_fields| drop_deferred(context, search_values, sub_fields) }

field.instance_variables.each do |key|
key = key.to_s.delete('@').to_sym

Expand All@@ -114,7 +143,7 @@ def drop_deferred(context, search_values, fields)
value = field.send(key)
key = key.to_s.concat('=').to_sym

search_value = field.type == 'Layout' ? nil : search_values&.dig(field.label)
search_value = field.type == 'Layout' ? nil : search_values&.dig(field.id)
field.send(key, evaluate(context, value, search_value))
end

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,20 @@ def build_elements
end
end

def validate_fields_ids(form = @form, used = [])
form&.each do |element|
if element.type == 'Layout' && element.component == 'Row'
validate_fields_ids(element.fields, used)
else
if used.include?(element.id)
raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
"All field must have different 'id'. Conflict come from field '#{element.id}'"
end
used << element.id
end
end
end

def build_widget(field)
case field[:widget]
when 'AddressAutocomplete'
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,11 +3,12 @@ module Decorators
module Action
class DynamicField < BaseFormElement
attr_accessor :type, :label, :description, :is_required, :is_read_only, :if_condition, :value, :default_value,
:collection_name, :enum_values, :placeholder
:collection_name, :enum_values, :placeholder, :id

def initialize(
type:,
label:,
label: nil,
id: nil,
description: nil,
is_required: false,
is_read_only: false,
Expand All@@ -21,7 +22,13 @@ def initialize(
)
super(type: type)

@label = label
if id.nil? && label.nil?
raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
"A field must have an 'id' or a 'label' defined."
end

@label = label.nil? ? id : label
@id = id.nil? ? label : id
@description = description
@is_required = is_required
@is_read_only = is_read_only
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,6 +32,7 @@ def initialize(content:, **options)

class RowElement < LayoutElement
include ForestAdminDatasourceToolkit::Exceptions

attr_accessor :fields

def initialize(options)
Expand All@@ -58,7 +59,7 @@ def validate_no_layout_subfields!(fields)

def instantiate_subfields(fields)
fields.map do |field|
ForestAdminDatasourceToolkit::Components::Actions::ActionFieldFactory.build(field.to_h)
DynamicField.new(**field.to_h)
end
end
end
Expand Down
Loading