-
Notifications
You must be signed in to change notification settings - Fork 842
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
Update EuiSearchBar to React 16.3 lifecycle #863
Conversation
There was a problem hiding this 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, |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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', () => { |
There was a problem hiding this comment.
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', () => { |
There was a problem hiding this comment.
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);
});
Talked with @cjcenizal and decided to remove |
@cjcenizal take another pass? |
…rolled-value option works
…rolled-value option works
055e951
to
780040a
Compare
There was a problem hiding this 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. |
There was a problem hiding this comment.
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} |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 }) => { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
@cjcenizal pushed changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🍔 Awesome!
* 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
EuiSearchBar
's proptypes (had been changed to the wrong object at some point)componentWillUpdate
->getDerivedStateFromProps
/componentDidUpdate
and updated support for externally-controlled query valuesonChange
has changed from taking one argument,queryText
, to taking an argument with the shape{ query, queryText, error }
onParse
has been removed