Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI/Input/Container: add JS to retrieve values clientside
Browse files Browse the repository at this point in the history
nhaagen authored and root committed Nov 12, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent da392e8 commit ea05fe6
Showing 36 changed files with 1,365 additions and 17 deletions.
4 changes: 3 additions & 1 deletion components/ILIAS/UI/UI.php
Original file line number Diff line number Diff line change
@@ -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() =>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 36 additions & 5 deletions components/ILIAS/UI/resources/js/Input/Container/rollup.config.js
Original file line number Diff line number Diff line change
@@ -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'
}
};
file: './dist/container.min.js',
format: 'iife',
banner: copyright,
plugins: [
terser({
format: {
comments: preserveCopyright,
},
}),
],
globals: {
jquery: '$',
},
},
external: ['jquery'],
};
Original file line number Diff line number Diff line change
@@ -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<string>}
*
*
* @type {Object<string,class>}
*/
#transforms;

/**
* @type {HTMLElement}
*/
#container;

/**
* @type {FormNode}
*/
#nodes;

/**
* @param {Object<string,Class>} 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<string, Container>}
*/
#instances = [];

/**
* @type {Object<string, class>}
*/
#transforms;

/**
* @param {Object<string,Class>} 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()];
}
}
45 changes: 45 additions & 0 deletions components/ILIAS/UI/resources/js/Input/Container/src/container.js
Original file line number Diff line number Diff line change
@@ -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);
178 changes: 178 additions & 0 deletions components/ILIAS/UI/resources/js/Input/Container/src/formnode.class.js
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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 [];
}
}
Original file line number Diff line number Diff line change
@@ -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}]`];
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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];
}
}
Original file line number Diff line number Diff line change
@@ -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,
);
}
}
Original file line number Diff line number Diff line change
@@ -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
{
}
Original file line number Diff line number Diff line change
@@ -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
*/
Original file line number Diff line number Diff line change
@@ -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');
}
}
206 changes: 206 additions & 0 deletions components/ILIAS/UI/src/examples/Input/Field/Section/foldable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<?php

declare(strict_types=1);

namespace ILIAS\UI\examples\Input\Field\Section;

/**
* ---
* description: >
* 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
+ '&nbsp;&nbsp;'.repeat(indent + 1)
+ label
+ ' <small>(' + type.replace('-field-input', '') + ')</small> '
+ ': <b>' + value + '</b>'
+ '<br/>';
});
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
"<pre>" . print_r($result, true) . "</pre><br/>" .
$renderer->render([
$form,
$button_js,
$button_fold,
$button_unfold
]);
}
Original file line number Diff line number Diff line change
@@ -14,4 +14,5 @@
<div class="c-input__help-byline">{BYLINE}</div>
<!-- END byline -->

</fieldset>
<div class="c-input__value_representation"></div>
</fieldset>
Original file line number Diff line number Diff line change
@@ -3,3 +3,4 @@
<!-- BEGIN byline -->
<div class="c-input__help-byline">{BYLINE}</div>
<!-- END byline -->

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<form role="form" class="c-form c-form--horizontal" enctype="multipart/form-data"<!-- BEGIN reference_error --> describedby="{ERROR_ID}"<!-- END reference_error --> {NAME}<!-- BEGIN action --> action="{URL}"<!-- END action --> method="post" novalidate="novalidate">
<form role="form" id="{ID}" class="c-form c-form--horizontal" enctype="multipart/form-data"<!-- BEGIN reference_error --> describedby="{ERROR_ID}"<!-- END reference_error --> {NAME}<!-- BEGIN action --> action="{URL}"<!-- END action --> method="post" novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions">{BUTTONS_TOP}</div>
<!-- BEGIN required_text_top -->
190 changes: 190 additions & 0 deletions components/ILIAS/UI/tests/Client/Input/Container/container.test.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<form role="form" id="test_container_id" class="c-form c-form--horizontal" enctype="multipart/form-data" action="#" method="post" novalidate="novalidate">

<div class="c-form__header">
<div class="c-form__actions"><button class="btn btn-default" data-action="">Save</button></div>
</div>

<fieldset class="c-input" data-il-ui-component="text-field-input" data-il-ui-input-name="form/input_0">
<label for="il_ui_fw_66d80b0abfe699_92234000">Item 1</label>
<div class="c-input__field">
<input id="il_ui_fw_66d80b0abfe699_92234000" type="text" name="form/input_0" class="c-field-text" value="value 0">
</div>
<div class="c-input__help-byline">Just some basic input</div>
<div class="c-input__value_representation"></div>
</fieldset>
<fieldset class="c-input" data-il-ui-component="text-field-input" data-il-ui-input-name="form/input_1">
<label for="il_ui_fw_66d80b0abfe699_92234001">Item 2</label>
<div class="c-input__field">
<input id="il_ui_fw_66d80b0abfe699_92234001" type="text" name="form/input_1" class="c-field-text" value="value 1">
</div>
<div class="c-input__help-byline">Just some basic input (2)</div>
<div class="c-input__value_representation"></div>
</fieldset>
<fieldset class="c-input" data-il-ui-component="text-field-input" data-il-ui-input-name="form/input_2">
<label for="il_ui_fw_66d80b0abfe699_92234002">Item 3</label>
<div class="c-input__field">
<input id="il_ui_fw_66d80b0abfe699_92234002" type="text" name="form/input_2" class="c-field-text">
</div>
<div class="c-input__help-byline">Just some basic input (3)</div>
<div class="c-input__value_representation"></div>
</fieldset>

<div class="c-form__footer">
<div class="c-form__actions"><button class="btn btn-default" data-action="">Save</button></div>
</div>

</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<form role="form" id="test_container_id" class="c-form c-form--horizontal" enctype="multipart/form-data" action="#" method="post" novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions"><button class="btn btn-default" data-action="">Save</button></div>
<div class="c-form__required"><span class="asterisk">*</span><span class="small"> Required</span></div>
</div>

<fieldset class="c-input" data-il-ui-component="switchable-group-field-input" data-il-ui-input-name="form/input_0">
<label>Pick One</label>
<div class="c-input__field">
<fieldset class="c-input" data-il-ui-component="group-field-input" data-il-ui-input-name="form/input_0/input_1">
<label><input type="radio" name="form/input_0" value="1" checked="checked"><span>Switchable Group number one (with numeric key)</span></label>
<div class="c-input__field">
<fieldset class="c-input" data-il-ui-component="text-field-input" data-il-ui-input-name="form/input_0/input_1/input_2">
<label for="il_ui_fw_66d840fe3cb926_32134854">Item 1.1</label>
<div class="c-input__field">
<input id="il_ui_fw_66d840fe3cb926_32134854" type="text" name="form/input_0/input_1/input_2" class="c-field-text">
</div>
<div class="c-input__help-byline">Just some field</div>
<div class="c-input__value_representation"></div>
</fieldset>
<fieldset class="c-input" data-il-ui-component="text-field-input" data-il-ui-input-name="form/input_0/input_1/input_3">
<label for="il_ui_fw_66d840fe3ce7b7_56789185">Item 1.2</label>
<div class="c-input__field">
<input id="il_ui_fw_66d840fe3ce7b7_56789185" type="text" name="form/input_0/input_1/input_3" class="c-field-text" value="val 1.2">
</div>
<div class="c-input__help-byline">Just some other field</div>
<div class="c-input__value_representation"></div>
</fieldset>
<fieldset class="c-input" data-il-ui-component="date-time-field-input" data-il-ui-input-name="form/input_0/input_1/input_4">
<label for="il_ui_fw_66d840fe3d2d72_79335356">Item 1.3</label>
<div class="c-input__field">
<div class="c-input-group">
<input id="il_ui_fw_66d840fe3d2d72_79335356" type="date" name="form/input_0/input_1/input_4" class="c-field-datetime" value="2024-09-26">
</div>
</div>
<div class="c-input__help-byline">a date</div>
<div class="c-input__value_representation"></div>
</fieldset>
</div>
<div class="c-input__value_representation"></div>
</fieldset>

<fieldset class="c-input" data-il-ui-component="group-field-input" data-il-ui-input-name="form/input_0/input_5">
<label><input type="radio" name="form/input_0" value="g2"><span>Switchable Group number two</span></label>
<div class="c-input__field">
<fieldset class="c-input" data-il-ui-component="text-field-input" data-il-ui-input-name="form/input_0/input_5/input_6">
<label for="il_ui_fw_66d840fe3d5d39_29530003">Item 2</label>
<div class="c-input__field">
<input id="il_ui_fw_66d840fe3d5d39_29530003" type="text" value="this should not appear" name="form/input_0/input_5/input_6" class="c-field-text">
</div>
<div class="c-input__help-byline">Just another field</div>
<div class="c-input__value_representation"></div>
</fieldset>
</div>
<div class="c-input__help-byline">with byline</div>
<div class="c-input__value_representation"></div>
</fieldset>

</div>
<div class="c-input__help-byline">...or the other</div>
<div class="c-input__value_representation"></div>
</fieldset>

<div class="c-form__footer">
<div class="c-form__required"><span class="asterisk">*</span><span class="small"> Required</span></div>
<div class="c-form__actions"><button class="btn btn-default" data-action="">Save</button></div>
</div>

</form>
Original file line number Diff line number Diff line change
@@ -264,6 +264,7 @@ public function testRenderDateTimeWithDurationAndFilterContext(): void
<input id="id_1" type="date" class="c-field-datetime" />
</div>
</div>
<div class="c-input__value_representation"></div>
</fieldset>
');
$this->assertEquals($expected, $html);
Original file line number Diff line number Diff line change
@@ -129,7 +129,7 @@ public function testRender(): void
$html = $this->getDefaultRenderer()->render($form);

$expected = $this->brutallyTrimHTML('
<form role="form" class="c-form c-form--horizontal" enctype="multipart/form-data" action="MY_URL" method="post" novalidate="novalidate">
<form role="form" id="id_2" class="c-form c-form--horizontal" enctype="multipart/form-data" action="MY_URL" method="post" novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions"><button class="btn btn-default" data-action="">save</button></div>
</div>'
@@ -174,7 +174,7 @@ public function testSubmitCaptionRender(): void
$html = $this->brutallyTrimHTML($r->render($form));

$expected = $this->brutallyTrimHTML('
<form role="form" class="c-form c-form--horizontal" enctype="multipart/form-data" action="MY_URL" method="post" novalidate="novalidate">
<form role="form" id="id_2" class="c-form c-form--horizontal" enctype="multipart/form-data" action="MY_URL" method="post" novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions"><button class="btn btn-default" data-action="">create</button></div>
</div>'
@@ -201,7 +201,7 @@ public function testRenderNoUrl(): void
$html = $this->brutallyTrimHTML($r->render($form));

$expected = $this->brutallyTrimHTML('
<form role="form" class="c-form c-form--horizontal" enctype="multipart/form-data" method="post" novalidate="novalidate">
<form role="form" id="id_2" class="c-form c-form--horizontal" enctype="multipart/form-data" method="post" novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions">
<button class="btn btn-default" data-action="">save</button>
@@ -261,7 +261,7 @@ public function testRenderWithErrorOnField(): void

$html = $this->brutallyTrimHTML($r->render($form));
$expected = $this->brutallyTrimHTML('
<form role="form" class="c-form c-form--horizontal" enctype="multipart/form-data" describedby="id_1" method="post"
<form role="form" id="id_4" class="c-form c-form--horizontal" enctype="multipart/form-data" describedby="id_1" method="post"
novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions">
@@ -278,6 +278,7 @@ public function testRenderWithErrorOnField(): void
invalid...
</div>
<div class="c-input__help-byline">byline</div>
<div class="c-input__value_representation"></div>
</fieldset>
<div class="c-form__footer">
<div class="c-form__actions">
@@ -338,7 +339,7 @@ public function testRenderWithErrorOnForm(): void

$html = $this->brutallyTrimHTML($r->render($form));
$expected = $this->brutallyTrimHTML('
<form role="form" class="c-form c-form--horizontal" enctype="multipart/form-data" describedby="id_1" method="post" novalidate="novalidate">
<form role="form" id="id_3" class="c-form c-form--horizontal" enctype="multipart/form-data" describedby="id_1" method="post" novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions"><button class="btn btn-default" data-action="">save</button></div>
</div>
@@ -374,7 +375,7 @@ public function testStandardFormRenderWithRequired(): void
);

$expected = $this->brutallyTrimHTML('
<form role="form" class="c-form c-form--horizontal" enctype="multipart/form-data" action="MY_URL" method="post" novalidate="novalidate">
<form role="form" id="id_2" class="c-form c-form--horizontal" enctype="multipart/form-data" action="MY_URL" method="post" novalidate="novalidate">
<div class="c-form__header">
<div class="c-form__actions"><button class="btn btn-default" data-action="">save</button></div>
<div class="c-form__required">
Original file line number Diff line number Diff line change
@@ -138,6 +138,7 @@ protected function getFormWrappedHtml(
<div class="c-input__help-byline">' . $byline . '</div>';
}
$html .= '
<div class="c-input__value_representation"></div>
</fieldset>
';
return $this->brutallyTrimHTML($html);
Original file line number Diff line number Diff line change
@@ -185,6 +185,7 @@ public function testRender(): void
</div>
</div>
<div class="c-input__help-byline">byline</div>
<div class="c-input__value_representation"></div>
</fieldset>
');
$this->assertEquals($expected, $html);
Original file line number Diff line number Diff line change
@@ -287,6 +287,7 @@ public function testRenderWithMetadata(): void
<div class="c-input__field">
<input id="id_1" type="text" name="name_0[input_1][]" class="c-field-text"/>
</div>
<div class="c-input__value_representation"></div>
</fieldset>
<input id="id_2" type="hidden" name="name_0[input_2][]" value="file_id"/>
</div>
@@ -369,6 +370,7 @@ public function testRenderWithMetadataValue(): void
<div class="c-input__field">
<input id="id_1" type="text" value="test" name="name_0[input_1][]" class="c-field-text"/>
</div>
<div class="c-input__value_representation"></div>
</fieldset>
<input id="id_2" type="hidden" name="name_0[input_2][]" value="test_file_id_1"/>
</div>
Original file line number Diff line number Diff line change
@@ -388,13 +388,15 @@ public function testGroupRendering(): void
<input id="id_1" type="text" class="c-field-text" />
</div>
<div class="c-input__help-byline">in 1</div>
<div class="c-input__value_representation"></div>
</fieldset>
<fieldset class="c-input" data-il-ui-component="text-field-input" data-il-ui-input-name="">
<label for="id_2">input2</label>
<div class="c-input__field">
<input id="id_2" type="text" class="c-field-text" />
</div>
<div class="c-input__help-byline">in 2</div>
<div class="c-input__value_representation"></div>
</fieldset>
EOT;
$actual = $this->brutallyTrimHTML($this->getDefaultRenderer()->render($group));
Original file line number Diff line number Diff line change
@@ -161,8 +161,8 @@ public function testRender(): void
<div class=\"c-field-markdown__preview hidden\">
</div>
</div>
</div>
<div class=\"c-input__value_representation\"></div>
</fieldset>
"
);
@@ -222,6 +222,7 @@ public function testRenderWithByline(): void
</div>
<div class=\"c-input__help-byline\">$byline</div>
<div class=\"c-input__value_representation\"></div>
</fieldset>
"
);
@@ -282,6 +283,7 @@ public function testRenderWithLimits(): void
</div>
<div class=\"c-input__help-byline\">$byline</div>
<div class=\"c-input__value_representation\"></div>
</fieldset>
"
);
@@ -339,6 +341,7 @@ public function testRenderWithDisabled(): void
</div>
<div class=\"c-input__help-byline\">$byline</div>
<div class=\"c-input__value_representation\"></div>
</fieldset>
"
);
@@ -396,6 +399,7 @@ public function testRenderWithRequired(): void
</div>
<div class=\"c-input__help-byline\">$byline</div>
<div class=\"c-input__value_representation\"></div>
</fieldset>
"
);
@@ -455,6 +459,7 @@ public function testRenderWithError(): void
</div>
<div class=\"c-input__error-msg alert alert-danger\" id=\"id_9\"><span class=\"sr-only\">ui_error:</span>$error</div>
<div class=\"c-input__help-byline\">$byline</div>
<div class=\"c-input__value_representation\"></div>
</fieldset>
"
);
Original file line number Diff line number Diff line change
@@ -211,6 +211,7 @@ public function testRenderNoOptions(): void
</ul>
</div>
<div class="c-input__help-byline">byline</div>
<div class="c-input__value_representation"></div>
</fieldset>';

$this->assertHTMLEquals($expected, $r->render($ms));
Original file line number Diff line number Diff line change
@@ -147,6 +147,7 @@ public function testRatingRenderFull(): void
</div>
<div class="c-input__help-byline">byline</div>
<div class="c-input__value_representation"></div>
</fieldset>'
);

Original file line number Diff line number Diff line change
@@ -441,8 +441,10 @@ public function testRender(): SG
for="id_2">f</label>
<div class="c-input__field"><input id="id_2" type="text" class="c-field-text" /></div>
<div class="c-input__help-byline">some field</div>
<div class="c-input__value_representation"></div>
</fieldset>
</div>
<div class="c-input__value_representation"></div>
</fieldset>
<fieldset class="c-input" data-il-ui-component="group-field-input" data-il-ui-input-name="">
<label for="id_3">
@@ -454,11 +456,14 @@ public function testRender(): SG
for="id_4">f2</label>
<div class="c-input__field"><input id="id_4" type="text" class="c-field-text" /></div>
<div class="c-input__help-byline">some other field</div>
<div class="c-input__value_representation"></div>
</fieldset>
</div>
<div class="c-input__value_representation"></div>
</fieldset>
</div>
<div class="c-input__help-byline">byline</div>
<div class="c-input__value_representation"></div>
</fieldset>
EOT;
$this->assertEquals(
Original file line number Diff line number Diff line change
@@ -236,6 +236,7 @@ public function testLauncherInlineRendering(): void
<input type="checkbox" id="id_2" value="checked" name="form/input_0" class="c-field-checkbox" />
</div>
<div class="c-input__help-byline">ok</div>
<div class="c-input__value_representation"></div>
</fieldset>
</form>
</div>

0 comments on commit ea05fe6

Please sign in to comment.