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

Issue #11827 Fix ShadowDOM input bug with setDefaultValue #11986

Closed
wants to merge 8 commits into from

Conversation

chrisdieckhaus
Copy link

@chrisdieckhaus chrisdieckhaus commented Jan 8, 2018

Fixes Issue #11827.

#7253 successfully fixed the number input backspace bug with removing decimals by not re-setting the default value if node === activeElement. However, shadowDOM elements are wrapped in a shadowRoot element, so comparing them to node always triggered an update to node.defaultValue (which triggered input's validation that removed the decimal point). I've updated setDefaultState to check for shadow DOM element, and if so, iterate to the root activeElement to compare against node.

I haven't added tests yet because I need some input on that. I'm assuming the tests should go in ReactDOMInput-test.js. However, I'm not sure how to properly mock a shadow DOM element in these tests, and would appreciate some input on that.

  • complete PR checklist
  • add tests

@jquense
Copy link
Contributor

jquense commented Jan 8, 2018

Generally this sort of thing is "tested" via a manual fixture. Check out fixtures/dom from the repo root for some existing ones and you can add one there. There is no automation, so don't be confused if you can't run them via a cli or something :P

Copy link
Contributor

@nhunzaker nhunzaker left a comment

Choose a reason for hiding this comment

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

Looks good. Just need to verify manually

let activeElement = node.ownerDocument.activeElement;

if (activeElement && activeElement.shadowRoot) {
activeElement = getShadowElementRoot(node.ownerDocument.activeElement);
Copy link
Contributor

Choose a reason for hiding this comment

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

Huh. Nice!

@aweary
Copy link
Contributor

aweary commented Jan 8, 2018

@chrisdieckhaus looks like you need to run yarn prettier so CI passes :)

@chrisdieckhaus
Copy link
Author

Added manual test case to number-inputs/index.js. Let me know if that's not the right place to put that.

The field should read "3.", preserving the decimal place
</TestCase.ExpectedResult>

<NumberTestCase />
Copy link
Contributor

Choose a reason for hiding this comment

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

The formatting is perfect here, but it doesn't look like this attaches to a Shadow DOM. What is the best way to do that?

Copy link
Author

Choose a reason for hiding this comment

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

I’m not entirely sure how to deal with a Shadow DOM in the test fixtures. I’ll look into it.

Copy link
Contributor

@nhunzaker nhunzaker left a comment

Choose a reason for hiding this comment

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

Marking this as "request changes" for the issues list.

@chrisdieckhaus
Copy link
Author

chrisdieckhaus commented Jan 17, 2018

Hi @nhunzaker is this good to merge?

attachShadowDomElement(hostElementId, stringHtmlElement) {
var hostElement = document.querySelector(hostElementId);
var shadow = hostElement.attachShadow({mode: 'open'});
shadow.innerHTML = stringHtmlElement;
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this injects<input type="number" value={this.state.value} onChange={this.onChange} />, which is the form React requires, however this won't wire on onChange, just set an HTML string uncontrolled by React. This will create a false positive for the bug because React doesn't interact with this injected markup.

We need to render a React component inside of a shadow dom sub-tree. It looks like #11827 includes a reproduction that does just this:
https://jsfiddle.net/69z2wepo/94566/

Drawing from that example, you could do:

import NumberTestCase from './NumberTestCase';

export default class NumberTestCaseShadowDOM extends React.Component {
  componentWillMount() {
    // I tucked the definition in here because I wasn't sure what extending HTMLElement
    // would do in IE9. Maybe this isn't necessary.
    class ShadowNumberInput extends HTMLElement {
      connectedCallback(){
        ReactDOM.render(<NumberTestCase />, this.attachShadow({ mode: 'open' }));
      }
    }

    customElements.define('with-shadow-dom', ShadowNumberInput);
  }

  render() {
    return (<shadow-number-input {...this.props} />);
  }
}

Here's a working example: https://jsfiddle.net/2n77ootd/2/

Copy link
Author

Choose a reason for hiding this comment

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

So whenever I go with that sort of an implementation, I get the following error when it tries to render the shadow element:

TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.

It seems like custom elements are not compatible with ES5 style classes. I'm able to get it working with their suggested solution, native-shim.js. Is this something we'd be able to include in react? Or should we be looking for a different solution to this?

Copy link
Author

Choose a reason for hiding this comment

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

@nhunzaker thoughts on this?

@nhunzaker
Copy link
Contributor

nhunzaker commented Jan 17, 2018

@chrisdieckhaus Sorry, not quite yet. The test case needs to render React inside of a shadow dom root. Like use ReactDOM.render inside of a Shadow DOM to verify that it coordinates properly with the parent DOM (Sunny DOM 😛?).

I left some thoughts. Essentially I think we just need to render the same number input test case inside of a Shadow DOM, then mount an associated custom element with React (inside of the render method of the fixture component).

@chrisdieckhaus
Copy link
Author

Ah yeah I see what you mean. I'll play around with it and see what I can come up with.

@chrisdieckhaus
Copy link
Author

@nhunzaker, I added the native-shim.js file I mentioned earlier. I'm not sure if it's something that's acceptable to add, though. Take a look at it when you get a chance.

@nhunzaker
Copy link
Contributor

Hey! Awesome. I'm out of pocket for a bit, but I'll look at this first thing in Monday (traveling). Thank you for sticking with me!

@chrisdieckhaus
Copy link
Author

Ah this is interesting. Custom Elements are not supposed (yet) in Firefox, which prevents the Number Input test case page from rendering.

@nhunzaker
Copy link
Contributor

@chrisdieckhaus Hmm. We probably need to do a compatibility check. I think it would be fine if the fixture said something like "This browser does not support custom elements, this fixture is invalid".

I apologize for getting back to this as late as I have. If you'd like me to pick this up, I'd be happy to carry this through.

@gaearon
Copy link
Collaborator

gaearon commented Aug 9, 2018

Would #11896 also fix this?

@stale
Copy link

stale bot commented Jan 10, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

@stale stale bot added the Resolution: Stale Automatically closed due to inactivity label Jan 10, 2020
@stale
Copy link

stale bot commented Jan 17, 2020

Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you!

@stale stale bot closed this Jan 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Resolution: Stale Automatically closed due to inactivity
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants