Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(forms): Move the itemtype config field for QuestionTypeItem #18681

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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 @@ -228,6 +235,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 @@ -316,6 +333,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 @@ -1208,9 +1232,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 @@ -389,6 +389,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 @@ -201,6 +201,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-2" data-glpi-form-editor-specific-question-options data-glpi-form-editor-question-extra-details>
{{ question_type.renderAdministrationOptionsTemplate(question)|raw }}
Expand Down
Loading
Loading