Skip to content
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 terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move auth components from shadow DOM to light DOM #18015

Merged
merged 8 commits into from
Sep 26, 2023

Conversation

piitaya
Copy link
Member

@piitaya piitaya commented Sep 25, 2023

Proposed change

Move the auth component from shadow DOM to light DOM to have better (full?) password manager support.
It's seems to work (at least with 1password). Tested in Chrome desktop, Safari desktop, Chrome Android and Safari iOS.

Some components are duplicated (ha-auth-form, ha-auth-textfield, ha-auth-form-string) so we don't need to touch original component to add light dom support.

All previous features works and the design is the same as before.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (thank you!)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Example configuration

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue or discussion:
  • Link to documentation pull request:

Checklist

  • The code change is tested and works locally.
  • There is no commented out code in this PR.
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

Comment on lines 155 to 158
protected fieldElementName(type: string) {
return `ha-form-${type}`;
}
Copy link
Member

Choose a reason for hiding this comment

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

This should be handled in core eventually, it can provide a different type for the auth text fields.

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought that too at the beginning.
Don't you think we can keep this in front-end as it is a front-end issue. We can remove this code once shadow dom support is added to password manager (never? 😂)

Comment on lines 10 to 13
protected fieldElementName(type: string): string {
if (type === "string") {
return "ha-auth-form-string";
}
return super.fieldElementName(type);
}
Copy link
Member

Choose a reason for hiding this comment

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

This should be handled in core eventually, it can provide a different type for the auth text fields.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, so we don't have to duplicate ha-form but only add an option to render without shadow dom.

@bramkragten bramkragten mentioned this pull request Sep 25, 2023
8 tasks
@steverep
Copy link
Member

For now ha-auth-form-string is only a copy of ha-form-string but it should be styled with material guidelines and we need to add a11y support.

I can review for a11y if and when we might be ready to merge, so just let me know. Also, just throwing out an idea, maybe use Vaadin components here to gain most of the Material styling (e.g. their canned login form)? Advantage is that they already proxy everything to light DOM by design, and it might be easier to maintain/swap out when the bigger password managers actually decide to implement support.

@bramkragten
Copy link
Member

If we use Vaadin we will stil need to place that inside the light DOM, and it gives us a lot less flexibility.

@steverep
Copy link
Member

steverep commented Sep 25, 2023

If we use Vaadin we will stil need to place that inside the light DOM, and it gives us a lot less flexibility.

AFAIK, they switched most if not all their components to use this "teleport" system to render in the light DOM. One of the impetuses was for better a11y because a lot of ARIA attributes take ID references, which quickly break when shadow trees get involved.

In other words, I think just using them inside the existing ha-auth-flow would allow deleting the polyfill.

@bramkragten
Copy link
Member

bramkragten commented Sep 25, 2023

Only if you use the modal version:

Login is incompatible with password managers if placed inside another component’s shadow root. [1] This isn’t an issue, though, when using Login’s modal overlay.

@steverep
Copy link
Member

Hmm... RTFM Steve 😊

@piitaya piitaya force-pushed the improve_password_manager_support branch from 8a17af6 to b3bb005 Compare September 25, 2023 21:10
@piitaya
Copy link
Member Author

piitaya commented Sep 25, 2023

I found a way to load ha-textfield styles. The only thing missing is the required helper on the field but I think we can live without it 😅 Done !

@bramkragten I preferred to duplicate some component for auth instead of adding option to ha-form and ha-form-string and ha-textfield so it's easier to maintain and we only have to remove these file once the shadow dom is supported by password manager. WDYT?

@piitaya piitaya marked this pull request as ready for review September 26, 2023 09:21
@piitaya
Copy link
Member Author

piitaya commented Sep 26, 2023

Ready to review !
@steverep, the html code isn't changed so accessibility support should be the same but I would prefer a review from you 🙂

Comment on lines +79 to +88
<style>
.action {
margin: 24px 0 8px;
text-align: center;
}
.store-token {
margin-top: 10px;
margin-left: -16px;
}
</style>
Copy link
Member

@bramkragten bramkragten Sep 26, 2023

Choose a reason for hiding this comment

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

Let's keep this in static get styles, that should also work when using light dom right?

Copy link
Member Author

@piitaya piitaya Sep 26, 2023

Choose a reason for hiding this comment

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

static styles doesn't work with light dom. That's the reason I use this hacky thing 😅

const style = document.createElement("style");
style.innerHTML = HaTextField.elementStyles as unknown as string;
this.append(style);

Copy link
Member

Choose a reason for hiding this comment

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

I thought Lit would do that for use, but ok 🤷‍♂️

Copy link
Contributor

Choose a reason for hiding this comment

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

Because that's something that's done in the default createRenderRoot implementation.

@piitaya piitaya force-pushed the improve_password_manager_support branch from 667a84a to 4f9df62 Compare September 26, 2023 15:32
@piitaya piitaya merged commit d8c98d8 into dev Sep 26, 2023
8 checks passed
@piitaya piitaya deleted the improve_password_manager_support branch September 26, 2023 18:17
Copy link
Member

@steverep steverep left a comment

Choose a reason for hiding this comment

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

Was in the middle of reviewing... 😞

Aside from my comments inline, there is a significant a11y issue now. The password field is labeled with the username text. This is because the input has aria-labelledby="label", so now it just finds the first element in the document with that ID, which is of course for the username. Note they don't have helpers so not strictly a problem, but aria-controls and aria-describedby would also be broken.

Either need to give everything distinct id attributes, or delete the aria-labelledby as it's redundant with the wrapping label element anyway.

// add parent style to light dom
const style = document.createElement("style");
style.innerHTML = HaForm.elementStyles as unknown as string;
this.append(style);
Copy link
Member

Choose a reason for hiding this comment

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

Most browsers don't care I think, but strictly speaking, it should be attached to document.head.

// add parent style to light dom
const style = document.createElement("style");
style.innerHTML = HaTextField.elementStyles as unknown as string;
this.append(style);
Copy link
Member

Choose a reason for hiding this comment

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

Same here

protected createRenderRoot() {
// add parent style to light dom
const style = document.createElement("style");
style.innerHTML = HaFormString.elementStyles as unknown as string;
Copy link
Member

Choose a reason for hiding this comment

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

Should use textContent instead for security and performance

@piitaya
Copy link
Member Author

piitaya commented Sep 26, 2023

Was in the middle of reviewing... 😞

Aside from my comments inline, there is a significant a11y issue now. The password field is labeled with the username text. This is because the input has aria-labelledby="label", so now it just finds the first element in the document with that ID, which is of course for the username. Note they don't have helpers so not strictly a problem, but aria-controls and aria-describedby would also be broken.

Either need to give everything distinct id attributes, or delete the aria-labelledby as it's redundant with the wrapping label element anyway.

@steverep I pushed a fix for this issue : #18032. Good catch!
I also improved the style definition because there style are global and not scoped.

@KTibow
Copy link
Contributor

KTibow commented Oct 1, 2023

This is causing some jankiness, at least in Firefox. See the attached screen recording. This was recorded using a development build on the latest version of dev.
Screencast from 2023-10-01 07-48-13.webm

@frenck
Copy link
Member

frenck commented Oct 1, 2023

@KTibow Let's keep track of such things in issues, instead of handled/closed PRs.

Thanks 👍

../Frenck

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants