diff --git a/components/ILIAS/UI/UI.php b/components/ILIAS/UI/UI.php index dfe29b391ff3..81b294eafe52 100644 --- a/components/ILIAS/UI/UI.php +++ b/components/ILIAS/UI/UI.php @@ -53,7 +53,9 @@ public function init( $contribute[Component\Resource\PublicAsset::class] = fn() => new Component\Resource\ComponentJS($this, "js/Input/Container/dist/filter.js"); $contribute[Component\Resource\PublicAsset::class] = fn() => - new Component\Resource\ComponentJS($this, "js/Input/Field/dist/input.factory.min.js"); + new Component\Resource\ComponentJS($this, "js/Input/Container/dist/container.min.js"); + $contribute[Component\Resource\PublicAsset::class] = fn() => + new Component\Resource\ComponentJS($this, "js/Input/Field/dist/input.factory.min.js"); $contribute[Component\Resource\PublicAsset::class] = fn() => new Component\Resource\ComponentJS($this, "js/Input/Field/dynamic_inputs_renderer.js"); $contribute[Component\Resource\PublicAsset::class] = fn() => diff --git a/components/ILIAS/UI/resources/js/Input/Container/dist/container.min.js b/components/ILIAS/UI/resources/js/Input/Container/dist/container.min.js new file mode 100644 index 000000000000..faad9c5e2555 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/dist/container.min.js @@ -0,0 +1,15 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ +!function(t){"use strict";function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t);class r{#t;#e;#n;#r;#i;#s;constructor(t,e,n){this.#t=t,this.#e=e,this.#n=n,this.#r=[],this.#i=[]}getName(){return this.#e.split("/").pop()}getFullName(){return this.#e}getLabel(){return this.#n}getType(){return this.#t}addChildNode(t){this.#r.push(t)}getAllChildren(){return this.#r}getChildByName(t){const e=Array.from(this.#r).filter((e=>e.getName()===t));return 0===e.length?null:e.shift()}addHtmlField(t){this.#i.push(t)}getHtmlFields(){return this.#i}getValues(){const t=[];return this.#i.forEach((e=>{"checkbox"===e.type||"radio"===e.type?e.checked&&t.push(e.value):t.push(e.value)})),t}setTransforms(t){this.#s=t}getChildren(){return this.#s&&this.#s.childrenTransform?this.#s.childrenTransform(this):this.getAllChildren()}getValuesRepresentation(){return this.#s&&this.#s.valueTransform?this.#s.valueTransform(this):this.getValues()}}const i="data-il-ui-component",s="data-il-ui-input-name",l=`fieldset[${i}][ ${s}]`,a=".c-input__field";class o{#s;#l;#a;constructor(t,e){this.#s=t,this.#l=e,this.#a=new r("form","FormContainerInput",e.getAttribute("id")),Array.from(e.querySelectorAll(l)).filter((t=>t.parentNode===t.closest("form"))).forEach((t=>this.#o(this.#a,t)))}#o(t,e){const n=this.#d(e);t.addChildNode(n);const r=Array.from(e.querySelectorAll(`${a} ${l}`)).filter((t=>t.closest(a)===e.querySelector(a)));r.length>0&&r.forEach((t=>this.#o(n,t)))}#d(t){const e=t.getAttribute(i),n=t.getAttribute(s),a=t.querySelector("label").textContent.trim(),o=new r(e,n,a);return o.setTransforms(this.#u(e)),Array.from(t.querySelectorAll("[name]")).filter((e=>e.closest(l)===t)).forEach((t=>o.addHtmlField(t))),o}#u(t){return this.#s[t]?this.#s[t]:null}getNodes(){return this.#a}getNodeByName(t,e){let n=e;n||(n=this.#a);return t.split("/").slice(1).forEach((t=>(n=n.getChildByName(t),n||null))),n}getValuesRepresentation(t,e,n){let r=t;r||(r=this.getNodes());let i=e;i||(i=0);let s=n;s||(s=[]);const l={label:r.getLabel(),value:r.getValuesRepresentation(),indent:i,type:r.getType()};return s.push(l),r.getChildren().forEach((t=>this.getValuesRepresentation(t,i+1,s))),s}getNodesFlat(t,e){let n=e,r=t;n||(n=[],r=this.#a),n.push(r);const i=r.getChildren();return i.length>0&&i.forEach((t=>this.getNodesFlat(t,n))),n}}const d={"switchable-group-field-input":new class{valueTransform(t){const e=t.getChildren();if(0===e.length)return[];const n=[];return e.forEach((t=>n.push(t.getLabel()))),n}childrenTransform(t){return t.getAllChildren().filter((t=>t.getHtmlFields()[0].checked))}},"optional-roup-field-input":new class{childrenTransform(t){return t.getHtmlFields()[0].checked?t.getAllChildren():[]}},"radio-field-input":new class{valueTransform(t){const e=t.getHtmlFields().filter((t=>t.checked));if(0===e.length)return[];const n=[];return e.forEach((t=>n.push(t.nextElementSibling.textContent))),n}},"multiSelect-field-input":new class{valueTransform(t){const e=t.getHtmlFields().filter((t=>t.checked));if(0===e.length)return[];const n=[];return e.forEach((t=>n.push(t.parentNode.textContent))),n}},"password-field-input":new class{valueTransform(t){return null}},"duration-field-input":new class{valueTransform(t){const[e,n]=t.getAllChildren().map((t=>t.getValues()[0]));return e&&n?[`${e} - ${n}`]:["-"]}childrenTransform(t){return[]}},"link-field-input":new class{valueTransform(t){const[e,n]=t.getAllChildren().map((t=>t.getValues()[0]));return[`${e} [${n}]`]}},"select-field-input":new class{valueTransform(t){const e=t.getHtmlFields()[0];return[e.options[e.options.selectedIndex].text]}}};il=il||{},il.UI=il.UI||{},il.UI.filter=function(t){t((function(){t("div.il-filter").each((function(){var n=this,i=t(n).find(".il-standard-form"),l=0,o=1;i.attr("action",window.location.pathname),t(n).find(".il-filter-field-status").each((function(){$hidden_input=this,"0"===t($hidden_input).val()?t(t("div.il-filter .il-popover-container")[l]).hide():t(t("div.il-filter .il-filter-add-list li")[l]).hide(),l++})),t(".il-filter-bar-opener").find("button:first").hide(),t(".il-filter-bar-opener button").click((function(){t(".il-filter-bar-opener button").toggle(),"false"==t(this).attr("aria-expanded")?t(this).attr("aria-expanded","true"):t(this).attr("aria-expanded","false")}));var d=!1;t(n).find(".il-popover-container").each((function(){var n=t(this).find(":input:not(:button)"),r=n.val(),i=t(this).find(".leftaddon").text(),s=e(n,r);""!==s?(t(".il-filter-inputs-active").find("span[id='"+o+"']").html(i+": "+s),d=!0):t(".il-filter-inputs-active").find("span[id='"+o+"']").hide(),o++})),d||t(".il-filter-inputs-active").hide(),t(".input-group .btn.btn-bulky").attr("data-placement","bottom");var u=!0;t(n).find(".il-filter-add-list").find("li").each((function(){"none"!==t(this).css("display")&&"hidden"!==t(this).css("visibility")&&(u=!1)})),u&&t(".btn-bulky").parents(".il-popover-container").hide(),i.on("keydown",":input:not(:button)",(function(e){if(13===e.which){var n=a(i.attr("data-cmd-apply")),s=n.query_params;r(t(this),s),i.attr("action",n.path),i.submit(),e.preventDefault()}})),t(".il-filter-field").keydown((function(e){var n=e.which;if(13===n||32===n){t(this).click();var r=s(t(this));0!=r.length&&r[0].focus(),e.preventDefault()}}))}))}));var e=function(e,n){var r="";if(e.is(":checkbox")){var i=[];e.each((function(){t(this).prop("checked")&&i.push(t(this).parent().find("span").text())})),0!=i.length&&(active_checkboxes=i.join(", "),r=active_checkboxes)}else if(e.is("select")&&""!==n){r=e.find("option:selected").text()}else void 0!==n&&""!==n&&(r=n);return r},n=function(e,n,r){t(e.parents(".il-filter").find(".il-filter-field-status").get(n)).val(r)},r=function(t,e){for(var n in e){var r='';t.parents("form").find(".il-filter-bar").before(r)}},i=function(t){return t.parents(".il-popover-container").find(":input")},s=function(t){return t.parents(".il-popover-container").find(".il-standard-popover-content").children().children().find("input")},l=function(t){return t.parents(".il-standard-form").find(".btn-bulky").parents(".il-popover-container")};function a(t){for(var e=["source","scheme","authority","userInfo","user","pass","host","port","relative","path","directory","file","query","fragment"],n=/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(t),r={},i=14;i--;)n[i]&&(r[e[i]]=n[i]);return r.query_params={},(r[e[12]]||"").replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(t,e,n){e&&(r.query_params[e]=n)})),delete r.source,r}return{onInputUpdate:function(e,n){let r;var i=t(n.triggerer[0]).parents(".il-popover").attr("id");r=i?document.querySelector("span[data-target='"+i+"']"):n.triggerer[0].closest(".input-group").querySelector("span.il-filter-field"),r&&(r.innerText=n.options.string_value)},onRemoveClick:function(e,r){var a=t("#"+r),o=a.parents(".il-popover-container").index();n(a,o,"0"),a.parents(".il-popover-container").hide(),i(a).val("");var d=s(a);d.each((function(){t(this).prop("checked",!1)})),d.parents(".il-popover-container").find(".il-filter-field").html("");var u=a.parents(".input-group").find(".input-group-addon.leftaddon").html(),c=function(e,n){return e.parents(".il-standard-form").find(".btn-link").filter((function(){return t(this).text()===n})).parents("li")}(a,u);c.show();var f=l(a);0!=a.parents(".il-standard-form").find(".il-popover-container:hidden").length&&f.show()},onAddClick:function(e,r){var a=t("#"+r),o=a.text();a.parent().hide();var d=a.parent().index();n(a,d,"1");var u=function(e,n){return e.parents(".il-standard-form").find(".input-group-addon.leftaddon").filter((function(){return t(this).text()===n}))}(a,o);u.parents(".il-popover-container").show(),i(u).focus(),u.parent().find(".il-filter-field").click();var c=s(u);0!=c.length&&c[0].focus();var f=l(a);0===a.parents(".il-filter").find(".il-filter-add-list").find("li:visible").length&&f.hide(),f.find(".il-popover").hide()},onCmd:function(e,n,i){var s=t("#"+n),l=a(s.parents("form").attr("data-cmd-"+i)),o=l.query_params;r(s,o),s.parents("form").attr("action",l.path),s.parents("form").submit()},onAjaxCmd:function(e,n,r){var i=t("#"+n),s=i.parents("form").attr("data-cmd-"+r),l=i.parents("form").serialize();t.ajax({type:"GET",url:s+"&"+l})}}}(n.default),il.UI.Input=il.UI.Input||{},il.UI.Input.Container=new class{#c=[];#s;constructor(t){this.#s=t}init(t){const e=`#${t}`,n=document.querySelector(e);if(void 0!==this.#c[t])throw new Error(`Container with id '${t}' has already been registered.`);this.#c[t]=new o(this.#s,n)}get(t){return this.#c[t]}getAll(){return this.#c}first(){return this.#c[Object.keys(this.#c).shift()]}}(d)}($); diff --git a/components/ILIAS/UI/resources/js/Input/Container/rollup.config.js b/components/ILIAS/UI/resources/js/Input/Container/rollup.config.js index fd0b89b811e5..bf1639971aa6 100755 --- a/components/ILIAS/UI/resources/js/Input/Container/rollup.config.js +++ b/components/ILIAS/UI/resources/js/Input/Container/rollup.config.js @@ -1,7 +1,38 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +import terser from '@rollup/plugin-terser'; +import copyright from '../../../../../../../scripts/Copyright-Checker/copyright'; +import preserveCopyright from '../../../../../../../scripts/Copyright-Checker/preserveCopyright'; + export default { - input: './src/filter.js', + input: './src/container.js', output: { - file: './dist/filter.js', - format: 'es' - } -}; \ No newline at end of file + file: './dist/container.min.js', + format: 'iife', + banner: copyright, + plugins: [ + terser({ + format: { + comments: preserveCopyright, + }, + }), + ], + globals: { + jquery: '$', + }, + }, + external: ['jquery'], +}; diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/container.class.js b/components/ILIAS/UI/resources/js/Input/Container/src/container.class.js new file mode 100644 index 000000000000..3e9752ddfda0 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/container.class.js @@ -0,0 +1,221 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +import FormNode from './formnode.class.js'; + +/** + * The attribute marks a DOM node as UIForm node and gives the type of the + * Component yielding this node. + * @type {string} + */ +const FIELD_ATTRIBUTE_TYPE = 'data-il-ui-component'; + +/** + * The attribute marks a DOM node as UIForm node and gives the name of the + * Node as provided by the Namesource + * @type {string} + */ +const FIELD_ATTRIBUTE_NAME = 'data-il-ui-input-name'; + +/** + * @type {string} + */ +const SEARCH_FIELD = `fieldset[${FIELD_ATTRIBUTE_TYPE}][ ${FIELD_ATTRIBUTE_NAME}]`; + +/** + * @type {string} + */ +const FIELD_INPUT_AREA = '.c-input__field'; + +/** + * @type {string} + */ +const SEARCH_INPUT = '[name]'; + +export default class Container { +/** + * A collection of final processing functions + * to present values for specific Input types. + * Functions are called with FormNode as single parameter. + * @name valueRepresentation + * @function + * @param {FormNode} node + * @return {Array} + * + * + * @type {Object} + */ + #transforms; + + /** + * @type {HTMLElement} + */ + #container; + + /** + * @type {FormNode} + */ + #nodes; + + /** + * @param {Object} transforms + * @param {HTMLElement} container + * @return {void} + */ + constructor(transforms, container) { + this.#transforms = transforms; + this.#container = container; + this.#nodes = new FormNode('form', 'FormContainerInput', container.getAttribute('id')); + + Array.from(container.querySelectorAll(SEARCH_FIELD)) + .filter((domFieldNode) => domFieldNode.parentNode === domFieldNode.closest('form')) + .forEach((domFieldNode) => this.#register(this.#nodes, domFieldNode)); + } + + /** + * @param {FormNode} pointer + * @param {HTMLElement} domFieldNode + * @return {void} + */ + #register(current, domFieldNode) { + const node = this.#buildNode(domFieldNode); + current.addChildNode(node); + + const furtherChildren = Array.from(domFieldNode.querySelectorAll(`${FIELD_INPUT_AREA} ${SEARCH_FIELD}`)) + .filter( + (cn) => cn.closest(FIELD_INPUT_AREA) === domFieldNode.querySelector(FIELD_INPUT_AREA), + ); + if (furtherChildren.length > 0) { + furtherChildren.forEach((domChildFieldNode) => this.#register(node, domChildFieldNode)); + } + } + + #buildNode(domFieldNode) { + const type = domFieldNode.getAttribute(FIELD_ATTRIBUTE_TYPE); + const name = domFieldNode.getAttribute(FIELD_ATTRIBUTE_NAME); + const label = domFieldNode.querySelector('label').textContent.trim(); + + const node = new FormNode(type, name, label); + node.setTransforms(this.#getTransformsFor(type)); + + Array.from(domFieldNode.querySelectorAll(SEARCH_INPUT)) + .filter( + (input) => input.closest(SEARCH_FIELD) === domFieldNode, + ) + .forEach((input) => node.addHtmlField(input)); + + return node; + } + + /** + * @param {string} type + * @return {valueRepresentation} + */ + #getTransformsFor(type) { + if (this.#transforms[type]) { + return this.#transforms[type]; + } + return null; + } + + /** + * @return {FormNode} + */ + getNodes() { + return this.#nodes; + } + + /** + * @param {string} name + * @param {?FormNode} initEntry + * @return {FormNode} + */ + getNodeByName(name, initEntry) { + let entry = initEntry; + if (!entry) { + entry = this.#nodes; + } + + const parts = name.split('/').slice(1); + parts.forEach( + (part) => { + entry = entry.getChildByName(part); + if (!entry) { + return null; + } + return entry; + }, + ); + return entry; + } + + /** + * @param {?FormNode} initNode + * @param {?number} initIndent + * @param {?Array} initOut + * @return {Array} + */ + getValuesRepresentation(initNode, initIndent, initOut) { + let node = initNode; + if (!node) { + node = this.getNodes(); + } + + let indent = initIndent; + if (!indent) { + indent = 0; + } + let out = initOut; + if (!out) { + out = []; + } + + const entry = { + label: node.getLabel(), + value: node.getValuesRepresentation(), + indent, + type: node.getType(), + }; + out.push(entry); + + node.getChildren().forEach( + (child) => this.getValuesRepresentation(child, indent + 1, out), + ); + return out; + } + + /** + * @param {?FormNode} initNode + * @param {?FormNode[]} initOut + * @return {FormNode[]} + */ + getNodesFlat(initNode, initOut) { + let out = initOut; + let node = initNode; + if (!out) { + out = []; + node = this.#nodes; + } + + out.push(node); + const children = node.getChildren(); + if (children.length > 0) { + children.forEach( + (child) => this.getNodesFlat(child, out), + ); + } + return out; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/container.factory.js b/components/ILIAS/UI/resources/js/Input/Container/src/container.factory.js new file mode 100644 index 000000000000..cc9441928733 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/container.factory.js @@ -0,0 +1,64 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +import Container from './container.class.js'; + +export default class ContainerFactory { + /** + * @type {Array} + */ + #instances = []; + + /** + * @type {Object} + */ + #transforms; + + /** + * @param {Object} transforms + */ + constructor(transforms) { + this.#transforms = transforms; + } + + /** + * @param {string} componentId + * @return {void} + */ + init(componentId) { + const search = `#${componentId}`; + const component = document.querySelector(search); + if (this.#instances[componentId] !== undefined) { + throw new Error(`Container with id '${componentId}' has already been registered.`); + } + this.#instances[componentId] = new Container(this.#transforms, component); + } + + /** + * @param {string} componentId + * @return {Container} + */ + get(componentId) { + return this.#instances[componentId]; + } + + getAll() { + return this.#instances; + } + + first() { + return this.#instances[Object.keys(this.#instances).shift()]; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/container.js b/components/ILIAS/UI/resources/js/Input/Container/src/container.js new file mode 100644 index 000000000000..5168978ffb7d --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/container.js @@ -0,0 +1,45 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +import $ from 'jquery'; + +import SwitchableGroupTransforms from './transforms/switchablegroup.transform'; +import OptionalGroupTransforms from './transforms/optionalgroup.transform'; +import RadioTransforms from './transforms/radio.transform'; +import PasswordTransforms from './transforms/password.transform'; +import DurationTransforms from './transforms/duration.transform'; +import LinkTransforms from './transforms/link.transform'; +import SelectTransforms from './transforms/select.transform'; +import MultiSelectTransforms from './transforms/multiselect.transform'; + +import filter from './filter.main'; +import ContainerFactory from './container.factory'; + +const transforms = { + 'switchable-group-field-input': new SwitchableGroupTransforms(), + 'optional-roup-field-input': new OptionalGroupTransforms(), + 'radio-field-input': new RadioTransforms(), + 'multiSelect-field-input': new MultiSelectTransforms(), + 'password-field-input': new PasswordTransforms(), + 'duration-field-input': new DurationTransforms(), + 'link-field-input': new LinkTransforms(), + 'select-field-input': new SelectTransforms(), +}; + +il = il || {}; +il.UI = il.UI || {}; +il.UI.filter = filter($); +il.UI.Input = il.UI.Input || {}; +il.UI.Input.Container = new ContainerFactory(transforms); diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/formnode.class.js b/components/ILIAS/UI/resources/js/Input/Container/src/formnode.class.js new file mode 100644 index 000000000000..8c5305d07100 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/formnode.class.js @@ -0,0 +1,178 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class FormNode { + /** + * @type {string} + */ + #type; + + /** + * @type {string} + */ + #name; + + /** + * @type {string} + */ + #label; + + /** + * @type {FormNode[]} + */ + #children; + + /** + * @type {HTMLElement[]} + */ + #htmlFields; + + #transforms; + + /** + * @param {string} type + * @param {string} name + * @param {string} label + * @return {void} + */ + constructor(type, name, label) { + this.#type = type; + this.#name = name; + this.#label = label; + + this.#children = []; + this.#htmlFields = []; + } + + /** + * @return {string} + */ + getName() { + return this.#name.split('/').pop(); + } + + /** + * @return {string} + */ + getFullName() { + return this.#name; + } + + /** + * @return {string} + */ + getLabel() { + return this.#label; + } + + /** + * @return {string} + */ + getType() { + return this.#type; + } + + /** + * @param {FormNode} node + * @return {void} + */ + addChildNode(node) { + this.#children.push(node); + } + + /** + * @return {FormNode[]} + */ + getAllChildren() { + return this.#children; + } + + /** + * @param {string} name + * @return {FormNode|null} + */ + getChildByName(name) { + const filtered = Array.from(this.#children) + .filter( + (child) => child.getName() === name, + ); + if (filtered.length === 0) { + return null; + } + return filtered.shift(); + } + + /** + * @param {HTMLElement} htmlField + * @return {void} + */ + addHtmlField(htmlField) { + this.#htmlFields.push(htmlField); + } + + /** + * @return {HTMLElement[]} + */ + getHtmlFields() { + return this.#htmlFields; + } + + /** + * @return {Array} + */ + getValues() { + const values = []; + + this.#htmlFields.forEach( + (htmlField) => { + if (htmlField.type === 'checkbox' || htmlField.type === 'radio') { + if (htmlField.checked) { + values.push(htmlField.value); + } + } else { + values.push(htmlField.value); + } + }, + ); + return values; + } + + /** + * @param {Class} transforms + */ + setTransforms(transforms) { + this.#transforms = transforms; + } + + /** + * @return {FormNode[]} + */ + getChildren() { + if (this.#transforms && this.#transforms.childrenTransform) { + return this.#transforms.childrenTransform(this); + } + return this.getAllChildren(); + } + + /** + * @return {Array} + */ + getValuesRepresentation() { + if (this.#transforms && this.#transforms.valueTransform) { + return this.#transforms.valueTransform(this); + } + return this.getValues(); + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/duration.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/duration.transform.js new file mode 100644 index 000000000000..1f55134c485b --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/duration.transform.js @@ -0,0 +1,36 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class DurationTransforms { + /** + * @param {FormNode} node + * @return {Array} + */ + valueTransform(node) { + const [start, end] = node.getAllChildren().map((child) => child.getValues()[0]); + if (start && end) { + return [`${start} - ${end}`]; + } + return ['-']; + } + + /** + * @param {FormNode} node + * @return {Array} + */ + childrenTransform(node) { + return []; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/link.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/link.transform.js new file mode 100644 index 000000000000..b9e2975ee0fa --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/link.transform.js @@ -0,0 +1,25 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class LinkTransforms { + /** + * @param {FormNode} node + * @return {Array} + */ + valueTransform(node) { + const [label, url] = node.getAllChildren().map((child) => child.getValues()[0]); + return [`${label} [${url}]`]; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/multiselect.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/multiselect.transform.js new file mode 100644 index 000000000000..beab191cea69 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/multiselect.transform.js @@ -0,0 +1,32 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class MultiSelectTransforms { + /** + * @param {FormNode} node + * @return {Array} + */ + valueTransform(node) { + const checked = node.getHtmlFields().filter((element) => element.checked); + if (checked.length === 0) { + return []; + } + const representation = []; + checked.forEach( + (field) => representation.push(field.parentNode.textContent), + ); + return representation; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/optionalgroup.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/optionalgroup.transform.js new file mode 100644 index 000000000000..38bed2473338 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/optionalgroup.transform.js @@ -0,0 +1,28 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class OptionalGroupTransforms { + /** + * @param {FormNode} node + * @return {Array} + */ + childrenTransform(node) { + if (!node.getHtmlFields()[0].checked + ) { + return []; + } + return node.getAllChildren(); + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/password.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/password.transform.js new file mode 100644 index 000000000000..1a894e623227 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/password.transform.js @@ -0,0 +1,24 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class SwitchableGroupTransforms { + /** + * @param {FormNode} node + * @return {null} + */ + valueTransform(node) { + return null; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/radio.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/radio.transform.js new file mode 100644 index 000000000000..27ae3248d12f --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/radio.transform.js @@ -0,0 +1,32 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class RadioTransforms { + /** + * @param {FormNode} node + * @return {Array} + */ + valueTransform(node) { + const checked = node.getHtmlFields().filter((element) => element.checked); + if (checked.length === 0) { + return []; + } + const representation = []; + checked.forEach( + (field) => representation.push(field.nextElementSibling.textContent), + ); + return representation; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/select.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/select.transform.js new file mode 100644 index 000000000000..d9b890933fad --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/select.transform.js @@ -0,0 +1,25 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class SelectTransforms { + /** + * @param {FormNode} node + * @return {Array} + */ + valueTransform(node) { + const field = node.getHtmlFields()[0]; + return [field.options[field.options.selectedIndex].text]; + } +} diff --git a/components/ILIAS/UI/resources/js/Input/Container/src/transforms/switchablegroup.transform.js b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/switchablegroup.transform.js new file mode 100644 index 000000000000..94478b9895d5 --- /dev/null +++ b/components/ILIAS/UI/resources/js/Input/Container/src/transforms/switchablegroup.transform.js @@ -0,0 +1,40 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +export default class SwitchableGroupTransforms { + /** + * @param {FormNode} node + * @return {Array} + */ + valueTransform(node) { + const children = node.getChildren(); + if (children.length === 0) { + return []; + } + const representation = []; + children.forEach((child) => representation.push(child.getLabel())); + return representation; + } + + /** + * @param {FormNode} node + * @return {Array} + */ + childrenTransform(node) { + return node.getAllChildren().filter( + (child) => child.getHtmlFields()[0].checked, + ); + } +} diff --git a/components/ILIAS/UI/src/Component/Input/Container/Form/Form.php b/components/ILIAS/UI/src/Component/Input/Container/Form/Form.php index 703ea43695ee..0d53f1960159 100755 --- a/components/ILIAS/UI/src/Component/Input/Container/Form/Form.php +++ b/components/ILIAS/UI/src/Component/Input/Container/Form/Form.php @@ -25,10 +25,11 @@ use Psr\Http\Message\ServerRequestInterface; use ILIAS\UI\Component\Input\Container\Container; use ILIAS\UI\Component\Input\Input; +use ILIAS\UI\Component\JavaScriptBindable; /** * This describes commonalities between all forms. */ -interface Form extends Container +interface Form extends Container, JavaScriptBindable { } diff --git a/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Form.php b/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Form.php index 2a8a684785d6..71316d51711e 100755 --- a/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Form.php +++ b/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Form.php @@ -26,12 +26,15 @@ use Psr\Http\Message\ServerRequestInterface; use ILIAS\UI\Implementation\Component\Input\PostDataFromServerRequest; use ILIAS\UI\Implementation\Component\Input\NameSource; +use ILIAS\UI\Implementation\Component\JavaScriptBindable; /** * This implements commonalities between all forms. */ abstract class Form extends Container implements C\Input\Container\Form\Form { + use JavaScriptBindable; + /** * @param C\Input\Container\Form\FormInput[] $inputs */ diff --git a/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php b/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php index db1fdada1866..807c7c3720bc 100755 --- a/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php +++ b/components/ILIAS/UI/src/Implementation/Component/Input/Container/Form/Renderer.php @@ -26,6 +26,7 @@ use ILIAS\UI\Renderer as RendererInterface; use ILIAS\UI\Component; use LogicException; +use ILIAS\UI\Implementation\Render\ResourceRegistry; class Renderer extends AbstractComponentRenderer { @@ -34,6 +35,10 @@ class Renderer extends AbstractComponentRenderer */ public function render(Component\Component $component, RendererInterface $default_renderer): string { + $component = $component->withAdditionalOnLoadCode( + fn($id) => "il.UI.Input.Container.init('{$id}');" + ); + if ($component instanceof Form\Standard) { return $this->renderStandard($component, $default_renderer); } @@ -63,7 +68,17 @@ protected function renderStandard(Form\Standard $component, RendererInterface $d $tpl->setVariable("BUTTONS_TOP", $default_renderer->render($submit_button)); $tpl->setVariable("BUTTONS_BOTTOM", $default_renderer->render($submit_button)); - $tpl->setVariable("INPUTS", $default_renderer->render($component->getInputGroup())); + $tpl->setVariable( + "INPUTS", + $default_renderer + //->withAdditionalContext($component) + ->render($component->getInputGroup()) + ); + + + + $id = $this->bindJavaScript($component); + $tpl->setVariable("ID", $id); return $tpl->get(); } @@ -134,4 +149,11 @@ protected function maybeAddRequired(Form\Form $component, Template $tpl): void $tpl->setVariable("TXT_REQUIRED", $this->txt("required_field")); } } + + + public function registerResources(ResourceRegistry $registry): void + { + parent::registerResources($registry); + $registry->register('./assets/js/container.min.js'); + } } diff --git a/components/ILIAS/UI/src/examples/Input/Field/Section/foldable.php b/components/ILIAS/UI/src/examples/Input/Field/Section/foldable.php new file mode 100644 index 000000000000..c7d19468b5d4 --- /dev/null +++ b/components/ILIAS/UI/src/examples/Input/Field/Section/foldable.php @@ -0,0 +1,206 @@ + + * Sections may fold + * + * expected output: > + * ILIAS shows the rendered Component. + * --- + */ +function foldable() +{ + global $DIC; + $ui = $DIC->ui()->factory(); + $renderer = $DIC->ui()->renderer(); + $request = $DIC->http()->request(); + + $number_input = $ui->input()->field()->numeric("number", "Put in a number.") + ->withLabel("a number"); + $text_input = $ui->input()->field()->text("text", "Put in some text.") + ->withLabel("a text"); + + $file_input = $ui->input()->field()->file( + new \ilUIDemoFileUploadHandlerGUI(), + "Upload File", + "you can drop your files here" + ); + + $link_input = $ui->input()->field()->link( + "a LinkField", + "enter label and url" + ); + + + $section1 = $ui->input()->field()->section( + [ + $number_input->withValue(5), + $text_input->withValue('some text'), + $file_input, + $link_input, + ], + "first section", + "fill in some values" + ); + + $optional_group = $ui->input()->field()->optionalGroup( + [ + $ui->input()->field()->duration("a dependent duration field", ""), + $ui->input()->field()->text("a dependent text field", "") + ], + 'optional group', + 'check to edit the field of the group', + ); + $optional_group2 = $ui->input()->field()->optionalGroup( + [ + $ui->input()->field()->section( + [ + $ui->input()->field()->tag( + "Basic Tag", + ['Interesting', 'Boring', 'Animating', 'Repetitious'], + "Just some tags" + ), + $rating = $ui->input()->field()->rating("Rate with the Stars:", "change the rating") + ], + 'fields in opt. section' + )], + 'optional section', + 'byline opt. section', + ); + + $group1 = $ui->input()->field()->group( + [ + "field_1_1" => $ui->input()->field()->text("Item 1.1", "Just some field"), + "field_1_2" => $ui->input()->field()->text("Item 1.2", "Just some other field"), + ], + "Switchable Group number one", + "Byline for Switchable Group number one" + ); + $options = array( + "1" => "Pick 1", + "2" => "Pick 2", + "3" => "Pick 3", + "4" => "Pick 4", + ); + $group2 = $ui->input()->field()->group( + [ + $ui->input()->field()->multiselect("now, pick more", $options, "This is the byline of multi text") + ->withValue([2,3]), + $ui->input()->field()->select("now, select one more", $options, "This is the byline of select") + ->withValue(2), + + $ui->input()->field()->radio("now, pick just one more", "byline for radio (pick one more)") + ->withOption('single1', 'Single 1') + ->withOption('single2', 'Single 2') + ], + "Switchable Group number two", + "Byline for Switchable Group number two" + ); + $switchable_group = $ui->input()->field()->switchableGroup( + [ + "g1" => $group1, + "g2" => $group2, + ], + "Pick One", + "Byline for the whole Switchable Group (pick one)" + ); + + $section2 = $ui->input()->field()->section( + [ $number_input->withValue(7), + $text_input->withValue('some other text'), + $optional_group->withValue(null), + $switchable_group, + $text_input->withValue('final words'), + ], + "second section", + "fill in some other values" + ); + + + $form = $ui->input()->container()->form()->standard('#', [$optional_group2, $section1, $section2]); + + $button_js = $ui->button()->standard('log struct', '')->withOnLoadCode( + fn($id) => "document.querySelector('#{$id}').addEventListener( + 'click', + (event) => console.log( + il.UI.Input.Container.get(event.srcElement.parentNode.querySelector('form').id) + .getNodes() + ) + );" + ); + $button_unfold = $ui->button()->standard('unfold', '')->withOnLoadCode( + fn($id) => "document.querySelector('#{$id}').addEventListener( + 'click', + (event) => { + const form = event.srcElement.parentNode.querySelector('form'); + const nodes = il.UI.Input.Container.get(form.id); + const formparts = nodes.getNodes().getChildren(); + + formparts.forEach((part) => { + const partArea = form.querySelector('[data-il-ui-input-name=\"' + part.getFullName() +'\"]'); + const fieldArea = partArea.querySelector(':scope > div.c-input__field'); + const valArea = partArea.querySelector(':scope > div.c-input__value_representation'); + + fieldArea.style.display = 'block'; + valArea.innerHTML = ''; + }); + } + );" + ); + + $button_fold = $ui->button()->standard('fold', '')->withOnLoadCode( + fn($id) => "document.querySelector('#{$id}').addEventListener( + 'click', + (event) => { + const form = event.srcElement.parentNode.querySelector('form'); + const nodes = il.UI.Input.Container.get(form.id); + const formparts = nodes.getNodes().getChildren(); + + formparts.forEach((part) => { + const values = nodes.getValuesRepresentation(part); + let txt = ''; + values.forEach((v) => { + const {label, value, indent, type} = v; + txt = txt + + '  '.repeat(indent + 1) + + label + + ' (' + type.replace('-field-input', '') + ') ' + + ': ' + value + '' + + '
'; + }); + + const partArea = form.querySelector('[data-il-ui-input-name=\"' + part.getFullName() +'\"]'); + const fieldArea = partArea.querySelector(':scope > div.c-input__field'); + const valArea = partArea.querySelector(':scope > div.c-input__value_representation'); + + fieldArea.style.display = 'none'; + valArea.style.fontSize = '80%'; + valArea.innerHTML = txt; + }); + + } + );" + ); + + if ($request->getMethod() == "POST") { + $form = $form->withRequest($request); + $result = $form->getData()[0]; + } else { + $result = "No result yet."; + } + + //Return the rendered form + return + "
" . print_r($result, true) . "

" . + $renderer->render([ + $form, + $button_js, + $button_fold, + $button_unfold + ]); +} diff --git a/components/ILIAS/UI/src/templates/default/Input/tpl.context_form.html b/components/ILIAS/UI/src/templates/default/Input/tpl.context_form.html index 116c46e1112f..86d7a9aabbe0 100755 --- a/components/ILIAS/UI/src/templates/default/Input/tpl.context_form.html +++ b/components/ILIAS/UI/src/templates/default/Input/tpl.context_form.html @@ -14,4 +14,5 @@ - \ No newline at end of file +
+ diff --git a/components/ILIAS/UI/src/templates/default/Input/tpl.optionalgroup_label.html b/components/ILIAS/UI/src/templates/default/Input/tpl.optionalgroup_label.html index 2630c5296cad..50e0477cdcb6 100644 --- a/components/ILIAS/UI/src/templates/default/Input/tpl.optionalgroup_label.html +++ b/components/ILIAS/UI/src/templates/default/Input/tpl.optionalgroup_label.html @@ -3,3 +3,4 @@ + diff --git a/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html b/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html index ebc6006c21ce..3f902e54f2d0 100755 --- a/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html +++ b/components/ILIAS/UI/src/templates/default/Input/tpl.standard.html @@ -1,4 +1,4 @@ -
describedby="{ERROR_ID}" {NAME} action="{URL}" method="post" novalidate="novalidate"> + describedby="{ERROR_ID}" {NAME} action="{URL}" method="post" novalidate="novalidate">
{BUTTONS_TOP}
diff --git a/components/ILIAS/UI/tests/Client/Input/Container/container.test.js b/components/ILIAS/UI/tests/Client/Input/Container/container.test.js new file mode 100644 index 000000000000..dabb45a56b77 --- /dev/null +++ b/components/ILIAS/UI/tests/Client/Input/Container/container.test.js @@ -0,0 +1,190 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + +import { expect } from 'chai'; +import { JSDOM } from 'jsdom'; + +import ContainerFactory from '../../../../resources/js/Input/Container/src/container.factory.js'; +import Container from '../../../../resources/js/Input/Container/src/container.class.js'; +import FormNode from '../../../../resources/js/Input/Container/src/formnode.class.js'; +import SwitchableGroupTransforms from '../../../../resources/js/Input/Container/src/transforms/switchablegroup.transform.js'; + +/** + * get HTML document from file + * + * @return JSDOM + */ +function loadMockedDom(file) { + const path = 'components/ILIAS/UI/tests/Client/Input/Container/'; + const doc = JSDOM.fromFile(path + file, { contentType: 'text/html', resources: 'usable' }) + .then((dom) => dom.window.document); + return doc; +} + +describe('Input\\Container components are there', () => { + it('ContainerFactory', () => { + expect(ContainerFactory).to.not.be.undefined; + }); + + it('Container', () => { + expect(Container).to.not.be.undefined; + }); + it('FormNode', () => { + expect(FormNode).to.not.be.undefined; + }); +}); + +/* +*/ +describe('Input\\Container', () => { + before(async () => { + const transforms = {}; + global.doc = await loadMockedDom('containertest_simple.html'); + global.containerSimple = new Container(transforms, global.doc.querySelector('#test_container_id')); + }); + + it('is built and provides a FormNode', () => { + expect(global.containerSimple).to.be.an.instanceOf(Container); + expect(global.containerSimple.getNodes()).to.be.an.instanceOf(FormNode); + }); + + it('provides a list of FormNodes and values', () => { + const expected = [ + { + label: 'test_container_id', + value: [], + indent: 0, + type: 'form', + }, + { + label: 'Item 1', + value: ['value 0'], + indent: 1, + type: 'text-field-input', + }, + { + label: 'Item 2', + value: ['value 1'], + indent: 1, + type: 'text-field-input', + }, + { + label: 'Item 3', + value: [''], + indent: 1, + type: 'text-field-input', + }, + ]; + + expect(global.containerSimple.getValuesRepresentation()).to.eql(expected); + }); + + it('finds FormNodes by name; FormNodes have name, label and value', async () => { + const doc = await loadMockedDom('containertest_switchablegroup.html'); + const transforms = {}; + const containerSwitchableGroup = new Container(transforms, doc.querySelector('#test_container_id')); + const expected = [ + ['form/input_0', 'Pick One', []], + ['form/input_0/input_1', 'Switchable Group number one (with numeric key)', ['1']], + ['form/input_0/input_1/input_2', 'Item 1.1', ['']], + ['form/input_0/input_1/input_3', 'Item 1.2', ['val 1.2']], + ['form/input_0/input_1/input_4', 'Item 1.3', ['2024-09-26']], + ['form/input_0/input_5', 'Switchable Group number two', []], + ['form/input_0/input_5/input_6', 'Item 2', ['this should not appear']], + ]; + + expected.forEach( + (n) => { + const [name, label, values] = n; + const node = containerSwitchableGroup.getNodeByName(name); + expect(node.getFullName()).to.eql(name); + expect(node.getLabel()).to.eql(label); + expect(node.getValues()).to.eql(values); + }, + ); + }); + + it('filters switchable groups', async () => { + const doc = await loadMockedDom('containertest_switchablegroup.html'); + const transforms = {}; + transforms['switchable-group-field-input'] = new SwitchableGroupTransforms(); + + const containerSwitchableGroup = new Container(transforms, doc.querySelector('#test_container_id')); + + let expected = [ + { + label: 'test_container_id', value: [], indent: 0, type: 'form', + }, + { + label: 'Pick One', + value: ['Switchable Group number one (with numeric key)'], + indent: 1, + type: 'switchable-group-field-input', + }, + { + label: 'Switchable Group number one (with numeric key)', + value: ['1'], + indent: 2, + type: 'group-field-input', + }, + { + label: 'Item 1.1', + value: [''], + indent: 3, + type: 'text-field-input', + }, + { + label: 'Item 1.2', + value: ['val 1.2'], + indent: 3, + type: 'text-field-input', + }, + { + label: 'Item 1.3', + value: ['2024-09-26'], + indent: 3, + type: 'date-time-field-input', + }, + ]; + expect(containerSwitchableGroup.getValuesRepresentation()).to.eql(expected); + + doc.getElementsByName('form/input_0')[1].checked = 'checked'; + + expected = [ + { + label: 'test_container_id', value: [], indent: 0, type: 'form', + }, + { + label: 'Pick One', + value: ['Switchable Group number two'], + indent: 1, + type: 'switchable-group-field-input', + }, + { + label: 'Switchable Group number two', + value: ['g2'], + indent: 2, + type: 'group-field-input', + }, + { + label: 'Item 2', + value: ['this should not appear'], + indent: 3, + type: 'text-field-input', + }, + ]; + expect(containerSwitchableGroup.getValuesRepresentation()).to.eql(expected); + }); +}); diff --git a/components/ILIAS/UI/tests/Client/Input/Container/containertest_simple.html b/components/ILIAS/UI/tests/Client/Input/Container/containertest_simple.html new file mode 100644 index 000000000000..47643a321859 --- /dev/null +++ b/components/ILIAS/UI/tests/Client/Input/Container/containertest_simple.html @@ -0,0 +1,36 @@ + + +
+
+
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+ + + + \ No newline at end of file diff --git a/components/ILIAS/UI/tests/Client/Input/Container/containertest_switchablegroup.html b/components/ILIAS/UI/tests/Client/Input/Container/containertest_switchablegroup.html new file mode 100644 index 000000000000..0b788ff6b1e5 --- /dev/null +++ b/components/ILIAS/UI/tests/Client/Input/Container/containertest_switchablegroup.html @@ -0,0 +1,69 @@ +
+
+
+
* Required
+
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+ +
+
+ + + +
\ No newline at end of file diff --git a/components/ILIAS/UI/tests/Component/Input/Container/Filter/FilterInputTest.php b/components/ILIAS/UI/tests/Component/Input/Container/Filter/FilterInputTest.php index c24229aa3553..32cf7cacbcd8 100644 --- a/components/ILIAS/UI/tests/Component/Input/Container/Filter/FilterInputTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Container/Filter/FilterInputTest.php @@ -264,6 +264,7 @@ public function testRenderDateTimeWithDurationAndFilterContext(): void
+
'); $this->assertEquals($expected, $html); diff --git a/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php b/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php index e030b8d2f262..c7e4aa6e9f50 100755 --- a/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Container/Form/StandardFormTest.php @@ -129,7 +129,7 @@ public function testRender(): void $html = $this->getDefaultRenderer()->render($form); $expected = $this->brutallyTrimHTML(' -
+
' @@ -174,7 +174,7 @@ public function testSubmitCaptionRender(): void $html = $this->brutallyTrimHTML($r->render($form)); $expected = $this->brutallyTrimHTML(' - +
' @@ -201,7 +201,7 @@ public function testRenderNoUrl(): void $html = $this->brutallyTrimHTML($r->render($form)); $expected = $this->brutallyTrimHTML(' - +
@@ -261,7 +261,7 @@ public function testRenderWithErrorOnField(): void $html = $this->brutallyTrimHTML($r->render($form)); $expected = $this->brutallyTrimHTML(' -
@@ -278,6 +278,7 @@ public function testRenderWithErrorOnField(): void invalid...
+
diff --git a/components/ILIAS/UI/tests/Component/Input/Field/GroupInputTest.php b/components/ILIAS/UI/tests/Component/Input/Field/GroupInputTest.php index ebddc0212db7..ffb1344691ed 100755 --- a/components/ILIAS/UI/tests/Component/Input/Field/GroupInputTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Field/GroupInputTest.php @@ -388,6 +388,7 @@ public function testGroupRendering(): void
+
@@ -395,6 +396,7 @@ public function testGroupRendering(): void
+
EOT; $actual = $this->brutallyTrimHTML($this->getDefaultRenderer()->render($group)); diff --git a/components/ILIAS/UI/tests/Component/Input/Field/MarkdownTest.php b/components/ILIAS/UI/tests/Component/Input/Field/MarkdownTest.php index 295e119125bb..f9a66a1876ef 100755 --- a/components/ILIAS/UI/tests/Component/Input/Field/MarkdownTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Field/MarkdownTest.php @@ -161,8 +161,8 @@ public function testRender(): void
- +
" ); @@ -222,6 +222,7 @@ public function testRenderWithByline(): void +
" ); @@ -282,6 +283,7 @@ public function testRenderWithLimits(): void +
" ); @@ -339,6 +341,7 @@ public function testRenderWithDisabled(): void +
" ); @@ -396,6 +399,7 @@ public function testRenderWithRequired(): void +
" ); @@ -455,6 +459,7 @@ public function testRenderWithError(): void
ui_error:$error
+
" ); diff --git a/components/ILIAS/UI/tests/Component/Input/Field/MultiSelectInputTest.php b/components/ILIAS/UI/tests/Component/Input/Field/MultiSelectInputTest.php index b199cac5780e..1c21c323995b 100755 --- a/components/ILIAS/UI/tests/Component/Input/Field/MultiSelectInputTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Field/MultiSelectInputTest.php @@ -211,6 +211,7 @@ public function testRenderNoOptions(): void +
'; $this->assertHTMLEquals($expected, $r->render($ms)); diff --git a/components/ILIAS/UI/tests/Component/Input/Field/RatingInputTest.php b/components/ILIAS/UI/tests/Component/Input/Field/RatingInputTest.php index b20c9ce14831..a249b02d6f86 100644 --- a/components/ILIAS/UI/tests/Component/Input/Field/RatingInputTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Field/RatingInputTest.php @@ -147,6 +147,7 @@ public function testRatingRenderFull(): void +
' ); diff --git a/components/ILIAS/UI/tests/Component/Input/Field/SwitchableGroupInputTest.php b/components/ILIAS/UI/tests/Component/Input/Field/SwitchableGroupInputTest.php index eafb9c8afb90..601451b1fd57 100755 --- a/components/ILIAS/UI/tests/Component/Input/Field/SwitchableGroupInputTest.php +++ b/components/ILIAS/UI/tests/Component/Input/Field/SwitchableGroupInputTest.php @@ -441,8 +441,10 @@ public function testRender(): SG for="id_2">f
+
+
+
+
+
EOT; $this->assertEquals( diff --git a/components/ILIAS/UI/tests/Component/Launcher/LauncherInlineTest.php b/components/ILIAS/UI/tests/Component/Launcher/LauncherInlineTest.php index 9f24178e53d2..274c89e58330 100755 --- a/components/ILIAS/UI/tests/Component/Launcher/LauncherInlineTest.php +++ b/components/ILIAS/UI/tests/Component/Launcher/LauncherInlineTest.php @@ -236,6 +236,7 @@ public function testLauncherInlineRendering(): void +