Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ed79b1a
chore: add tests
matthv Sep 23, 2024
520a977
chore: add tests
matthv Sep 25, 2024
eadaf5c
feat(form): add page element
matthv Sep 24, 2024
15c1aaf
feat(form): handle page to factory with new PageElement to ActionLayo…
matthv Sep 24, 2024
a8fc72d
fix(page): validation
matthv Sep 25, 2024
d97b80a
chore: add test on base_action
matthv Sep 25, 2024
829fd52
chore: add field type layout
matthv Sep 26, 2024
030f22e
feat(form): add new form_factory
matthv Sep 26, 2024
01897a1
fix: form layout
matthv Sep 26, 2024
b0ff2c8
fix: action layout
matthv Sep 26, 2024
5b9d54c
fix: actions
matthv Sep 27, 2024
ff2451e
fix: generator_actions and refactor extract fields & layout
matthv Sep 27, 2024
483213e
chore: add page test on generator_action
matthv Sep 27, 2024
0ca9436
fix: missing validate ids
matthv Sep 27, 2024
3856420
chore: add test on form factory
matthv Sep 27, 2024
416d03c
feat(action): ensure form is correct
matthv Sep 27, 2024
945d4f1
fix: remove old test on base_action
matthv Sep 27, 2024
935f219
feat: update drop function to work with the page element
nicolasalexandre9 Sep 27, 2024
c810827
test: add tests
nicolasalexandre9 Sep 27, 2024
526e285
chore: fix test
matthv Sep 30, 2024
d4d79c0
fix: action collection
matthv Sep 30, 2024
1e7dab6
chore: remove useless ElementFactory
matthv Sep 30, 2024
3c9a824
fix: search_field behavior in nested layout component
nicolasalexandre9 Sep 30, 2024
7b9e46e
fix: form factory elements
nicolasalexandre9 Oct 4, 2024
8070aaf
refactor: generator_action
matthv Oct 9, 2024
f6eb514
fix: next_button & previous_button to string
matthv Oct 9, 2024
23e6aeb
refactor: test
matthv Oct 9, 2024
9c9a7cf
fix(action_field_factory): return nil when element is empty
matthv Oct 9, 2024
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
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ Metrics/MethodLength:
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/action/base_action.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/action/action_collection_decorator.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/action/form_factory.rb'

Metrics/BlockLength:
Exclude:
Expand Down Expand Up @@ -285,6 +286,7 @@ Layout/LineLength:
- 'packages/forest_admin_agent/lib/forest_admin_agent/http/forest_admin_api_requester.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/services/permissions.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/action/form_layout_element.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/override/context/create_override_customization_context.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/override/context/update_override_customization_context.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/override/context/delete_override_customization_context.rb'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ def self.build_schema(collection, name)
action = collection.schema[:actions][name]
action_index = collection.schema[:actions].keys.index(name)
slug = get_action_slug(name)

form_elements = extract_fields_and_layout(collection.get_form(nil, name))
if action.static_form?
fields = build_fields(collection, form_elements[:fields])
Expand Down Expand Up @@ -72,6 +71,14 @@ def self.build_layout_schema(field)
component: field.component.camelize(:lower),
fields: field.fields.map { |f| build_layout_schema(f) }
}
elsif field.component == 'Page'
return {
**field.to_h,
component: field.component.camelize(:lower),
elements: field.elements.map do |f|
build_layout_schema(f)
end
}
end

{ **field.to_h, component: field.component.camelize(:lower) }
Expand Down Expand Up @@ -135,18 +142,15 @@ def self.build_layout(elements)
def self.extract_fields_and_layout(form)
fields = []
layout = []

form&.each do |element|
if element.type == Actions::FieldType::LAYOUT
if element.component == 'Row'
extract = extract_fields_and_layout(element.fields)
element.fields = extract[:layout]
if %w[Page Row].include?(element.component)
extract = extract_fields_and_layout_for_component(element)
layout << element
fields.concat(extract[:fields])
else
layout << element
end

else
fields << element
# frontend rule
Expand All @@ -156,6 +160,13 @@ def self.extract_fields_and_layout(form)

{ fields: fields, layout: layout }
end

def self.extract_fields_and_layout_for_component(element)
key = element.component == 'Page' ? :elements : :fields
extract = extract_fields_and_layout(element.public_send(key))
element.public_send(:"#{key}=", extract[:layout])
extract
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,80 @@ module Schema
end
end

context 'with page element' do
before do
@collection = collection_build(
schema: {
actions: {
'Charge credit card' => BaseAction.new(
scope: Types::ActionScope::SINGLE
)
}
},
get_form: [
ActionLayoutElement::PageElement.new(
next_button_label: 'Next',
previous_button_label: 'Previous',
elements: [
ActionLayoutElement::HtmlBlockElement.new(content: '<h1>Charge the credit card of the customer</h1>'),
ActionLayoutElement::RowElement.new(
fields: [
ActionField.new(id: 'label', label: 'label', type: 'String'),
ActionField.new(id: 'amount', label: 'amount', type: 'String')
]
)
]
)
]
)
end

it 'generate schema correctly' do
schema = described_class.build_schema(@collection, 'Charge credit card')

expect(schema).to eq(
{
id: 'collection-0-charge-credit-card',
name: 'Charge credit card',
submitButtonLabel: nil,
description: nil,
type: 'single',
baseUrl: nil,
endpoint: '/forest/_actions/collection/0/charge-credit-card',
httpMethod: 'POST',
redirect: nil,
download: false,
fields: [
{
field: 'label',
label: 'label',
type: 'String',
description: nil,
isRequired: false,
isReadOnly: false,
widgetEdit: nil,
defaultValue: nil
},
{
field: 'amount',
label: 'amount',
type: 'String',
description: nil,
isRequired: false,
isReadOnly: false,
widgetEdit: nil,
defaultValue: nil
}
],
# layout: [
# { component: 'page', type: 'Layout', elements: ['htmlBlock', 'row'] }
# ],
hooks: { load: false, change: ['changeHook'] }
}
)
end
end

describe 'extract_fields_and_layout' do
before do
@collection = collection_build(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def initialize(child_collection, datasource)
end

def add_action(name, action)
ensure_form_is_correct(action.form, name)
action.build_elements
action.validate_fields_ids
@actions[name] = action
Expand Down Expand Up @@ -41,15 +42,13 @@ def get_form(caller, name, data = nil, filter = nil, metas = {})
context = get_context(caller, action, form_values, filter, used, metas[:change_field])

dynamic_fields = action.form
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.id == metas[:search_field] }
end
dynamic_fields = select_in_form_fields(dynamic_fields, metas[:search_field]) if metas[:search_field]
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 = drop_deferred(context, metas[:search_values], dynamic_fields)

fields.compact

set_watch_changes_on_fields(form_values, used, fields)

Expand All @@ -64,6 +63,31 @@ def refine_schema(sub_schema)

private

def ensure_form_is_correct(form, action_name)
return if form.nil? || form.empty?

is_page_component = ->(element) { element[:type] == 'Layout' && element[:component] == 'Page' }
pages = is_page_component.call(form.first)

form.each do |element|
if pages != is_page_component.call(element)
raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
"You cannot mix pages and other form elements in smart action '#{action_name}' form"
end
end
end

def select_in_form_fields(fields, search_field)
fields.select do |field|
if nested_layout_element?(field)
key = field.component == 'Page' ? :elements : :fields
select_in_form_fields(field.public_send(:"#{key}"), search_field)
else
field.id == search_field
end
end
end

def set_watch_changes_on_fields(form_values, used, fields)
fields.each do |field|
if field.type != 'Layout'
Expand All @@ -77,14 +101,20 @@ def set_watch_changes_on_fields(form_values, used, fields)
field.watch_changes = used.include?(field.id)
elsif field.component == 'Row'
set_watch_changes_on_fields(form_values, used, field.fields)
elsif field.component == 'Page'
set_watch_changes_on_fields(form_values, used, field.elements)
end
end
end

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

field.fields = yield(field.fields)
if field.component == 'Page'
field.elements = yield(field.elements)
elsif field.component == 'Row'
field.fields = yield(field.fields)
end
end

def drop_defaults(context, fields, data)
Expand All @@ -110,10 +140,12 @@ def drop_ifs(context, fields)
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 || [])
elsif nested_layout_element?(field)
key = field.component == 'Page' ? :elements : :fields
field.public_send(:"#{key}=", drop_ifs(context, field.public_send(:"#{key}") || []))

true unless field.public_send(:"#{key}").empty?

true unless field.fields.empty?
else
true
end
Expand Down Expand Up @@ -170,6 +202,10 @@ def get_context(caller, action, form_values = [], filter = nil, used = [], chang

Context::ActionContext.new(self, caller, form_values, filter, used, change_field)
end

def nested_layout_element?(field)
field.type == 'Layout' && %w[Page Row].include?(field.component)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,22 @@ def initialize(scope:, form: nil, is_generate_file: false, description: nil, sub
end

def build_elements
@form = @form&.map do |field|
if field.key? :widget
build_widget(field)
elsif field[:type] == 'Layout'
build_layout_element(field)
else
DynamicField.new(**field)
end
end
@form = FormFactory.build_elements(form)
end

def static_form?
return form&.all?(&:static?) && form&.none? { |field| field.type == 'Layout' } if form

true
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)
if element.type == 'Layout'
if %w[Page Row].include?(element.component)
key = element.component == 'Page' ? :elements : :fields
validate_fields_ids(element.public_send(key), used)
end
else
if used.include?(element.id)
raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
Expand All @@ -38,69 +39,6 @@ def validate_fields_ids(form = @form, used = [])
end
end
end

def build_widget(field)
case field[:widget]
when 'AddressAutocomplete'
WidgetField::AddressAutocompleteField.new(**field)
when 'Checkbox'
WidgetField::CheckboxField.new(**field)
when 'CheckboxGroup'
WidgetField::CheckboxGroupField.new(**field)
when 'ColorPicker'
WidgetField::ColorPickerField.new(**field)
when 'CurrencyInput'
WidgetField::CurrencyInputField.new(**field)
when 'DatePicker'
WidgetField::DatePickerField.new(**field)
when 'Dropdown'
WidgetField::DropdownField.new(**field)
when 'FilePicker'
WidgetField::FilePickerField.new(**field)
when 'JsonEditor'
WidgetField::JsonEditorField.new(**field)
when 'NumberInput'
WidgetField::NumberInputField.new(**field)
when 'NumberInputList'
WidgetField::NumberInputListField.new(**field)
when 'RadioGroup'
WidgetField::RadioGroupField.new(**field)
when 'RichText'
WidgetField::RichTextField.new(**field)
when 'TextArea'
WidgetField::TextAreaField.new(**field)
when 'TextInput'
WidgetField::TextInputField.new(**field)
when 'TextInputList'
WidgetField::TextInputListField.new(**field)
when 'TimePicker'
WidgetField::TimePickerField.new(**field)
when 'UserDropdown'
WidgetField::UserDropdownField.new(**field)
else
raise ForestAdminDatasourceToolkit::Exceptions::ForestException, "Unknow widget type: #{field[:widget]}"
end
end

def build_layout_element(field)
case field[:component]
when 'Separator'
FormLayoutElement::SeparatorElement.new(**field)
when 'HtmlBlock'
FormLayoutElement::HtmlBlockElement.new(**field)
when 'Row'
FormLayoutElement::RowElement.new(**field)
else
raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
"Unknow component type: #{field[:component]}"
end
end

def static_form?
return form&.all?(&:static?) && form&.none? { |field| field.type == 'Layout' } if form

true
end
end
end
end
Expand Down
Loading