fix(champ): allow to submit when secondary options are empty

This commit is contained in:
Paul Chavard 2022-07-19 19:01:53 +02:00
parent 36df41c1e4
commit e85fe71887
6 changed files with 85 additions and 53 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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

View file

@ -4,6 +4,7 @@
{},
{ data: { secondary_options: champ.secondary_options }, required: champ.mandatory?, id: champ.input_id, aria: { describedby: champ.describedby_id } }
.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?
@ -13,4 +14,5 @@
= 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" } }
{ 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?