8000 Fix the Choices.js quirks by isolating it in a shadow root by m-vo · Pull Request #8261 · contao/contao · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix the Choices.js quirks by isolating it in a shadow root #8261

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
May 13, 2025

Conversation

m-vo
Copy link
Member
@m-vo m-vo commented Apr 3, 2025

Fixes #8248

Overview

We had numerous attempts at integrating Choices with our mutation observer based handling of components (#7901, #8075, #8203, …). The main issue is, that Choices wraps/unwraps the underlying select element by removing and readding it to the DOM, which is again triggering mutations. On top there are some global event-listeners. This all screams "race condition".

This PR therefore goes down a completely different route. The strategy basically looks like this:

  • On connect, create a sibling div element (the host) and hide the original element (add .hidden class)
  • Create a separate DOM (shadow root) with a clone of the original select element inside the host and initialize Choices on that element
  • When there are changes (change event on Choices-owned select), update the original element.
  • On disconnect/beforeCache, remove the host and show the original element again (remove .hidden class)

Now, no unwanted mutations occur anymore. In fact, the potentially hundreds of added Choices DOM nodes are also not part of the original DOM which should also make queries etc. on parent elements cheaper.

Implementation details

Choices was a contao-component, now we install it as a npm package (88a718c). We therefore do not load the respective css/js files in be_main anymore.

Due to isolation, the shadow DOM styles cannot target classes on the parent (custom properties such as our color presets penetrate the boundary, though). There is the :host selector, however, that targets the host element. In order to allow styling depending on the current color scheme, I moved the color scheme controller to the html element and made a small change, so that you now can have multiple outlet targets where the classes are applied (d7f298e). The host elements are now such targets.

Because global styles do not penetrate the boundary, we are only applying them to the shadow roots themselves. With webpack that can be done quite nicely: We use an annotated import to get the loader output and then create a CssStyleSheet object from it, that is shared across all controller instances (b076c53).

Speaking of styles… There were the choices default styles, our adjustments from the contao-component and the base styles (backend.css) - all of them overwriting each other and sometimes only working with a very specific specificity (say that 3 times in a row). Now that styles are isolated, I moved them in one file (assets/component/choices.pcss importing choices.js/public/assets/styles/choices.css) and refactored them. Even with a lot of testing, it is very likely that I broke something while doing so. So please have a close look at this when reviewing.

contao--choices:create event

Because the controller is now basically rewritten, I also added an event that lets you alter the config before the Choices instance is created. If we want to go with this one-line-addition, this PR will also supersede #8115 and #8154.

Usage:

selectElement.addEventLister('contao--choices:init', (event) => {
    const config = event.details.config;
    config.foo = bar; // alter config

    // If you need to target the select node, the Choices
    // instance is created on, use `event.details.select`.
});

@m-vo m-vo added the bug label Apr 3, 2025
@m-vo m-vo added this to the 5.5 milestone Apr 3, 2025
@m-vo m-vo self-assigned this Apr 3, 2025
@m-vo m-vo requested a review from leofeyer as a code owner April 3, 2025 10:43
@m-vo m-vo requested review from fritzmg and zoglo April 3, 2025 10:43
@leofeyer
Copy link
Member
leofeyer commented Apr 3, 2025

As discussed in the call, we also want to include the changes from #8154 in this PR.

@m-vo
Copy link
Member Author
m-vo commented Apr 3, 2025

As discussed in the call, we also want to include the changes from #8154 in this PR.

See 798f69a

Copy link
Member
@zoglo zoglo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JS part for this PR works flawlessly ❤️
The CSS part still needs more adjustments, more in the requested changes.

There's still the CSS in tl_filter > tl_select that has to be changed, however that would need the choices-container to get the tl_select class and reset a lot of other stuff, e.g. here:

Before:

image

image

After:
image


Meaning we would most likely need to add the tl_select class within the choices-controller but also another one to reset the styles so we don't have something like this:

image

@m-vo
Copy link
Member Author
m-vo commented Apr 4, 2025

There's still the CSS in tl_filter > tl_select that has to be changed, however that would need the choices-container to get the tl_select class and reset a lot of other stuff, e.g. here:

IMHO this can simply be fixed by switching to .tl_search > :not(span, strong) {…}.
See d0fef2b.

@m-vo m-vo requested a review from zoglo April 4, 2025 10:02
@zoglo
Copy link
Member
zoglo commented Apr 4, 2025

IMHO this can simply be fixed by switching to .tl_search > :not(span, strong) {…}. See d0fef2b.

Depending on how users were adapting this and adding new fields, this might break their layouts so I am unsure about it. Would adding a class for the new element + adding the styling not make it less prone to errors? Wdyt?

@m-vo
Copy link
Member Author
m-vo commented Apr 4, 2025

Depending on how users were adapting this and adding new fields, this might break their layouts so I am unsure about it. Would adding a class for the new element + adding the styling not make it less prone to errors? Wdyt?

We can also include all hosts, but I would rather do it the other way round and exclude any descriptions/labels. If targeting span and strong is too loose, we could give them a class. But IMHO overkill.

@m-vo
Copy link
Member Author
m-vo commented Apr 22, 2025

@zoglo You requested changes - is there anything left we need to address?

@zoglo
Copy link
Member
zoglo commented Apr 22, 2025

@zoglo You requested changes - is there anything left we need to address?

The first quirk is the width that has been removed and will change the widths of options accordingly to the selected option within tl_filter.

  • Readding the widths would fix this

The other one is somethintg about the shadow-root taking 32px height as briefly discussed on slack, something that causes a slight layout shift of 1px when focusing the select.

  • Adding the same height as inputs to the container containing the shadow-root will fix this as well

Maybe the screencast shows it better :) (switching between demo and the current PR)

8261.mp4

@zoglo zoglo self-assigned this Apr 23, 2025
@zoglo
Copy link
Member
zoglo commented Apr 23, 2025

@m-vo as discussed on Slack, I did make sure that the CSS does not change the layouting.

4df465b ensures that the existing widths are preserved for .tl_limit, .tl_sorting and .tl_subpanel.

4ff6c23 fixes a forgotten font-size for choices options

d166609 is a bit trickier... I did remove the margin within the shadow DOM but only retain it for a specific case where the instance is not part of the subpanels or filters and where it's not an initialized choices within the shadow dom (Hence the comment).

TL;DR - Same styles as before now :)

@zoglo zoglo requested a review from leofeyer April 23, 2025 17:01
zoglo
zoglo previously approved these changes Apr 23, 2025
@leofeyer leofeyer changed the title Fix Choices quirks by isolating it in a shadow root Fix the Choices.js quirks by isolating it in a shadow root May 12, 2025
@leofeyer leofeyer merged commit 707a313 into contao:5.5 May 13, 2025
17 checks passed
@leofeyer
Copy link
Member

Thank you @m-vo.

leofeyer added a commit that referenced this pull request May 13, 2025
#8261)"

This reverts commit 707a313.

# Conflicts:
#	core-bundle/public/backend.183ec4da.js.LICENSE.txt
#	core-bundle/public/backend.9ec43b5f.js.LICENSE.txt
#	core-bundle/public/backend.f83a03fe.js
#	core-bundle/public/backend.f83a03fe.js.LICENSE.txt
#	core-bundle/public/entrypoints.json
#	core-bundle/public/manifest.json
@zoglo
Copy link
Member
zoglo commented May 13, 2025

I reopened #8248 so we don't forget that this PR has been reverted in 18e970d due to a race condition within the onSubmitCallback on the selects.

It's a race condition where the Backend.autoSubmit registered on the native select would fire the values earlier than the select being updated (Unsure right now what triggers the onchange because the value change should have done that)

@zoglo zoglo mentioned this pull request May 13, 2025
7 tasks
@leofeyer leofeyer removed this from the 5.5 milestone May 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0