[Bps-public-commit] rt-extension-formtools branch dynamic-forms-from-config updated. 0.53-100-ga0acc98
BPS Git Server
git at git.bestpractical.com
Tue Nov 7 20:55:14 UTC 2023
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt-extension-formtools".
The branch, dynamic-forms-from-config has been updated
via a0acc982528c2977ecdd311b78baa65fe3a82359 (commit)
from f42ab019e8dc72643ea69bab14baa12148502802 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a0acc982528c2977ecdd311b78baa65fe3a82359
Author: sunnavy <sunnavy at bestpractical.com>
Date: Tue Nov 7 15:51:35 2023 -0500
Support client side dependent validation
Currently only "required" is supported.
diff --git a/html/Admin/FormTools/Modify.html b/html/Admin/FormTools/Modify.html
index 12747e9..98d22fc 100644
--- a/html/Admin/FormTools/Modify.html
+++ b/html/Admin/FormTools/Modify.html
@@ -138,8 +138,8 @@
</a>
</p>
</div>
- <div class="modal fade formtools-element-modal" id="formtools-element-<% CSSClass($item) %>-modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
+ <div class="modal fade formtools-element-modal" id="formtools-element-<% CSSClass($item) %>-modal" data-type="component" data-name="<% $item %>" tabindex="-1" role="dialog">
+ <div class="modal-dialog <% %dependent_custom_fields ? 'modal-lg' : '' %>" role="document">
<div class="modal-content">
<form class="formtools-element-form">
<div class="modal-header">
@@ -166,6 +166,37 @@
</label>
</div>
</&>
+% if ( %dependent_custom_fields ) {
+ <&| /Elements/LabeledValue, Label => '' &>
+ <div class="custom-control custom-checkbox">
+ <input class="custom-control-input" id="<% CSSClass($item) %>-depedent-validation" type="checkbox" name="dependent_validation" value="1" />
+ <label class="custom-control-label has-text-input" for="<% CSSClass($item) %>-depedent-validation">
+ <&|/l&>Show validation if</&>
+ <div class="d-inline-block">
+ <select name="dependent_name" class="form-control">
+ <option value=""><&|/l&>(no value)</&></option>
+% for my $name ( sort keys %dependent_custom_fields ) {
+ <option value="<% $name %>"><% $name %></option>
+% }
+ </select>
+ </div>
+ is
+ <div class="d-inline-block">
+ <select name="dependent_value" data-dependent-name="dependent_name" multiple class="form-control">
+ <option value=""><&|/l&>(no value)</&></option>
+% for my $name ( sort keys %dependent_custom_fields ) {
+ <optgroup label="<% $name %>">
+% for my $value ( @{$dependent_custom_fields{$name}} ) {
+ <option value="<% $value %>"><% $value %></option>
+% }
+ </optgroup>
+% }
+ </select>
+ </div>
+ </label>
+ </div>
+ </&>
+% }
<&| /Elements/LabeledValue, Label => '' &>
<div class="custom-control custom-checkbox">
<input class="custom-control-input" id="<% CSSClass($item) %>-hide" type="checkbox" name="hide" value="1" />
@@ -353,8 +384,8 @@
<div class="modal-wrapper">
% $i = 0;
% for my $item ( grep { $_->{type} ne 'hidden' || $_->{'input-name'} ne 'create_ticket' } @{$form->{'formtools-pages'}{$page_name}{content} || []} ) {
- <div class="modal fade formtools-element-modal" id="formtools-element-<% $form_page_id{$page_name} %>-<% $i %>-modal" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
+ <div class="modal fade formtools-element-modal" id="formtools-element-<% $form_page_id{$page_name} %>-<% $i %>-modal" tabindex="-1" role="dialog" data-type="<% $item->{type} %>" data-name="<% $item->{arguments} && $item->{arguments}{name} ? $item->{arguments}{name} : '' %>">
+ <div class="modal-dialog <% $item->{type} eq 'component' && %dependent_custom_fields && !RT::Extension::FormTools::is_core_field($item->{arguments}{name}) ? 'modal-lg' : '' %>" role="document">
<div class="modal-content">
<form class="formtools-element-form">
<div class="modal-header">
@@ -403,6 +434,37 @@
</label>
</div>
</&>
+% if ( %dependent_custom_fields ) {
+ <&| /Elements/LabeledValue, Label => '' &>
+ <div class="custom-control custom-checkbox">
+ <input class="custom-control-input" id="formtools-element-<% $form_page_id{$page_name} %>-<% $i %>-depedent-validation" type="checkbox" name="dependent_validation" value="1" <% $item->{arguments}{dependent_validation}{enabled} ? 'checked="checked"' : '' |n %> />
+ <label class="custom-control-label has-text-input" for="formtools-element-<% $form_page_id{$page_name} %>-<% $i %>-depedent-validation">
+ <&|/l&>Show validation if</&>
+ <div class="d-inline-block">
+ <select name="dependent_name" class="form-control selectpicker">
+ <option value=""><&|/l&>(no value)</&></option>
+% for my $name ( sort keys %dependent_custom_fields ) {
+ <option value="<% $name %>" <% ($item->{arguments}{dependent_validation}{name} // '') eq $name ? 'selected="selected"' : '' |n %>><% $name %></option>
+% }
+ </select>
+ </div>
+ is
+ <div class="d-inline-block">
+ <select name="dependent_value" data-dependent-name="dependent_name" multiple class="form-control selectpicker">
+ <option value=""><&|/l&>(no value)</&></option>
+% for my $name ( sort keys %dependent_custom_fields ) {
+ <optgroup label="<% $name %>">
+% for my $value ( @{$dependent_custom_fields{$name}} ) {
+ <option value="<% $value %>" <% ($item->{arguments}{dependent_validation}{name} // '') eq $name && grep( { $value eq $_ } @{$item->{arguments}{dependent_validation}{values} || []} ) ? 'selected="selected"' : '' |n %>><% $value %></option>
+% }
+ </optgroup>
+% }
+ </select>
+ </div>
+ </label>
+ </div>
+ </&>
+% }
% }
<&| /Elements/LabeledValue, Label => '' &>
@@ -472,6 +534,34 @@ jQuery(function() {
event.returnValue = true;
}
});
+
+ jQuery('[name=dependent_name]').change(function () {
+ const modal = jQuery(this).closest('.modal');
+ const target = modal.find('[data-dependent-name="' + jQuery(this).attr('name') + '"');
+ const name = jQuery(this).val();
+ target.find('optgroup').addClass('hidden');
+ target.find('optgroup[label="' + name + '"]').removeClass('hidden');
+ target.selectpicker('refresh');
+ // Hide duplicate dividers
+ target.closest('div').find('li.dropdown-divider').slice(1).hide();
+ });
+
+ jQuery('.formtools-element-modal').on('shown.bs.modal', function (e) {
+ const name = jQuery(this).attr('data-name');
+
+ const select = jQuery(this).find('select[name=dependent_name]');
+
+ select.find('option').addClass('hidden');
+ jQuery('.formtools-form-pages [data-type=component][data-name]').each(function () {
+ const available_name = jQuery(this).attr('data-name');
+ if ( available_name !== name ) {
+ select.find('option[value="' + available_name + '"]').removeClass('hidden');
+ }
+ });
+
+ select.change();
+ select.selectpicker('refresh');
+ });
});
</script>
<%INIT>
@@ -579,6 +669,7 @@ my $cfs = $queue->TicketCustomFields;
my @custom_fields;
my %default_values;
my %tooltips;
+my %dependent_custom_fields;
while ( my $cf = $cfs->Next ) {
push @custom_fields, $cf->Name;
@@ -588,6 +679,10 @@ while ( my $cf = $cfs->Next ) {
}
}
$tooltips{$cf->Name} = $cf->EntryHint // '';
+
+ if ( $cf->Type eq 'Select' ) {
+ $dependent_custom_fields{$cf->Name} = [ map { $_->Name } @{$cf->Values->ItemsArrayRef || {}} ];
+ }
}
my %other_components = (
diff --git a/html/FormTools/Field b/html/FormTools/Field
index 91cbccb..98127eb 100644
--- a/html/FormTools/Field
+++ b/html/FormTools/Field
@@ -145,8 +145,24 @@ $default = '' unless defined $default;
% if ($render_as ne 'hidden' && $show_label) { # no label if hidden
<&| /Elements/LabeledValue, RawLabel => $m->interp->apply_escapes($field_label, 'h') . $after_label, LabelSpanClass => $tooltip ? 'prev-icon-helper' : '', LabelTooltip => $tooltip &>
- <& SELF:Value, %ARGS, input_name => $input_name, field_type => $field_type, default => $default, values => \@values, cf => $cf &>
- <% $after_input |n %>
+ <div data-name="<% $cf ? $cf->Name : '' %>"
+% if ( $ARGS{dependent_validation} && $ARGS{dependent_validation}{enabled} && $ARGS{dependent_validation}{name} ) {
+% my $dependent_cf = RT::CustomField->new($session{CurrentUser});
+% $dependent_cf = RT::CustomField->new( $session{'CurrentUser'} );
+% $dependent_cf->SetContextObject($ticket) if $cf->can('SetContextObject');
+
+% # try loading CFs for this Queue, followed by Global, followed by any CF of given $name
+% $dependent_cf->LoadByName( Name => $ARGS{dependent_validation}{name}, Queue => $queue->id ) if (defined $queue);
+% $dependent_cf->LoadByName( Name => $ARGS{dependent_validation}{name}, Queue => 0 ) unless ( $cf->id );
+% if ( $dependent_cf ) {
+ data-dependent-name="<% GetCustomFieldInputName( Object => $ticket, CustomField => $dependent_cf ) %>"
+ data-dependent-values="<% JSON($ARGS{dependent_validation}{values} || []) %>"
+% }
+% }
+ >
+ <& SELF:Value, %ARGS, input_name => $input_name, field_type => $field_type, default => $default, values => \@values, cf => $cf &>
+ <% $after_input |n %>
+ </div>
</&>
% } else {
diff --git a/html/Forms/dhandler b/html/Forms/dhandler
index 7b88305..da768ba 100644
--- a/html/Forms/dhandler
+++ b/html/Forms/dhandler
@@ -35,6 +35,61 @@ foreach my $element ( @{$form_config->{'formtools-pages'}{$page}{'content'}} ) {
% if ( $form_config->{'formtools-pages'}{$page}{'next'} ) {
<& /FormTools/Next, Label => $button_label, Back => $show_back, Name => 'Submit' &>
% }
+
+
+<script type="text/javascript">
+jQuery(function () {
+ jQuery('[data-dependent-name]').each(function () {
+ const dependent_name = jQuery(this).attr('data-dependent-name');
+ const values = JSON.parse(jQuery(this).attr('data-dependent-values'));
+ const target = jQuery(this);
+ const syncValidation = function(source) {
+ if ( source.target ) {
+ source = jQuery(source.target);
+ }
+
+ let source_values = [];
+ const name = source.attr('name');
+ if ( source.is(':checkbox') || source.is(':radio') ) {
+ jQuery(':input[name="' + name +'"]:checked').each( function () {
+ source_values.push(source.val());
+ });
+ }
+ else {
+ jQuery(':input[name="' + name +'"]').each(function () {
+ const val = jQuery(this).val();
+ if ( Array.isArray(val) ) {
+ source_values.push(...val);
+ }
+ else {
+ source_values.push(val);
+ }
+ });
+ }
+ let matched;
+ for ( let source_value of source_values ) {
+ if ( values.includes(source_value) ) {
+ matched = true;
+ }
+ }
+
+ if ( matched ) {
+ target.find(':input:visible').attr('required', true);
+ }
+ else {
+ target.find(':input:visible').attr('required', false);
+ }
+ };
+ jQuery(':input[type!=hidden][name="' + dependent_name +'"]').change(syncValidation).change();
+
+ // Handle the case where dependent input is on previous pages
+ if ( jQuery(':input[type=hidden][name="' + dependent_name +'"]').length ) {
+ syncValidation(jQuery(':input[type=hidden][name="' + dependent_name +'"]'));
+ }
+ });
+});
+</script>
+
</&>
<%init>
diff --git a/static/css/rt-extension-formtools.css b/static/css/rt-extension-formtools.css
index 27152c7..33f4132 100644
--- a/static/css/rt-extension-formtools.css
+++ b/static/css/rt-extension-formtools.css
@@ -80,3 +80,8 @@ div .formtools-admin-description {
.formtools-element-empty-room {
height: 200px;
}
+
+#formtools-edit .custom-control-label.has-text-input::before,
+#formtools-edit .custom-control-label.has-text-input::after {
+ top: 0.5rem;
+}
diff --git a/static/js/rt-extension-formtools.js b/static/js/rt-extension-formtools.js
index 9391d31..3a38734 100644
--- a/static/js/rt-extension-formtools.js
+++ b/static/js/rt-extension-formtools.js
@@ -148,6 +148,30 @@ formTools = {
}
}
+
+ const dependent_validation = form.find(':input[name=dependent_validation]');
+ if ( dependent_validation.length ) {
+ value.arguments.dependent_validation ||= {};
+ if ( dependent_validation.is(':checked') ) {
+ value.arguments.dependent_validation.enabled = 1;
+ }
+ else {
+ value.arguments.dependent_validation.enabled = 0;
+ }
+ }
+
+ const dependent_name = form.find(':input[name=dependent_name]');
+ if ( dependent_name.length ) {
+ value.arguments.dependent_validation ||= {};
+ value.arguments.dependent_validation.name = dependent_name.val();
+ }
+
+ const dependent_value = form.find(':input[name=dependent_value]');
+ if ( dependent_value.length ) {
+ value.arguments.dependent_validation ||= {};
+ value.arguments.dependent_validation.values = dependent_value.val();
+ }
+
const hide = form.find(':input[name=hide]');
if ( hide.length ) {
if ( hide.is(':checked') ) {
-----------------------------------------------------------------------
Summary of changes:
html/Admin/FormTools/Modify.html | 103 ++++++++++++++++++++++++++++++++--
html/FormTools/Field | 20 ++++++-
html/Forms/dhandler | 55 ++++++++++++++++++
static/css/rt-extension-formtools.css | 5 ++
static/js/rt-extension-formtools.js | 24 ++++++++
5 files changed, 201 insertions(+), 6 deletions(-)
hooks/post-receive
--
rt-extension-formtools
More information about the Bps-public-commit
mailing list