11/Input/Radio and Multi-Select with Searchable Context
This optional Searchable Context Wrapper for Radio and Multi-Select inputs makes picking options from a large list of options less intimidating. When activated for those components, it adds a filter searchbar when expanded and a compact preview mode when collapsed.
This PR has yet to be approved by the JF.
Overview
Hey, everyone :wave: concept talks with @yvseiler, @oliversamoila and internal feature requests at Concepts and Training GmbH lead to the Searchable functionality that I want to present to you in this PR.
UPDATE: You can see the Searchable Input Context in action as part of the examples for the Radio and Multi-Select Input Components.
It aligns with findings from my previous paper commissioned by the University of Bern about complex forms: Splitting input into a condensed viewing vs editing mode can greatly simplify the presented data and clarify what selection has been set.
When collapsed it only shows the currently selected options making it easier to check settings at a glance. When expanded a search field helps to quickly find a known option.
It supports multi-select and radio fields as a method of input, but could potentially be expanded to other fields (e.g. tree)
We think this addition to the UI components is a good candidate for the selection of content styles in the page editor. We would like to submit this for consideration when moving from legacy UI to Kitchen Sink UI.
For more details have a look at the description added to the PHP factory.
Thanks
I used to be mostly a frontend designer and this is one of my first "real" PHP and JS ES6 projects, so there might be some rough edges that still need to be refined during further implementation. Thanks to the ILIAS developer documentation, the very clean and clear implementation of most existing UI components and the the patient guidance of @rklees and @nhaagen, I had quite a good experience digging into this challenge.
Project Overview & Progress
- [x] PHP interface
- [x] PHP factory
- [x] PHP class implementation
- [x] PHP Renderer for current proof-of-concept state
- [x] PHP kitchen sink documentation
- [X] JS factory
- [X] JS class implementation
- [X] SCSS style code
- [X] Examples
- [x] approval of JF needed
- [x] PHP/JS remove debug messages
- [x] PHP use translation strings in renderer
- [x] PHP Examples fix submitting of the form data (or remove it)
- [x] PHP checker passes
- [x] JS ESlint passes
- [x] (S)CSS Squad topic: should forms in ILIAS 11 move from media queries to container queries?
- [x] SCSS refine paddings and margins
- [x] PHP unit tests
- [x] PHP code review
- [x] JS code review
Did I miss anything? :smile:
We'll do some internal QS before we move on in the community process.
A big thank you @catenglaender for this work and a big step towards a very valuable UI component to simplify forms. I like it!
The Select Search UI component has been dissolved to be part of the Radio and MultiSelect Input component.
More specific Unit Tests have been added. All automatic checks pass. :heavy_check_mark:
I think we can move on with the community processes. What do you think @rklees?
Hi @Amstutz and @thibsy,
would you take this over? Maybe look into the interface changes first, so we could take this to JF soon?
Kind regards!
Hi @catenglaender
Note, this is only the review of the public interface. The review of the implementation (especially the JS part) will follow, once this is approved by the JF.
First and foremost; Thx for this excellent contribution. This will ease the use of several multi select use cases involving large lists.
Please change or explain why not:
- [x] Usage Rules: Add one, asking that this "SHOULD be used if the list contains more than 5 items". If possible also add one of when this SHOULD NOT be used.
- [x] A11y: This control could be tricky to use for people with e.g. limited vision. Therefore, I believe it to be of worth, to have some A11y in the rules and therefore guarded from any future refactoring without explicit discussion about the change. What comes to my mind, the search must be only operable by using the keyboard? Further, people without or limited vision should be made aware of what is happening when they are using the search.
- [x] Searchable I: we are not very fond of the term "search". Please rename this interface to HasFilter and its method to withHasFilter(). Please also make sure to search-and-replace all of your changes and amend for this change in wording.
- [x] Searchable II: please change the return type declaration of the method to static. This will improve method visibility of chained method calls.
thx a lot! @Amstutz, @klees and @Thibeau Fuhrer [sr.solutions]
@catenglaender I pass this back to you ftm. Feel free to reassign this to me as soon as there is something we can do here to process this further.
Wow, I have never done a name refactoring on this scale - what an experience even with phpStorm's very impressive refactoring tools... hope I got it all. :slightly_smiling_face:
Ready for another check @Amstutz :+1:
Hey everyone,
just FYI some quick sentences regarding the differences between "search" and "filter" that we, as UI coordinators, currently see:
- A "search" starts with criteria regarding the things to search for. Once the user has provided these criteria, the system presents some results that match these criteria (sometimes more or less...) Also, depending on the concrete instance of a "search" there simply might be no matching results, as the thing to be searched for simply might not exist. Changing criteria might or might not help actually finding something.
- A "filter" starts with some data, probably in a tabular shape, where records are presented ("rows") that can be compared in some regard ("columns", "fields"). A filter than narrows down that existing data by applying criteria to the data. This might also lead to an empty dataset, but we know that data existed in the first place and we can go back there by removing filter criteria.
Both look similar from far away, but actually are quite different and thus might require different workflows and visual designs after all. This is why we try to distinguish these carefully.
Kind regards!
Jour Fixe, 12 MAY 2025: We highly appreciate this suggestion and accept the PR for trunk. As Timon already asked for, please add some information concerning A11y to the rules of the UI element.
Thanks for the looking at it in the Jour Fix! I am really happy that it got approved.
:heavy_check_mark: Accessibility hints are sufficient, I think. KS Documentation mentions the accessibility hint, which should prevent confusion from screen reader users and make clear what happens on input. :heavy_check_mark: Changed the (hopefully last) instance of the old naming :heavy_check_mark: Failing automatic tests are unrelated to the files in this PR
I think this is good to go :)
Some additional notes about accessibility: During an accessibility workshop with Jan Hellbusch organized by the Kompetenzzentrum barrierefreie digitale Hochschulverwaltung NRW on April 9th I had the presenters critique this UI component. The only thing that this UI component doesn't have, is a live-area announcing how many filter results were found - but most components like this don't have it, so the presenters didn't see any urgency to add it. Especially, because all other concerns and requirements were addressed: :heavy_check_mark: Expand/collapsed is properly handled by the button :heavy_check_mark: Focus after expanding is handled :heavy_check_mark: Form can be operated entirely in focus mode :heavy_check_mark: Hint text announces what the search field does, therefor a live-area announcing the results is not necessary In conclusion, they confirmed that the component follows best practices.
Hi @catenglaender
Here a first feedback from @yvseiler . Note that one from @thibsy will follow as well:
Hi @catenglaender
Thank you a lot for contributing to ILIAS, what a nice and helpful component.
Please implement the following changes:
- [x] Regarding the comment above on “Search” and ‘Filter’ and the differentiation of the two, my suggestion is that the field for entering keywords be renamed to “Find Item” or “Filter by” instead of the current “Search”. We had a similar discussion in relation to “Add New Item” where ‘Find’ or “Filter” was found to be the most appropriate word for the interaction performed here.
- [x] There are 3 errors found by the HTML Validator for this component, please have a look on those (they all appear near ): «The element a must not appear as a descendant of the button element.»
`Error: The element a must not appear as a descendant of the button element.
From line 5815, column 54; to line 5815, column 98
y: none;"><a class="glyph" aria-label="Expand Content"><span
Error: The element a must not appear as a descendant of the button element.
From line 5884, column 42; to line 5884, column 87
style=""><a class="glyph" aria-label="-collapse/back-"><span
Error: The element a must not appear as a descendant of the button element.
From line 5961, column 9; to line 5961, column 45
><a class="glyph" aria-label="Remove"><span `
kindly, @Amstutz and @yvseiler
Some more changes:
- more re-naming of "search" to "filter" both in code and in translation strings
- invalid html in example has been corrected
Let's get this through the door y'all... Some people are already waiting for this component :slightly_smiling_face:
Hi,
thanks for your detailed feedback!
- Usability I: This is two expectations from different user groups colliding. Users with sight expect a read more/less style button to always be at the end of a list and have no problems navigating back up with their mouse to the field inside. However, for people relying on screen readers expanding first, then going inside for the next operation is the correct order. In other words this is an read more/less expander for people with sight and an accordion for people relying on screen reader. As even people with sight will likely intent to go into the expanded area when they hit expand, I would highly recommend keeping it the way it is.
- FieldSelect is out of scope for now. I would have to look into it, but generally any HTML5 element that has children in the shadow DOM or uses the browser's/OS' components will probably need a different implementation.
- The json data is in public/assets/ui-examples/misc/ but it will only end up there if you use the update function of the ILIAS setup when using an existing dev environment.
- Usability II: This is how all forms behave and screen reader users will now this. Enter is for clicking buttons, spacebar for operating check and radio boxes. See: https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/examples/checkbox-mixed/ However, we could make buttons operable via Spacebar as well. Blocking Enter to submit on some areas of the form is not the norm but not the craziest thing to do for long forms. However, I would ask to make this a separate discussion as this should be a general design decision effecting all forms.
- the name wrapInHasQuickFilterContext() is following the established wrapInFormContext() - mechanically those two do very similar things.
- Will look for more names to replace. Not sure I agree with all the concrete suggestions though and might give some of them some thought. I am strongly against snake_case in SCSS class names as it looks visually similar to the double underscore used by BEMIT, the naming convention that we use in css. BEM naming convention uses kebab-case dashes in element names.
- the name QuickFilter was deliberately chosen to distinguish this filter from the filter UI component. Just from my own anecdotal experience I have seen a singular searchbar textfield often being referred to as QuickSearch or QuickFilter. Alternative suggestions: SimpleFilter, ListFilter, FieldFilter, LocalFilter - may favorite is FieldFilter, because that is very directly what it does... it filters this field.
- Usability III: this is the browsers default behavior. Radio und checkboxes must be selected with arrow keys, tab jumps over them. See: https://www.w3.org/WAI/ARIA/apg/patterns/radio/examples/radio/
- Usability IV: It's intentionally not selectable because the preview/summary mode is also not interactable via mouse when collapsed. However, it should be read out by screen readers when selecting the field container. I will double check the aria-label/describedby wiring.
- I will look into how the interface is exposed, but will probably have to ask someone.
- example II: This is intentional. The use case for style selection is what got this component greenlit and partially funded. I think people need to understand what a useful component this is for the selection of complex objects as we have no other field like it.
- I will look into improving the example descriptions, however, I am strongly against detailed visual descriptions as visual decisions might change globally and testcases which have unnecessarily detailed descriptions caused confusion an delayed releases in the past. I would prefer to describe actions rather than appearance.
- pretty sure I already renamed the examples... your branch might not be up to date or I didn't push the changes correctly.
- I am actually very glad that forms in ILIAS have as a whole developed away from complete HTML string comparison tests. Global changes in forms are not rare these days (and another one is soon fixing accessibility) and frequently caused tedious work to fix with no actual value. I would agree though that we could check for more: the presence of certain buttons for example. General HTML validity in my opinion would be a big one, because that is the actual dangerous one that can happen in HTML templates and mess up a whole page. But I would strongly advocate for being a bit more flexible with regard to the actual order and position of nodes.
- You mean DI for passing the elements of the filter into the class instead of having the class search them in the document? Will do.
- JS Unit Tests: Do you have an example for Unit tests that check DOM manipulation? As far as I know, having a script running in node doing actual DOM stuff is not that trivial. But I will try it... maybe it won't be a problem as the class might not actually look at the document anymore if all that has been refactored to outside of it.
- I will see if I can figure binding out.
- I will change the getter / setter
So I distill the following to dos out of all this... @thibsy (or anyone elese) feel free to add if I missed something or where you disagree on my reasoning for not changing it.
- [ ] replace the term search wherever it is still left
- [ ] more renaming... I am strongly against renaming the thing just "filter" as this puts it to close to the actual filter component, but how about field filter or field filter context?
- [ ] CSS change to follow BEM convention, not snake_case or camelCase but kebab-case for element names.
- [ ] Pass dependent elements of the filter into the class instead of having the class search for them
- [ ] remove bind function
- [ ] remove JS keyword get and set
- [ ] add JS unit tests
- [ ] add details to example description
- [ ] expand PHP Unit tests, but I would advice against just comparing a complete chunk of html
- [ ] screen reader should read out selected elements when the filter is collapsed and container is selected
- [ ] one I noticed myself: disable items jumping to top when navigating radio buttons with the arrow keys
These are imo bigger issues/projects beyond this scope, but worth discussing elsewhere:
- Should forms not submit on enter when you are in a field? This would be against the norm, but given how long settings forms are this might prevent accidental submission. I think though that users not using screen readers using the keyboard are more likely to make this mistake than screen reader users who know this convention.
- Making a select field filterable would require to not use the HTML5 dropdown but to build a lookalike. We could also derive a filterable single select from the radio component as this is the most common way to provide the behavior under the hood for a fake.
- Discussion if unit testing should just check complete html renders. I suspect this wastes more time than it provides value. Do we really want literally hundreds of unit tests fail if we change e.g. the dom structure of the glyph component? Isn't it enough if the glyph test fails, you can fix that and all is well again. The only full render html unit test I would strongly recommend is for HTML W3C validity.
Alright, hope I got it all. Thanks again for the detailed look. I really hope though that this is over soon :laughing:
All the best, Ferdinand
Hi @catenglaender,
Thanks for unpacking my review and expanding on some of my remarks. Let me follow up on some of your answers:
- [ ]
Field\MultiSelectexample I: I knew that it ends up there, but I would like to know how it ends up there =). Just curious, because I thought we'd have to announce this asset in the UI framework, which is seemingly not the case? - [x]
Field\hasQuickFilterTextfieldI: you're right, this would probably be misleading. Inspired by your "ListFilter" suggestion, please rename this interface toField\HasOptionFilter. I thinkField\HasFieldFiltercould also mean that the field itself is filterable from the form context, and what we are actually doing here is filtering possible options.- [x]
Field\Renderer::wrapInHasQuickFilterContext(): agreed. Definitely give some of them more thought, just wanted to describe what I meant with renaming this stuff =).
- [x]
- [x]
Field\hasQuickFilterTextfieldII: maybe this clarifies, otherwise HMU on Discord: instead of adding this interface to the list of interfaces e.g. theMultiSelectclass implements, you can add it to the list of interfaces theMultiSelectinterface extends from. This way, the class will implement this interface by inheritance. Otherwise a consumer cannot look at theMultiSelectinterface and tell that its also aHasOptionsFilter. - [x] Example descriptions: I agree, definitely stick to describing actions.
- [x] PHPUnit tests: I absolutely agree with you. That's why I took the time to write about creating rendering unit-tests which can withstand changes in other components (HTML). You can achieve this by applying these techniques. Here are some examples:
- [x] (S)CSS class names: thx for clarifying! Let's put a pin in the camelCase convention.
- [x] Dependency injection (DI): that's what I mean, correct.
- [x] JS unit tests: there are some unit tests which use JSDom to achieve this. However, I personally dislike this method and will therefore give you another example as well =)
This should (more or less) confirm your extracted todo's. Thx for your structured feedback.
Kind regards, @thibsy
Hello everyone,
With regard to accessibility, I would like to provide the following feedback:
The discussions around live regions are well known. I therefore recommend using a live region to ensure that changes are announced clearly and in good time for screen reader users. Two possible implementation variants:
1.) add remainer element aria-live=“polite” and connect
- Another possibility would be to work with aria-live, where a result is announced after each character (one character => e.g. 143 results; two characters e.g. 43 results, three characters e.g. 4 results. This could possibly be combined with the option of displaying the results individually using a button. When focusing using the keyboard (or clicking), the screen reader could then read out the entries accordingly and the results would then also be displayed for sighted users.
Debounce/frequency limit (e.g. 300 ms delay) should be used for both 1.) and 2.), so that not every character is spoken during fast input.
King regards, Annett
The accessibility feature to announce the number of results has been implemented (sound on to hear the screen reader recorded in the video): https://github.com/user-attachments/assets/88a8afed-0f6a-4e59-b749-8afb56c435ec Debounce had to be 500ms for Ubuntu Orca to not skip an expected announcement.
All review tasks have been addressed to the best of my abilities (and thanks to the help from colleagues and @thibsy beyond my abilities).
Failing unit tests are not mine... that's another filter :grimacing:
I would be really happy if we could finally merge this. I know of at least 2 projects who are waiting for this component, which makes me really excited. :smiley:
Thank you for your PR going over some details. Very helpful! :slightly_smiling_face:
:heavy_check_mark: The template entanglement was a conscious choice made for two reasons:
- I want skin designers to notice that they need to be more careful than usual when changing this html template and be aware which context they might have to check additionally besides just the input field itself
- I want to enable Kitchen Sink devs be able to just slap these classes to their list and item elements this way get the pin to top sorting and animations with minimal effort since they are mostly done with scss not JS. I did this before I realized that multiselect and radio should behave slightly different, so not sure how universal these utility classes will actually be for other fields. Technically, the container would work to address the child elements just fine both in JS and CSS. I do wonder though if it is a good idea that people doing unrelated work on the radio and multiselect have no clue that they could potentially break the optionFilter. At some point I was thinking about having a seperate html tpl for the option filter variation of the fields, but these kinds of variant tpls tend to go out of sync with one quickly becoming less maintained.
That was my thinking so far. Of course, we can keep tweaking this.
:heavy_check_mark: Comment about the debounce value added
:heavy_check_mark: Unit Tests were of the general KS filter components. Fixed them anyway.
:heavy_check_mark: Rebase done