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

Update EuiSearchBar to React 16.3 lifecycle #863

Merged
merged 6 commits into from
May 23, 2018

Conversation

chandlerprall
Copy link
Contributor

@chandlerprall chandlerprall commented May 21, 2018

  • fixed EuiSearchBar's proptypes (had been changed to the wrong object at some point)
  • refactored componentWillUpdate -> getDerivedStateFromProps / componentDidUpdate and updated support for externally-controlled query values
  • corrected how the custom date parser property / parse option is passed
  • onChange has changed from taking one argument, queryText, to taking an argument with the shape { query, queryText, error }
  • onParse has been removed
  • added an example of a controlled search bar to the docs

controlled searchbar example

Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

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

This looks really good, especially the addition of more tests! I have a couple questions though.

const schema = props.box ? props.box.schema : undefined;
const parseOptions = {
parseDate,
Copy link
Contributor

@cjcenizal cjcenizal May 21, 2018

Choose a reason for hiding this comment

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

Good catch here! I looked through the code a bit, and though parseDate isn't a recognized option, it does look like dateFormat is recognized. Maybe this was a mistake and originally parseDate was meant to be dateFormat. WDYT? Seems like it would make sense to support that here.

}
}

onSearch = (queryText) => {
try {
const query = parseQuery(queryText, this.props);
if (this.props.onParse) {
Copy link
Contributor

Choose a reason for hiding this comment

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

onChange / onParse were never called in the controlled-value case

I'm not sure I'm following. The example in the docs site is an example of this component in a controlled state, and the onChange and onParse callbacks are both being called. Did you mean in the uncontrolled case?

If you meant in the uncontrolled case, then I wonder if this is a case we should bother supporting, since this component is a bit special in that it's not used as part of a traditional form, but more used as a component for manipulating other parts of the UI.

Copy link
Contributor

Choose a reason for hiding this comment

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

I transplanted your tests to master and I see what you mean now. When query is changed externally, onParse and onChange are never called.

I'm not sure if this falls under the concept of a controlled input though. When I hear that term I think of a component which calls a callback when the user enters input, and then depends on the owner to re-provide that input in order to render it in the browser.

Now that I'm thinking about it more, the behavior added in this PR seems unexpected to me. What would be the use case?

@@ -75,4 +77,76 @@ describe('SearchBar', () => {

expect(component).toMatchSnapshot();
});

describe('controlled input', () => {
test('calls onParse callback when a new query is passed', () => {
Copy link
Contributor

@cjcenizal cjcenizal May 21, 2018

Choose a reason for hiding this comment

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

I think we can use Enzyme's setProps method to simplify this:

    test('calls onParse callback when a new query is passed', () => {
      const onParse = jest.fn();

      const component = mount(
        <EuiSearchBar
          query=""
          onParse={onParse}
        />
      );

      component.setProps({ query: 'is:active' });
      
      expect(onParse).toHaveBeenCalledTimes(1);
      const [[{ query, queryText }]] = onParse.mock.calls;
      expect(query).toBeInstanceOf(Query);
      expect(queryText).toBe('is:active');
    });

expect(queryText).toBe('is:active');
});

test('does not call onParse when an unwatched prop changes', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

And here too:

    test('does not call onParse when an unwatched prop changes', () => {
      const onParse = jest.fn();

      const component = mount(
        <EuiSearchBar
          query="is:active"
          isFoo={false}
          onParse={onParse}
        />
      );

      component.setProps({ isFoo: true });

      expect(onParse).toHaveBeenCalledTimes(0);
    });

@chandlerprall
Copy link
Contributor Author

chandlerprall commented May 22, 2018

Talked with @cjcenizal and decided to remove onParse callback and pass query, queryText, and error to onChange, and reducing to one informational callback removes confusion and follows the pattern for controlled components.

@chandlerprall
Copy link
Contributor Author

@cjcenizal take another pass?

@chandlerprall chandlerprall force-pushed the react-16-3-searchbar branch from 055e951 to 780040a Compare May 22, 2018 23:04
Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

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

Awesome! This looks much cleaner. I had a few small comments.

text: (
<div>
<p>
A <EuiCode>EuiSearchBar</EuiCode> can have its query controlled by a parent component by passing the `query` prop. Changes to the query will be passed back up through the <EuiCode>onChange</EuiCode> callback where the new Query must be stored in state and passed back into the search bar.
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor nit, instead of wrapping "query" in backticks, can we wrap it in <EuiCode>?


const component = mount(
<EuiSearchBar
{...requiredProps}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need requiredProps here, since that's just for testing that certain props like className are passed through to the root element.

component.setProps({
...requiredProps,
query: 'is:active',
onChange
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to set all of these? Can we just set the prop we want to change? These comments apply to the second test too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the enzyme docs, setProps is a "method that sets the props of the root component". It's all-or-nothing.

@@ -159,7 +159,7 @@ export class EuiInMemoryTable extends Component {
});
};

onQueryChange = (query) => {
onQueryChange = ({ query }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This method calls this.props.search.onChange(query). I think it might be more intuitive if we model EuiSearchBar's interface as closely as possible by instead calling this.props.search.onChange({ query, queryText, error }). It's another breaking change but it seems less surprising. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. This is a weird interception on the callback and it should continue to at least act like a passthrough.

@chandlerprall
Copy link
Contributor Author

@cjcenizal pushed changes

Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

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

🍔 Awesome!

@chandlerprall chandlerprall merged commit efef52f into elastic:master May 23, 2018
@chandlerprall chandlerprall deleted the react-16-3-searchbar branch May 23, 2018 20:36
chandlerprall added a commit to chandlerprall/eui that referenced this pull request May 24, 2018
* updated EuiSearchBar to React 16.3 lifecycle, refactored how its controlled-value option works

* revert dangling comma removal

* changelog

* Updated EuiSearchBar to better match the controlled component pattern, added example

* some more cleanups
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants