fix(champ): allow to submit when secondary options are empty
This commit is contained in:
parent
36df41c1e4
commit
e85fe71887
6 changed files with 85 additions and 53 deletions
|
@ -6,8 +6,11 @@ import {
|
|||
isTextInputElement,
|
||||
show,
|
||||
hide,
|
||||
enable,
|
||||
disable,
|
||||
getConfig
|
||||
} from '@utils';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ApplicationController } from './application_controller';
|
||||
import { AutoUpload } from '../shared/activestorage/auto-upload';
|
||||
|
@ -85,6 +88,7 @@ export class AutosaveController extends ApplicationController {
|
|||
isCheckboxOrRadioInputElement(target)
|
||||
) {
|
||||
this.toggleOtherInput(target);
|
||||
this.toggleLinkedSelect(target);
|
||||
this.enqueueAutosaveRequest();
|
||||
}
|
||||
}
|
||||
|
@ -219,4 +223,60 @@ export class AutosaveController extends ApplicationController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private toggleLinkedSelect(target: HTMLSelectElement | HTMLInputElement) {
|
||||
const secondaryOptions = target.dataset.secondaryOptions;
|
||||
if (isSelectElement(target) && secondaryOptions) {
|
||||
const parent = target.closest('.editable-champ-linked_drop_down_list');
|
||||
const secondary = parent?.querySelector<HTMLSelectElement>(
|
||||
'select[data-secondary]'
|
||||
);
|
||||
if (secondary) {
|
||||
const options = parseOptions(secondaryOptions);
|
||||
this.setSecondaryOptions(secondary, options[target.value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setSecondaryOptions(
|
||||
secondarySelectElement: HTMLSelectElement,
|
||||
options: string[]
|
||||
) {
|
||||
const wrapper = secondarySelectElement.closest('.secondary');
|
||||
const hidden = wrapper?.nextElementSibling as HTMLInputElement | null;
|
||||
|
||||
secondarySelectElement.innerHTML = '';
|
||||
|
||||
if (options.length) {
|
||||
disable(hidden);
|
||||
|
||||
if (secondarySelectElement.required) {
|
||||
secondarySelectElement.appendChild(makeOption(''));
|
||||
}
|
||||
for (const option of options) {
|
||||
secondarySelectElement.appendChild(makeOption(option));
|
||||
}
|
||||
|
||||
secondarySelectElement.selectedIndex = 0;
|
||||
enable(secondarySelectElement);
|
||||
show(wrapper);
|
||||
} else {
|
||||
hide(wrapper);
|
||||
disable(secondarySelectElement);
|
||||
enable(hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SecondaryOptions = z.record(z.string().array());
|
||||
|
||||
function parseOptions(options: string) {
|
||||
return SecondaryOptions.parse(JSON.parse(options));
|
||||
}
|
||||
|
||||
function makeOption(option: string) {
|
||||
const element = document.createElement('option');
|
||||
element.textContent = option;
|
||||
element.value = option;
|
||||
return element;
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ import '../new_design/procedure-form';
|
|||
import '../new_design/spinner';
|
||||
import '../new_design/support';
|
||||
|
||||
import '../new_design/champs/linked-drop-down-list';
|
||||
|
||||
import {
|
||||
toggleCondidentielExplanation,
|
||||
replaceSemicolonByComma
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
import { delegate } from '@utils';
|
||||
|
||||
const PRIMARY_SELECTOR = 'select[data-secondary-options]';
|
||||
const SECONDARY_SELECTOR = 'select[data-secondary]';
|
||||
const CHAMP_SELECTOR = '.editable-champ';
|
||||
|
||||
delegate('change', PRIMARY_SELECTOR, (evt) => {
|
||||
const primary = evt.target;
|
||||
const secondary = primary
|
||||
.closest(CHAMP_SELECTOR)
|
||||
.querySelector(SECONDARY_SELECTOR);
|
||||
const options = JSON.parse(primary.dataset.secondaryOptions);
|
||||
|
||||
selectOptions(secondary, options[primary.value]);
|
||||
});
|
||||
function makeOption(option) {
|
||||
let element = document.createElement('option');
|
||||
element.textContent = option;
|
||||
element.value = option;
|
||||
return element;
|
||||
}
|
||||
function selectOptions(selectElement, options) {
|
||||
selectElement.innerHTML = '';
|
||||
if (selectElement.required) {
|
||||
selectElement.appendChild(makeOption(''));
|
||||
}
|
||||
for (let option of options) {
|
||||
selectElement.appendChild(makeOption(option));
|
||||
}
|
||||
|
||||
selectElement.selectedIndex = 0;
|
||||
}
|
|
@ -70,15 +70,15 @@ export function getConfig() {
|
|||
return Gon.parse(window.gon);
|
||||
}
|
||||
|
||||
export function show(el: HTMLElement | null) {
|
||||
export function show(el: Element | null) {
|
||||
el?.classList.remove('hidden');
|
||||
}
|
||||
|
||||
export function hide(el: HTMLElement | null) {
|
||||
export function hide(el: Element | null) {
|
||||
el?.classList.add('hidden');
|
||||
}
|
||||
|
||||
export function toggle(el: HTMLElement | null, force?: boolean) {
|
||||
export function toggle(el: Element | null, force?: boolean) {
|
||||
if (force == undefined) {
|
||||
el?.classList.toggle('hidden');
|
||||
} else if (force) {
|
||||
|
@ -88,11 +88,15 @@ export function toggle(el: HTMLElement | null, force?: boolean) {
|
|||
}
|
||||
}
|
||||
|
||||
export function enable(el: HTMLInputElement | HTMLButtonElement | null) {
|
||||
export function enable(
|
||||
el: HTMLSelectElement | HTMLInputElement | HTMLButtonElement | null
|
||||
) {
|
||||
el && (el.disabled = false);
|
||||
}
|
||||
|
||||
export function disable(el: HTMLInputElement | HTMLButtonElement | null) {
|
||||
export function disable(
|
||||
el: HTMLSelectElement | HTMLInputElement | HTMLButtonElement | null
|
||||
) {
|
||||
el && (el.disabled = true);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,13 +83,13 @@ class Champs::LinkedDropDownListChamp < Champ
|
|||
[primary_value, secondary_value]
|
||||
end
|
||||
|
||||
def has_secondary_options_for_primary?
|
||||
primary_value.present? && secondary_options[primary_value]&.any?(&:present?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pack_value(primary, secondary)
|
||||
self.value = JSON.generate([primary, secondary])
|
||||
end
|
||||
|
||||
def has_secondary_options_for_primary?
|
||||
primary_value.present? && secondary_options[primary_value]&.any?(&:present?)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
{},
|
||||
{ data: { secondary_options: champ.secondary_options }, required: champ.mandatory?, id: champ.input_id, aria: { describedby: champ.describedby_id } }
|
||||
|
||||
= form.label :secondary_value, for: "#{champ.input_id}-secondary" do
|
||||
= champ.drop_down_secondary_libelle.presence || "Valeur secondaire dépendant de la première"
|
||||
- if champ.mandatory?
|
||||
%span.mandatory *
|
||||
- if champ.drop_down_secondary_description.present?
|
||||
.notice{ id: "#{champ.describedby_id}-secondary" }= string_to_html(champ.drop_down_secondary_description)
|
||||
= form.select :secondary_value,
|
||||
champ.secondary_options[champ.primary_value],
|
||||
{},
|
||||
{ data: { secondary: true }, required: champ.mandatory?, id: "#{champ.input_id}-secondary", aria: { describedby: "#{champ.describedby_id}-secondary" } }
|
||||
.secondary{ class: champ.has_secondary_options_for_primary? ? '' : 'hidden' }
|
||||
= form.label :secondary_value, for: "#{champ.input_id}-secondary" do
|
||||
= champ.drop_down_secondary_libelle.presence || "Valeur secondaire dépendant de la première"
|
||||
- if champ.mandatory?
|
||||
%span.mandatory *
|
||||
- if champ.drop_down_secondary_description.present?
|
||||
.notice{ id: "#{champ.describedby_id}-secondary" }= string_to_html(champ.drop_down_secondary_description)
|
||||
= form.select :secondary_value,
|
||||
champ.secondary_options[champ.primary_value],
|
||||
{},
|
||||
{ data: { secondary: true }, disabled: !champ.has_secondary_options_for_primary?, required: champ.mandatory?, id: "#{champ.input_id}-secondary", aria: { describedby: "#{champ.describedby_id}-secondary" } }
|
||||
= form.hidden_field :secondary_value, value: '', disabled: champ.has_secondary_options_for_primary?
|
||||
|
|
Loading…
Reference in a new issue