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

[Management] Index pattern step in React! #15936

Merged
merged 10 commits into from
Jan 16, 2018

Conversation

chrisronline
Copy link
Contributor

@chrisronline chrisronline commented Jan 9, 2018

Relates to #15905
Resolves #15922

Background

This PR is the first step at refactoring the index pattern creation wizard from Angular to React. The management team decided to do both the EUI and React refactor at the same time.

Details

This PR removes all existing angular code around the index pattern step (which is the first step, where you select an index pattern). It replaces it with React code and also creates a different separation than previously existed.

The file structure looks like:

  • components/ - This contains all the parts of the index pattern creation wizard, including this step
  • constants/ - This is where the constants used throughout index pattern creation are stored
  • lib/ - This is where helper functions exist, isolated and dependency-free

Further Work

Currently, the index.js within components/step_index_pattern exposes a function that does the actual React rendering, but this will eventually be deprecated as the shell (the code directly within sections/indices/create_index_pattern_wizard) has been converted.

Tests

  • lib/
  • components/step_index_pattern
  • components/step_index_pattern/components/indices_list
  • components/step_index_pattern/components/loading_indices
  • components/step_index_pattern/components/status_message
  • components/step_index_pattern/components/header

Screenshots

screen shot 2018-01-09 at 1 14 30 pm

screen shot 2018-01-09 at 1 14 38 pm

screen shot 2018-01-09 at 1 14 46 pm

screen shot 2018-01-09 at 1 15 11 pm

screen shot 2018-01-09 at 1 15 18 pm

screen shot 2018-01-09 at 1 15 26 pm

@chrisronline chrisronline self-assigned this Jan 9, 2018
@chrisronline chrisronline changed the title Index pattern step in React! [Management] Index pattern step in React! Jan 9, 2018
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.

Wow it's amazing how much more readable this becomes once you throw a little React at it! Great job splitting this up and refactoring the code. I had a few suggestions and questions. I know you already noted this in the PR description, but some snapshot tests for the new components would be great at some point.

@@ -124,7 +128,10 @@ <h2 class="euiTitle euiTitle--small euiTextColor euiTextColor--subdued">
ng-switch="controller.wizardStep"
>
<!-- Specify index pattern -->
<step-index-pattern
<div ng-switch-when="indexPattern">
<div id="stepIndexPatternReact"></div>
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use ng-react to create a temporary directive to wrap the component? This way we'd be using a consistent mechanism for bridging angular to react. Here's an example of how I'm defining the toasts directive wrapper and here's an example of how it's consumed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess I didn't think it mattered since this file is going away in #15905

Copy link
Contributor

Choose a reason for hiding this comment

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

OK your call

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 use ng-react either.

this.state = {
matchingIndices: [],
isLoadingIndices: false,
query: props.initialQuery || '',
Copy link
Contributor

@cjcenizal cjcenizal Jan 9, 2018

Choose a reason for hiding this comment

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

We can use defaultProps to pass in an empty string instead of applying a default here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good!

@@ -0,0 +1,3 @@
export function getPaginatedIndices(indices, page, perPage) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests for this would be great in a later iteration.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would maybe use the EuiPager component. It already does the math for this for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Where can I find that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry this one's a little hidden... it's with the services: https://github.com/elastic/eui/tree/master/src/services/paging

@@ -0,0 +1,11 @@
export function isIndexPatternQueryValid(pattern, illegalCharacters) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests for this would be great in a later iteration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

<EuiFlexItem grow={false}>
<EuiText>
<EuiTextColor color="subdued">
Looking for matching indices...
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we treat this loading feedback the same way we treated the feedback the same way we treat the loading feedback when checking for indices? With a spinner, left-aligned, etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup!

</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s"/>
<div>
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this div necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, removing

Copy link
Contributor

@bmcconaghy bmcconaghy left a comment

Choose a reason for hiding this comment

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

Looking good. Some tests would be nice to add I think before merging.

let containsErrors = false;
const errors = [];
const characterList = ILLEGAL_CHARACTERS.slice(0, ILLEGAL_CHARACTERS.length - 1).join(', ');

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this line here? You are taking every character but the last one to display, but don't know why.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, nice catch. I'm excluding the last one because it's an empty space and it looks off in the UI if I show that with a comma. Should I just show it? Or add a comment here?

<EuiFlexGroup justifyContent="spaceBetween" alignItems="flexEnd">
<EuiFlexItem grow={false}>
<EuiForm
isInvalid={showingIndexPatternQueryErrors && containsErrors}
Copy link
Contributor

Choose a reason for hiding this comment

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

since you use this twice I would do this above:
const isInvalid = showingIndexPatternQueryErrors && containsErrors;

<EuiButton
iconType="arrowRight"
onClick={() => goToNextStep(query)}
isDisabled={containsErrors || indices.length === 0}
Copy link
Contributor

Choose a reason for hiding this comment

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

Same kind of thing as above, maybe a isDisabled constant.

@@ -124,7 +128,10 @@ <h2 class="euiTitle euiTitle--small euiTextColor euiTextColor--subdued">
ng-switch="controller.wizardStep"
>
<!-- Specify index pattern -->
<step-index-pattern
<div ng-switch-when="indexPattern">
<div id="stepIndexPatternReact"></div>
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 use ng-react either.

this.goToTimeFieldStep();
$scope.$apply();
}
));
};
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems gross to me, but I'm assuming this code is transitional in nature. If this is just a step along the road to cleaner and more complete reactification, then this is OK. If this is intended as an integration pattern, then I have some issues with this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, just transitional

@@ -0,0 +1,3 @@
export function getPaginatedIndices(indices, page, perPage) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would maybe use the EuiPager component. It already does the math for this for you.

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.

LGTM! I had a few suggestions but none of them are blockers.

import sinon from 'sinon';

describe('createReasonableWait', () => {
it('should eventually calls the callback', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should just verify that this function returns a promise (i.e. that the return object provides a then method). Considering most tests take a few ms to execute, I think forcing this one to take 500 ms is too high a cost for such a simple unit of code.

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 call, I'll change that

};

// https://github.com/facebook/jest/issues/1377
const syncify = async (fn) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we migrate this to EUI? We have a test directory which publishes other test helpers. Seems like this one would be handy.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, sounds good

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

If you rebase master so you get Jest 22 you don't need syncify anymore:

await expect(myPromise).rejects.toThrow('failure');

return acceptableIndices.slice(0, MAX_NUMBER_OF_MATCHING_INDICES);
}

export function getWhitelistedIndices(indices, isIncludingSystemIndices, query, matchingIndices) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The word "whitelisted" threw me off in this function name. I kept thinking that it was responsible for whitelisting somehow, but I think that's actually just an implementation detail we can ignore from an interface standpoint. Since you're passing in some initial indices and then getting back different lists of indices which match to various degrees, how about renaming this function to matchIndices?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed!

@@ -0,0 +1,7 @@
export const canAppendWildcard = ({ data: keyPressed }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why destructure an object here if there's only one prop? Why not pass in the keyPressed argument directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, no need

@chrisronline chrisronline force-pushed the eui/reactify-ipc-step1 branch from ae7345d to 7f97352 Compare January 11, 2018 17:59
return null;
}

const indices = getMatchedIndices(allIndices, partialMatchedIndices, query, isIncludingSystemIndices);
Copy link
Contributor

@cjcenizal cjcenizal Jan 11, 2018

Choose a reason for hiding this comment

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

We're calling getMatchedIndices here and on lines 94 and 139. Would it be more efficient to call it once within render() and then pass the result as an argument to renderStatusMessage(), renderList(), and renderHeader()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For sure!

This was referenced Jan 12, 2018
@chrisronline
Copy link
Contributor Author

@cjcenizal Is this still good to go?

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.

⛳️ MERGE WORTHY!

@kimjoar
Copy link
Contributor

kimjoar commented Jan 16, 2018

Before merging, maybe rebase master (so you get Jest 22), remove syncify and do:

await expect(myPromise).rejects.toThrow('failure');

@chrisronline
Copy link
Contributor Author

@bmcconaghy I'm going to merge this as soon as tests pass so I can move on to the second step, but please let me know if you want any changes.

@chrisronline chrisronline force-pushed the eui/reactify-ipc-step1 branch from a0a84b0 to 16d34d8 Compare January 16, 2018 15:20
@chrisronline chrisronline merged commit bc3f360 into elastic:master Jan 16, 2018
chrisronline added a commit to chrisronline/kibana that referenced this pull request Jan 16, 2018
* Index pattern step in React!

* Remove dead lines

* Ensure this only shows up when applicable

* PR feedback

* Use pager

* Add tests for lib/

* PR feedback

* Tests and PR feedback

* More tests and PR feedback

* New jest functionality
@chrisronline chrisronline deleted the eui/reactify-ipc-step1 branch January 16, 2018 16:10
chrisronline added a commit that referenced this pull request Jan 16, 2018
* Index pattern step in React!

* Remove dead lines

* Ensure this only shows up when applicable

* PR feedback

* Use pager

* Add tests for lib/

* PR feedback

* Tests and PR feedback

* More tests and PR feedback

* New jest functionality
@chrisronline
Copy link
Contributor Author

Backport

6.x: 0b36c0a

@chrisronline
Copy link
Contributor Author

@azasypkin

6.2 just got a single part of the wizard in React, but when I did the original work, I did it all in a single branch and attempted to break it out into separate branches to make the PR process better as well as allowing us to incrementally test each section.

I think what happened is that constants file was used in the full branch but not necessary in this one but slipped in accidentally

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

Successfully merging this pull request may close these issues.

5 participants