Skip to content

Commit

Permalink
feat(forms): Move the itemtype config field for QuestionTypeItem
Browse files Browse the repository at this point in the history
  • Loading branch information
ccailly authored Jan 14, 2025
1 parent 5098959 commit ec0e2a1
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 34 deletions.
3 changes: 3 additions & 0 deletions ajax/dropdownAllItems.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@
if (isset($_POST['specific_tags_items_id_dropdown'])) {
$p['specific_tags'] = $_POST['specific_tags_items_id_dropdown'];
}
if (isset($_POST['aria_label'])) {
$p['aria_label'] = $_POST['aria_label'];
}
$p['_idor_token'] = Session::getNewIDORToken($_POST["idtable"], $idor_params);

echo Html::jsAjaxDropdown(
Expand Down
78 changes: 78 additions & 0 deletions js/modules/Forms/EditorController.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export class GlpiFormEditorController
*/
#options;

/**
* Subtypes options for each question type
* @type {Object}
*/
#question_subtypes_options;

/**
* Create a new GlpiFormEditorController instance for the given target.
* The target must be a valid form.
Expand All @@ -83,6 +89,7 @@ export class GlpiFormEditorController
this.#defaultQuestionType = defaultQuestionType;
this.#templates = templates;
this.#options = {};
this.#question_subtypes_options = {};

// Validate target
if ($(this.#target).prop("tagName") != "FORM") {
Expand Down Expand Up @@ -252,6 +259,16 @@ export class GlpiFormEditorController
this.#options[type] = options;
}

/**
* Register new subtypes options for the given question type.
*
* @param {string} type Question type
* @param {Object} options Subtypes options for the question type
*/
registerQuestionSubTypesOptions(type, options) {
this.#question_subtypes_options[type] = options;
}

/**
* Handle backend response
*/
Expand Down Expand Up @@ -340,6 +357,13 @@ export class GlpiFormEditorController
);
break;

case "change-question-sub-type":
this.#changeQuestionSubType(
target.closest("[data-glpi-form-editor-question]"),
target.val()
);
break;

// Add a new section at the end of the form
case "add-section":
this.#addSection(
Expand Down Expand Up @@ -1296,9 +1320,63 @@ export class GlpiFormEditorController
extracted_default_value
);

// Update sub question types
if (this.#question_subtypes_options[type] !== undefined) {
const sub_types_select = question.find("[data-glpi-form-editor-question-sub-type-selector]");

// Show sub question type selector
sub_types_select.closest("div").removeClass("d-none");
sub_types_select.attr('disabled', false);

// Remove current sub types options
sub_types_select.find('optgroup, option').remove();

// Find sub types available for the new type
const new_sub_types = this.#question_subtypes_options[type].subtypes;

// Copy the new sub types options into the dropdown
for (const category in new_sub_types) {
const optgroup = $(`<optgroup label="${category}"></optgroup>`);
for (const [sub_type, label] of Object.entries(new_sub_types[category])) {
const option = $(`<option value="${sub_type}">${label}</option>`);
optgroup.append(option);
}
sub_types_select.append(optgroup);
}

// Set the default sub type
if (this.#question_subtypes_options[type].default_value) {
sub_types_select.val(this.#question_subtypes_options[type].default_value);
}

// Update the field name and aria-label
sub_types_select.attr("name", this.#question_subtypes_options[type].field_name);
sub_types_select.attr("aria-label", this.#question_subtypes_options[type].field_aria_label);

// Remove the "original-name" data attribute to avoid conflicts
sub_types_select.removeAttr("data-glpi-form-editor-original-name");

// Trigger sub type change
sub_types_select.trigger("change");
} else {
// Hide sub question type selector
question.find("[data-glpi-form-editor-question-sub-type-selector]")
.attr('disabled', true)
.closest("div").addClass("d-none");
}

$(document).trigger('glpi-form-editor-question-type-changed', [question, type]);
}

/**
* Handle the change of the sub type of the given question.
* @param {jQuery} question Question to update
* @param {string} sub_type New sub type
*/
#changeQuestionSubType(question, sub_type) {
$(document).trigger('glpi-form-editor-question-sub-type-changed', [question, sub_type]);
}

/**
* Add a new section at the end of the form.
* @param {jQuery} target Current position in the form
Expand Down
24 changes: 24 additions & 0 deletions js/modules/Forms/QuestionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,30 @@ export class GlpiFormQuestionTypeItem {
this.#updateItemsIdDropdownID(question_details);
}
});

$(document).on('glpi-form-editor-question-sub-type-changed', (event, question, sub_type) => {
if (question.find('[name="type"], [data-glpi-form-editor-original-name="type"]').val() !== this.#question_type) {
return;
}

const select = question.find('[data-glpi-form-editor-question-type-specific] select');
const container = select.parent();

// Add a flag to all children to mark them as to be removed
container.children().attr('data-to-remove', 'true');

// Load the new dropdown
container.load(
`${CFG_GLPI.root_doc}/ajax/dropdownAllItems.php`,
{
'idtable' : sub_type,
'width' : '100%',
'name' : select.data('glpi-form-editor-original-name') || select.attr('name'),
'aria_label': select.attr('aria-label'),
},
() => container.find('[data-to-remove]').remove()
);
});
}

#updateItemsIdDropdownID(question_details) {
Expand Down
24 changes: 24 additions & 0 deletions src/Glpi/Form/QuestionType/AbstractQuestionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,28 @@ public function getDefaultValueConfig(array $serialized_data): ?JsonFieldInterfa

return $config_class::jsonDeserialize($serialized_data);
}

#[Override]
public function getSubTypes(): array
{
return [];
}

#[Override]
public function getSubTypeFieldName(): string
{
return 'sub_type';
}

#[Override]
public function getSubTypeFieldAriaLabel(): string
{
return __('Question sub type');
}

#[Override]
public function getSubTypeDefaultValue(?Question $question): ?string
{
return '';
}
}
29 changes: 29 additions & 0 deletions src/Glpi/Form/QuestionType/QuestionTypeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,33 @@ public function getDefaultValueConfigClass(): ?string;
* @return ?JsonFieldInterface
*/
public function getDefaultValueConfig(array $serialized_data): ?JsonFieldInterface;

/**
* Retrieve the allowed sub-types for the question type
*
* @return array
*/
public function getSubTypes(): array;

/**
* Retrieve the sub-type field name for the question type
*
* @return string
*/
public function getSubTypeFieldName(): string;

/**
* Retrieve the sub-type field label for the question type
*
* @return string
*/
public function getSubTypeFieldAriaLabel(): string;

/**
* Retrieve the default value for the sub-type field
*
* @param Question|null $question The question to get the default value for
* @return null|string
*/
public function getSubTypeDefaultValue(?Question $question): ?string;
}
83 changes: 49 additions & 34 deletions src/Glpi/Form/QuestionType/QuestionTypeItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@

use CartridgeItem;
use ConsumableItem;
use Dropdown;
use Glpi\Application\View\TemplateRenderer;
use Glpi\Form\Question;
use Group;
use Line;
use Override;
use PassiveDCEquipment;
Expand Down Expand Up @@ -167,6 +167,30 @@ public function validateExtraDataInput(array $input): bool
return true;
}

#[Override]
public function getSubTypes(): array
{
return Dropdown::buildItemtypesDropdownOptions($this->getAllowedItemtypes());
}

#[Override]
public function getSubTypeFieldName(): string
{
return 'itemtype';
}

#[Override]
public function getSubTypeFieldAriaLabel(): string
{
return $this->itemtype_aria_label;
}

#[Override]
public function getSubTypeDefaultValue(?Question $question): ?string
{
return $this->getDefaultValueItemtype($question);
}

#[Override]
public function renderAdministrationTemplate(?Question $question): string
{
Expand All @@ -175,29 +199,21 @@ public function renderAdministrationTemplate(?Question $question): string
{% set rand = random() %}
{{ fields.dropdownItemsFromItemtypes(
{{ fields.dropdownField(
default_itemtype|default(itemtypes|first|first),
'default_value',
default_items_id,
'',
{
'init' : init,
'itemtypes' : itemtypes,
'no_label' : true,
'display_emptychoice' : true,
'default_itemtype' : default_itemtype,
'default_items_id' : default_items_id,
'itemtype_name' : 'itemtype',
'items_id_name' : 'default_value',
'width' : '100%',
'container_css_class' : 'mt-2',
'no_sort' : true,
'aria_label' : itemtype_aria_label,
'specific_tags_items_id_dropdown': {
'aria-label': items_id_aria_label,
},
'add_data_attributes_itemtype_dropdown' : {
'glpi-form-editor-specific-question-extra-data': '',
},
'mb' : '',
'init' : init,
'no_label' : true,
'display_emptychoice': true,
'width' : '100%',
'container_css_class': 'mt-2',
'mb' : '',
'comments' : false,
'addicon' : false,
'aria_label' : aria_label,
}
) }}
Expand All @@ -212,14 +228,13 @@ public function renderAdministrationTemplate(?Question $question): string

$twig = TemplateRenderer::getInstance();
return $twig->renderFromStringTemplate($template, [
'init' => $question != null,
'question' => $question,
'question_type' => $this::class,
'default_itemtype' => $this->getDefaultValueItemtype($question) ?? '0',
'default_items_id' => $this->getDefaultValueItemId($question),
'itemtypes' => $this->getAllowedItemtypes(),
'itemtype_aria_label' => $this->itemtype_aria_label,
'items_id_aria_label' => $this->items_id_aria_label,
'init' => $question != null,
'question' => $question,
'question_type' => $this::class,
'default_itemtype' => $this->getDefaultValueItemtype($question),
'default_items_id' => $this->getDefaultValueItemId($question),
'itemtypes' => $this->getAllowedItemtypes(),
'aria_label' => $this->items_id_aria_label,
]);
}

Expand Down Expand Up @@ -257,11 +272,11 @@ public function renderEndUserTemplate(Question $question): string

$twig = TemplateRenderer::getInstance();
return $twig->renderFromStringTemplate($template, [
'question' => $question,
'itemtype' => $this->getDefaultValueItemtype($question) ?? '0',
'default_items_id' => $this->getDefaultValueItemId($question),
'aria_label' => $this->items_id_aria_label,
'items_id_aria_label' => $this->items_id_aria_label,
'question' => $question,
'itemtype' => $this->getDefaultValueItemtype($question) ?? '0',
'default_items_id' => $this->getDefaultValueItemId($question),
'aria_label' => $this->items_id_aria_label,
'sub_types' => $this->getSubTypes(),
]);
}

Expand Down
12 changes: 12 additions & 0 deletions templates/pages/admin/form/form_editor.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,18 @@
'{{ get_class(question_type)|e('js') }}',
{{ question_type.getFormEditorJsOptions()|raw }}
);
{% if question_type.getSubTypes() is not empty %}
controller.registerQuestionSubTypesOptions(
'{{ get_class(question_type)|e('js') }}',
{
'subtypes' : {{ question_type.getSubTypes()|json_encode|raw }},
'default_value' : '{{ question_type.getSubTypeDefaultValue(null)|e('js') }}',
'field_name' : '{{ question_type.getSubTypeFieldName()|e('js') }}',
'field_aria_label': '{{ question_type.getSubTypeFieldAriaLabel()|e('js') }}',
}
)
{% endif %}
{% endfor %}
$(container_selector).data('controller', controller);
Expand Down
23 changes: 23 additions & 0 deletions templates/pages/admin/form/form_question.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,29 @@
}
) }}

{% set sub_types = question_type.getSubTypes() %}
{{ fields.dropdownArrayField(
question_type.getSubTypeFieldName(),
question_type.getSubTypeDefaultValue(question),
question_type.getSubTypes(),
'',
{
'init' : question is not null,
'no_label' : true,
'mb' : '',
'field_class' : 'me-2' ~ (sub_types is empty ? ' d-none' : ''),
'class' : 'form-select form-select-sm',
'width' : 'auto',
'disabled' : sub_types is empty,
'aria_label' : question_type.getSubTypeFieldAriaLabel(),
'add_data_attributes' : {
'glpi-form-editor-on-change' : 'change-question-sub-type',
'glpi-form-editor-question-sub-type-selector' : '',
'glpi-form-editor-specific-question-extra-data': ''
}
}
) }}

{# Render the specific question options #}
<div class="ms-auto" data-glpi-form-editor-specific-question-options data-glpi-form-editor-question-extra-details>
{{ question_type.renderAdministrationOptionsTemplate(question)|raw }}
Expand Down
Loading

0 comments on commit ec0e2a1

Please sign in to comment.