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

fix(Search): fix event passed to onChange when clearing input (#12133) #12140

Merged

Conversation

gi
Copy link
Contributor

@gi gi commented Sep 23, 2022

  • fix(Search): when clearing, pass a pseudo input change event instead of
    the mouse click event with which the internal event handler is called.

Closes #12133

When the close button is clicked, the internal event handler is called with the mouse click event.

Previously, the onChange prop was called with this event or a modified version. However, that event callback normally expects an input change event.

This fixes the issue by creating and sending a pseudo input change event to the onChange prop.

Changelog

Changed

  • Search: onChange is called with proper data shape when clearing input

Testing / Reviewing

  • Verified in Storybook
  • Added unit tests.

…on-design-system#12133)

* fix(Search): when clearing, pass a pseudo input change event instead of
  the mouse click event with which the internal event handler is called.
@gi gi requested a review from a team as a code owner September 23, 2022 05:56
@gi gi requested review from tay1orjones and aledavila September 23, 2022 05:56
@github-actions
Copy link
Contributor

github-actions bot commented Sep 23, 2022

DCO Assistant Lite bot All contributors have signed the DCO.

@netlify
Copy link

netlify bot commented Sep 23, 2022

Deploy Preview for carbon-components-react ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 5365473
🔍 Latest deploy log https://app.netlify.com/sites/carbon-components-react/deploys/633c5dd58ef1e70008493d59
😎 Deploy Preview https://deploy-preview-12140--carbon-components-react.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@gi
Copy link
Contributor Author

gi commented Sep 23, 2022

I have read the DCO document and I hereby sign the DCO.

@netlify
Copy link

netlify bot commented Sep 23, 2022

Deploy Preview for carbon-elements ready!

Name Link
🔨 Latest commit 5365473
🔍 Latest deploy log https://app.netlify.com/sites/carbon-elements/deploys/633c5dd52ef6160009aa033b
😎 Deploy Preview https://deploy-preview-12140--carbon-elements.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

Copy link
Member

@tay1orjones tay1orjones left a comment

Choose a reason for hiding this comment

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

Thanks for noticing this and putting up a PR! This is a quite old snippet of code - 5+ years at this point. I think the original intent of this code was probably to replace the existing event.target.value with an empty string.

 function clearInput(event) {
   if (!value) {
     inputRef.current.value = '';
     onChange(event);
   } else {
-    const clearedEvt = Object.assign({}, event.target, {
+    const clearedEvt = Object.assign({}, event, {
       target: {
         value: '',
       },
     });
     onChange(clearedEvt);
   }

   onClear();
   setHasContent(false);
   focus(inputRef);
 }

This would be the smallest change to rectify the root issue and I think the least likely to introduce a breaking change. I'd prefer not to change the signature and call structure if possible, or at least as little as possible, as these could inadvertently break folks who have relied on this since v8/v9.

Comment on lines +16 to +32
let wrapper;

// const container = () => wrapper.find(`.${prefix}--search`);
const button = () => wrapper.find('button');
const input = () => wrapper.find('input');
// const label = () => wrapper.find('label');

const render = (props) => {
if (wrapper) {
return wrapper.setProps(props);
}

wrapper = mount(<Search labelText="testlabel" {...props} />);

return wrapper;
};

Copy link
Member

Choose a reason for hiding this comment

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

Should this be in a beforeEach or beforeAll?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could be a beforeAll, but that would only be doing the same thing as this but with less clarity: the functions would need to be declared as let variables and then assigned in the before routine.

This code solves for an issue that I see a lot with front-end tests and which I see with the tests in this suite: if you render the element under test in a beforeEach routine, then it makes any other setup in nested contexts very difficult. You end up with multiple renders of the element.

In fact, I believe that some of the tests in this suite are finding the wrong element rendered.

If you move the mounting or rendering into each test/it, then you avoid this issue.

To avoid duplicating the possibly complex mount/render declaration, I have adopted this paradigm which works quite well in my experience.

Each nested context/describe can build on the props:

  • default: props reset
    • with size large: props.large = true
    • with size small: props.small = true
  • with onChange: props.onChange = jest.fn();
  • etc.

Each test simply becomes

render(props);
expect(...);

@gi
Copy link
Contributor Author

gi commented Sep 27, 2022

@tay1orjones The actual culprit is the first if block:

if (!value) {
  inputRef.current.value = '';
  onChange(event);
}

This clears the input's value, but it sends the click event from the button, which does not contain the target.value:

  • event: mouse click event
  • event.target: button element
  • event.target.value: undefined

Additionally, the else clause was sending an "event" such that the event data was really the target button element and the "target" was this manufactured object:

else {
  const clearedEvt = Object.assign({}, event.target, {
    target: {
      value: '',
    },
  });
  onChange(clearedEvt);
}
  • clearedEvt: button element not an event
  • clearedEvt.target: { target: { value: '' } }

In both cases from this clearInput handler, the onChange callback does not receive the same type of event or target which it otherwise receives from the normal change events when the user inputs text.

Alternatively, this could be:

    if (!value) {
      inputRef.current.value = '';
    }

    const inputTarget = Object.assign({}, inputRef.current, { value: '' });
    const clearedEvt = { target: inputTarget, type: 'change' };

Copy link
Member

@tay1orjones tay1orjones left a comment

Choose a reason for hiding this comment

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

That makes sense, thanks!

@kodiakhq kodiakhq bot merged commit de54f57 into carbon-design-system:main Oct 4, 2022
@gi gi deleted the issue-12133/search-clear-click-onchange branch October 11, 2022 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[Bug]: Search - clear button does not call onChange with proper event
3 participants