Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public function test_to_form_input()
$refinery = new ILIAS\Refinery\Factory($df, $lng);
$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public function test_to_form_input()
$refinery = new ILIAS\Refinery\Factory($df, $lng);
$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public function testToFormInput(): void
$refinery = new ILIAS\Refinery\Factory($df, $lng);
$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
16 changes: 13 additions & 3 deletions components/ILIAS/OrgUnit/tests/ilModulesOrgUnitTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public function mockGetAmdForm(array $available_records, ilOrgUnitType $type): S

class ilModulesOrgUnitTypeTest extends TestCase
{
use NameSourceStubs;

public function getRefinery(): \ILIAS\Refinery\Factory
{
$data_factory = new \ILIAS\Data\Factory();
Expand All @@ -52,30 +54,36 @@ public function getRefinery(): \ILIAS\Refinery\Factory
public function getUIFactory(): NoUIFactory
{
$node_factory = $this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class);
$has_dynamic_inputs_name_source = $this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class);
$language = $this->createMock(ilLanguage::class);
$filter_factory = $this->createMock(Component\Input\Container\Filter\Factory::class);
$view_control_factory = $this->createMock(Component\Input\Container\ViewControl\Factory::class);
$control_factory = $this->createMock(Component\Input\ViewControl\Factory::class);
$upload_limit_resolver = $this->createMock(Component\Input\UploadLimitResolver::class);
$refinery = $this->getRefinery();
$name_source = $this->createCountingNameSourceStub('input_');

$factory = new class (
$node_factory,
$has_dynamic_inputs_name_source,
$language,
$filter_factory,
$view_control_factory,
$control_factory,
$upload_limit_resolver,
$refinery
$refinery,
$name_source,
) extends NoUIFactory {
public function __construct(
protected $node_factory,
protected $has_dynamic_inputs_name_source,
protected $language,
protected $filter_factory,
protected $view_control_factory,
protected $control_factory,
protected $upload_limit_resolver,
protected $refinery
protected $refinery,
protected $name_source,
) {
}

Expand All @@ -86,6 +94,7 @@ public function input(): Component\Input\Factory

$field_factory = new Component\Input\Field\Factory(
$this->node_factory,
$this->has_dynamic_inputs_name_source,
$this->upload_limit_resolver,
$signal_generator,
$data_factory,
Expand All @@ -95,7 +104,8 @@ public function input(): Component\Input\Factory

$form_factory = new Component\Input\Container\Form\Factory(
$field_factory,
$signal_generator
$signal_generator,
$this->name_source,
);
$container_factory = new Component\Input\Container\Factory(
$form_factory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public function testToFormInput(): void

$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ public function testToFormInput(): void

$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public function testToFormInput(): void

$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public function testToFormInput(): void

$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ public function testToFormInput(): void

$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function setUp(): void
$this->refinery = new Refinery($this->data_factory, $this->lng);
$this->field_factory = new FieldFactory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(UploadLimitResolver::class),
new SignalGenerator(),
$this->data_factory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public function testToFormInput(): void

$f = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new ILIAS\UI\Implementation\Component\SignalGenerator(),
$df,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ public function testScoreSettingsSectionSummary(): void

$field_factory = new ILIAS\UI\Implementation\Component\Input\Field\Factory(
$this->createMock(\ILIAS\UI\Implementation\Component\Input\Field\Node\Factory::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\HasDynamicInputsNameSource::class),
$this->createMock(\ILIAS\UI\Implementation\Component\Input\UploadLimitResolver::class),
new \ILIAS\UI\Implementation\Component\SignalGenerator(),
$data_factory,
Expand Down
12 changes: 12 additions & 0 deletions components/ILIAS/UI/UI.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ public function init(
$internal[UI\Implementation\Component\SignalGeneratorInterface::class],
$internal[UI\Implementation\Component\Modal\InterruptiveItem\Factory::class],
$internal[UI\Implementation\Component\Input\Field\Factory::class],
$internal[UI\Implementation\Component\Input\DefaultNameSource::class],
);
$internal[UI\Implementation\Component\SignalGeneratorInterface::class] = static fn() =>
new UI\Implementation\Component\SignalGenerator();
Expand Down Expand Up @@ -278,6 +279,7 @@ public function init(
new UI\Implementation\Component\Dropzone\File\Factory(
$internal[UI\Implementation\Component\SignalGeneratorInterface::class],
$internal[UI\Implementation\Component\Input\Field\Factory::class],
$internal[UI\Implementation\Component\Input\DefaultNameSource::class],
);

$internal[UI\Implementation\Component\Popover\Factory::class] = static fn() =>
Expand Down Expand Up @@ -322,6 +324,7 @@ public function init(
$internal[UI\Implementation\Component\Input\Field\Factory::class] = static fn() =>
new UI\Implementation\Component\Input\Field\Factory(
$internal[UI\Implementation\Component\Input\Field\Node\Factory::class],
$internal[UI\Implementation\Component\Input\HasDynamicInputsNameSource::class],
$internal[UI\Implementation\Component\Input\UploadLimitResolver::class],
$internal[UI\Implementation\Component\SignalGeneratorInterface::class],
$pull[Data\Factory::class],
Expand All @@ -345,16 +348,19 @@ public function init(
new UI\Implementation\Component\Input\Container\Form\Factory(
$internal[UI\Implementation\Component\Input\Field\Factory::class],
$internal[UI\Implementation\Component\SignalGeneratorInterface::class],
$internal[UI\Implementation\Component\Input\DefaultNameSource::class],
);
$internal[UI\Implementation\Component\Input\Container\Filter\Factory::class] = static fn() =>
new UI\Implementation\Component\Input\Container\Filter\Factory(
$internal[UI\Implementation\Component\SignalGeneratorInterface::class],
$internal[UI\Implementation\Component\Input\Field\Factory::class],
$internal[UI\Implementation\Component\Input\DefaultNameSource::class],
);
$internal[UI\Implementation\Component\Input\Container\ViewControl\Factory::class] = static fn() =>
new UI\Implementation\Component\Input\Container\ViewControl\Factory(
$internal[UI\Implementation\Component\SignalGeneratorInterface::class],
$internal[UI\Implementation\Component\Input\ViewControl\Factory::class],
$internal[UI\Implementation\Component\Input\DefaultNameSource::class],
);
$internal[UI\Implementation\Component\Input\ViewControl\Factory::class] = static fn() =>
new UI\Implementation\Component\Input\ViewControl\Factory(
Expand All @@ -364,6 +370,12 @@ public function init(
$internal[UI\Implementation\Component\SignalGeneratorInterface::class],
$use[Language\Language::class],
);
$internal[UI\Implementation\Component\Input\DefaultNameSource::class] = static fn() =>
new UI\Implementation\Component\Input\DefaultNameSource();
$internal[UI\Implementation\Component\Input\HasDynamicInputsNameSource::class] = static fn() =>
new UI\Implementation\Component\Input\HasDynamicInputsNameSource(
$internal[UI\Implementation\Component\Input\DefaultNameSource::class],
);

$internal[UI\Implementation\Component\Table\Factory::class] = static fn() =>
new UI\Implementation\Component\Table\Factory(
Expand Down
19 changes: 19 additions & 0 deletions components/ILIAS/UI/docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,25 @@ Storage of paramters in DataTable and SequenceNavigation look very much alike;
in favor of those and further/future components the implementation should be
realized as a trait to be used by several components.

### Remove all internal usages of `withDedicatedName()` (beginner, 8h)

`ILIAS\UI\Component\Input\Container\Container::withDedicatedName()` and
`ILIAS\UI\Component\Input\Input::withDedicatedName()` are currently used internally in a few places. When this happens,
we sometimes override already provided dedicated names (of the consumer) and introduce a hidden dependance on an
implementation detail. Since consumers MUST NOT rely on the names of inputs during data processing, this behaviour adds
complexity without clear benefit. Removing these internal usages will streamline naming and make the framework easier to
understand and maintain. This MAY be the first step towards removing `withDedicatedName()` altogether, if no advantage
will arise soon.

### Remove `OrderingTableContextRenderer` and revise `Table\Ordering` composition (advanced, ~5d)

The `ILIAS\UI\Implementation\Component\Input\Field\OrderingTableContextRenderer` was introduced during the refactoring
of `ILIAS\UI\Implementation\Component\Input\NameSource`. Its a temporary workaround that replaced the implementation of
such name source as an inline anonymous class inside the table context renderer. This name source was used in order to
bypass input mechanics in order to set the ordering row ids as dedicated input names directly as the actual input name.
This highlights the fact that `Table\Ordering` is some unfinished hybrid between a form input container and a table,
which is not ideal. The next step is to revisit the composition and interal handling of the ordering table, remove this
temporary workaround, and ensure the table complies with the normal mechanics of our inputs.

## Long Term

Expand Down

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion components/ILIAS/UI/resources/js/Input/Field/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ il.UI.Input = il.UI.Input || {};
forceChunking: should_upload_be_chunked,
chunkSize: chunk_size_in_bytes,
form: action_button.closest('form'),
templateRenderer: new il.UI.Input.HasDynamicInputsTemplateRenderer(
document,
current_file_count,
),

// override default rendering function.
addedfile: file => {
Expand Down Expand Up @@ -375,7 +379,9 @@ il.UI.Input = il.UI.Input || {};
return;
}

let preview = il.UI.core.TemplateRenderer.createContent(dropzones[input_id].options.customPreviewTemplate);
let preview = dropzones[input_id].options.templateRenderer.createContent(
dropzones[input_id].options.customPreviewTemplate,
);

// add file info to preview and setup expansion toggles.
preview.querySelector('[data-dz-name]').innerText = file.name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* 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 TemplateRenderer from '../../../Core/src/TemplateRenderer.js';

/**
* Updates all string values of the given attributeName and replaces the last [] or [n] array-
* postfix with a concrete newIndex (i.e. input_1[input_2][] becomes input_1[input_2][0])
*
* @param {HTMLElement} parentElement
* @param {number} newIndex
* @param {string} attributeName
*/
function replaceAllAttributeValueIndices(parentElement, newIndex, attributeName) {
parentElement.querySelectorAll(`[${attributeName}]`).forEach((child) => {
child.setAttribute(
attributeName,
replaceSingleAttributeValueIndex(child.getAttribute(attributeName), newIndex),
);
});
}

/**
* @param {string} name
* @param {number} index
*/
function replaceSingleAttributeValueIndex(name, index) {
return name.replace(/\[(\d*)\]$/, `[${index}]`);
}

/**
* @author Thibeau Fuhrer <thibeau@sr.solutions>
*/
export default class HasDynamicInputsTemplateRenderer extends TemplateRenderer {
/** @type {number} */
#currentInputGroupIndex;

/**
* @param {Document} document
* @param {number} currentInputGroupIndex
*/
constructor(document, currentInputGroupIndex) {
super(document);
this.#currentInputGroupIndex = currentInputGroupIndex;
}

/**
* @inheritDoc
*/
createContent(template) {
const newInputFragment = super.createContent(template);

replaceAllAttributeValueIndices(newInputFragment, this.#currentInputGroupIndex, 'data-il-ui-input-name');
replaceAllAttributeValueIndices(newInputFragment, this.#currentInputGroupIndex, 'name');
this.#currentInputGroupIndex += 1;

return newInputFragment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import JQueryEventListener from '../../../Core/src/JQueryEventListener.js';
import Tagify from '@yaireo/tagify';
import tag from './Tag/tag.js';
import OptionFilterFactory from './OptionFilter/OptionFilterFactory.js';
import HasDynamicInputsTemplateRenderer from './HasDynamicInputsTemplateRenderer.js';

il.UI = il.UI || {};
il.UI.Input = il.UI.Input || {};
Expand All @@ -59,4 +60,7 @@ il.UI.Input = il.UI.Input || {};
Input.tagInput = Input.tag || {};
Input.tagInput.init = (input, config, value, autocompleteEndpoint, autocompleteToken) => tag(
Tagify, input, config, value, autocompleteEndpoint, autocompleteToken);

// @todo: remove this once file input is migrated.
Input.HasDynamicInputsTemplateRenderer = HasDynamicInputsTemplateRenderer;
}(il.UI.Input));
2 changes: 0 additions & 2 deletions components/ILIAS/UI/src/Component/Input/Input.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ public function withAdditionalTransformation(Transformation $trafo);
/**
* Sets an optional dedicated name for this input which is used in the NAME
* attribute of the rendered input (instead of the auto-generated 'input_x').
* If the same dedicated name is used more than once, a counter will be
* added to the name.
*
* The dedicated name is inherited by all child inputs (e.g. for groups
* or sections) and added to their name in a path-like format.
Expand Down
Loading