-
Notifications
You must be signed in to change notification settings - Fork 374
feat(WizardPattern): Add WizardPattern and StatefulWizardPattern components #268
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
Conversation
140b467 to
653fcd0
Compare
Pull Request Test Coverage Report for Build 1207
💛 - Coveralls |
|
It looks like there is some recent news on the "controlled" pattern... https://twitter.com/brian_d_vaughn/status/972237949113835525 I recommend the following changes to introduce You can test this storybook I deployed to see the same exact behavior... I am happy w/ this pattern if those tweaks are made (and I think it is much safer long term). The controlled pattern is very powerful for a lot of the @AllenBW thanks for the tests examples... seems they work out fine! |
| } = this.props; | ||
| // Don't allow step clicks to skip into the future, but skipping into the past is ok. | ||
| const nextStepClicked = stepIndex === activeStepIndex + 1; | ||
| const futureStepClicked = stepIndex > activeStepIndex + 1; |
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.
is this piece too opinionated? I think we have the case we want to prevent the user from going back on Step 5 in v2v...
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.
There may be steps that are defaulted and can be skipped. I believe the application should decide if it is OK to navigate to a step.
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.
Yeah. I didn't really finish figuring this part out because this behavior is totally turned off in v2v. I think I am going to add back in the disableGoto piece that we had added and then abandoned in v2v, so that you can turn these buttons off step-by-step, and like you say, the application can decide.
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.
From Slack, my plan to address this flexibly is:
i'll have props for
isValid,allowEnterandallowExit.allowEnterwill default to theisValidof the previous step, but you can override it if you need to. we can useallowExit: falseon our final step.
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.
is this piece too opinionated? I think we have the case we want to prevent the user from going back on Step 5 in v2v...
In VM provision wizard, it should be possible to skip steps (jump to a later step).
There may be steps that are defaulted and can be skipped. I believe the application should decide if it is OK to navigate to a step.
Exactly my thoughts.
@mturley +1 on the proposed props for controlling the step navigation.
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.
@vojtechszocs @jeff-phillips-18 @priley86 @AllenBW curious about your thoughts on the boolean props being available on the inner, stateless WizardPattern i.e:
<WizardPattern activeStepIndex={0} steps={[{ allowEnter: true, ... }, ...]} />
while on the stateful wrapper, we can pass something that will take the current step index as an argument (since when you're using the stateful wrapper, you probably are not tracking the current step) and still allow you to have logic based on the step index:
<StatefulWizardPattern
steps={[
{
shouldAllowEnter: (activeStepIndex) => { ... },
...
},
...
]}
/>
Of course, all the props passed to StatefulWizardPattern are passed down into the stateless WizardPattern, so if you want to use allowEnter explicitly from there, you can do that. So even if we did a stateful wizard for v2v, we could still just pass allowExit: false instead of having to write shouldAllowExit: () => false. Should make it easier for people to switch/upgrade/downgrade.
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 guess there is no reason for shouldAllowExit to take the current step index (edit: although maybe it should take the target step index). and shouldBeValid doesn't make sense that it could change for step A when you move from step B to C. so really i guess it's just shouldAllowEnter that needs the activeStepIndex information, and the others shouldn't be callbacks (edit: except maybe shouldAllowExit).
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.
In either case, if you pass the simple booleans, that trumps any callback values. right?
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.
Maybe the entire thing should instead be one large shouldStepChange(activeStepIndex, targetStepIndex) callback at the top? that'll distinguish it from onStepChanged better 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.
Note: please don't reply here, I moved all of the above down to the main discussion thread for easy access.
|
does anyone have thoughts on calling this a Wizard Kind of still waiting on feedback in #256 from the community ... (and less opinionated on this right now) |
| disabled={nextStepDisabled} | ||
| > | ||
| {onFinalStep ? closeButtonText : nextButtonText} | ||
| <Icon type="fa" name="angle-right" /> |
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.
The > should not be shown on the close button
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.
Thanks, fixed.
| } = this.props; | ||
| const currentStep = steps[activeStepIndex]; | ||
| setControlledState({ activeStepIndex: newStepIndex }); | ||
| if (onStepChanged) onStepChanged(newStepIndex); |
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.
onStepChanged should validate with the application prior to actually navigating. There are implementations where the next button (or navigating forward) causes some action which may need to be validated before navigation. The onStepChanged method should be able to return a boolean or a promise to validate the navigation.
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.
Agreed, @jeff-phillips-18. we had implemented that validation with other props, like a disableGoto or a shouldPreventNavigation callback or boolean in each step of the steps array. or a callback at the component level that takes a step index argument and returns that boolean.
onStepChanged returning a promise is interesting-- but right now it's intended to be a callback for after the step changes, and won't be called at all if the step is prevented from changing. I'm cool with either pattern.
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.
To be clear, the disableGoto was added to v2v after this refactor forked off-- i only didn't add it here because we ended up circumventing it anyway with stepButtonsDisabled. All the validation for navigating forward is currently on shouldDisableNextStep, a callback that you can pass in to ModalWizard that takes a step index. I was considering also making available a nextStepDisabled prop that could override that. I think that might be sufficient instead of using promises on the optional callbacks.
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.
In case the user wants to add a completely custom button to the wizard's footer and/or further tweak existing button logic and/or add other custom stuff into the footer, it should also be possible to replace the footer with a custom implementation (in such case, the responsibility of handling footer content is shifted from ModalWizard to its user).
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 point @vojtechszocs. I'll have to add that into my next iteration.
| } = this.props; | ||
| // Don't allow step clicks to skip into the future, but skipping into the past is ok. | ||
| const nextStepClicked = stepIndex === activeStepIndex + 1; | ||
| const futureStepClicked = stepIndex > activeStepIndex + 1; |
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.
There may be steps that are defaulted and can be skipped. I believe the application should decide if it is OK to navigate to a step.
|
Overall like the piece, I think it's good work @mturley that being said... @priley86 @mturley like the idea of breaking this out into a separate example/storybook piece. Partial to giving preference towards stateless, and leaving pieces such as this as an example. Reasons...
|
|
@vojtechszocs, I'm doing a little refactoring around disabling step navigation in certain conditions. I'll push changes in a little while, and also some better unit tests. |
|
Hey @cdcabrera. I totally agree with the preference for stateless, and the concerns over session storage. I do feel however that if we are going to have an opinionated example, we might as well provide it as a component. The stateful pieces are totally optional (we can pass a different Maybe adding some console warnings in Also, even if we have opinionated examples, we are going to see more fragmentation/variation if we are forcing everyone to copy and paste them every time (and then what if we want to update them, suddenly people's code is out of date). People are free to use the source of I definitely can go either way on this. My thinking was, enable what you are describing as an ideal, and support simpler use cases too for prototyping / avoiding the cathedral problem. I want to avoid making someone write a ton of redux code they might have to throw away before they can get feedback on the interaction in their components. |
|
@cdcabrera note that we are planning to refactor |
|
@priley86 , one more thing on your comment i missed earlier:
I would be okay with that. That might better satisfy @cdcabrera's point that this is just an opinionated expression of other components, while still letting people take advantage of it as a higher-level component with a lower barrier to entry/setup. |
|
Actually @priley86 I think if it's going to be I don't think we should call it |
|
@mturley yes - i like the distinction a lot as far as naming goes and great job mentioning this (that is the main idea I think and was trying to stay unopinionated on this). As long as we can distinguish "patterns" from simple stateless components I'm very happy... and taking it a step further with Regarding navigation - I think it makes sense to keep it underneath Wizard for now (if possible). We can move to tertiary navigation/hierarchy soon enough. There is even more great features coming with Storybook 3.4 soon and hope to help w/ that upgrade. |
|
👍 this is something I've been converging to in my VM provision wizard component as well. Currently, I see the @jgiardino mentioned that the "embedded" wizard UX pattern isn't planned to be supported for now, which means a follow-up cleanup could take place:
Users should be encouraged to use I have some additional requirements, I'll implement these locally for now, later on we can decide if they're worth contributing to
TL;DR this PR is a great start and will help adopting the wizard UX pattern. It doesn't have to be perfect right from the start, we can improve it over time. @jelkosz please add any additional information if I missed anything. |
It's always a good idea to stay in sync with changes in React APIs. If we can use That said, I agree with @mturley that we could add this into
@priley86 one question regarding above code, why use Enzyme to |
@vojtechszocs i guess we have a minor disagreement on this piece (and apologies for confusion)... i prefer to not introduce more abstractions if a lifecycle method is already available (wasn't sure on this last week when it was discussed, but it seems that is the preferred approach moving forward). I see a lot of Redux/Recompose functionality moving into React Core (and I personally don't think we need to abstract those pieces anymore). Redux and Recompose will both be changing/simplifying to support future React Core APIs, so I prefer to be safe and use those Core APIs (also find them a lot simpler for beginners than HOCs). Same thing is about to happen with the Context API (React 16.3 will formalize that API and it will be having even more enhancements in the future ). So this would mean we would not need Recompose for sharing context either... FWIW I am sure that Redux/Recompose will be adjusting (and can post some discussions about that I saw recently), but I don't think it is good to target those APIs much longer for this sort of thing... re: testing .. I am not super opinionated on that piece right now. @AllenBW maybe you have thoughts? |
Once this PR is merged, It makes sense to put
There are two types of state, one that's related to inputs (data/callbacks needed by the component) and one that people call "UI state", for example:
You can either have a |
I guess my only rebuttal to this is we just have a disagreement on that piece. One could still easily introduce an HOC downstream (if If it's helpful for you...Material UI team just did the same upgrade... ... Last bit i'll add here... I certainly appreciate the healthy debate and thoughts being shared (it's a very good topic for this repo and pretty central to future implementations, so I am trying to look at this from both sides). I see the HOC pattern being used in this specific PR to apply derived lifecycle state, but from the comments above, it seems what's potentially being suggested is something different and additional (using an HOC to apply UI state lifecycle to a separate or multiple similar stateless components to augment those components or patterns). This same sort of abstraction could also be achieved with an es6 base class, however React's ecosystem tends to prefer the functional/HOC approach. An HOC need not be tied to Recompose (there are plenty examples of HOCs not tied to that library...Redux's core implementation is a great example of this). However, I do not see the use case of augmenting multiple components/patterns being presented currently in this PR. FWIW - the approach w/ Recompose still adds the dependency and the additional maintenance, and should be used with caution if it is intended to replicate Core APIs arbitrarily in a way that would prevent future upgrades...additionally, I don't see a reason to abstract such things unless the abstraction is intended for multiple components, and the current es6 base class vs recompose vs generic wrapper is the slope we face on this, for wrappers/ patterns / managers if we decide to introduce them here ... ⛷ |
|
First off @mturley THIS IS WONDERFUL and the discussion it's generating 😍 🙇♀️ OOh OOh a mention, replying to....
which was a response to:
@vojtechszocs the pr here does a bit to explain my preference of enzyme to tl;dr - less syntax, more flexibility, more meaningful snapshots Chiming in on on a few architecture points...
yes YES YES please for the sake of future us, (the following is an opinion) 3rd party libraries introduce maintenance complexity over time, using only what we need can make keeping future deps up to date much easier... (The pr mentioned above seems to contradict this position... but the backing of a large company assuages concerns of future volatility) Patternfly designates wizards as either being modal or embedded, presently, we don't make this distinction well, (or have an embedded example for that matter) having an embedded/modal folder in the wizard component offering kinda makes sense More comments to come... just getting this outta the way first 🌮 🥑 💃 |
|
@AllenBW , @priley86 , @vojtechszocs thank you guys all so much for your input. I'm really glad we're getting this much visibility on the right kinds of questions, in the right places, so quickly. I have much to churn and process here, and feedback has pushed back the pace I was originally on here. Good problem to have! New commits with all of the above taken into account will come tomorrow. Let's get this thing done right. |
|
To clarify, where I have landed after discussions around this Users can start with a
|
Fine w/ that pattern if others are, but pretty please, a separate PR :)
I'm happy to see it merge if the following three things are done:
This is currently on hold. We have other things that are more pressing than abstracting these API hooks. Our existing Redux Form flow is OK for now but we'd like to revisit after Summit. It should not block this PR though - I agree w/ you on that. |
|
Okay guys. Sorry for the delay on this. I am now moving to knock out the things @priley86 listed in his comment above. We will get this merged ASAP. Sorry for the continued delay @vojtechszocs... this coincided both with a team meetup week and my PTO week. Lots of distractions. |
|
@mturley - any updates here? |
83fa55c to
a2c1f5b
Compare
|
@priley86, @vojtechszocs, My sincere apologies for how long it took me to get back around to this. I've squashed everything and implemented the things in @priley86's checklist. There were some strange merge conflicts around the Wizard tests, so it would be nice to get a once-over on those. I'm running into weird issues running |
|
Looks like Travis is happy, so I guess I've got something wrong with my local environment. Will troubleshoot tomorrow. |
I think we've addressed everything.. if not we can follow up with another PR
|
i'm good to go here... thanks a lot for your diligence and patience w/ this one @mturley ! I think we can continue iterating on the patterns/storybook hierarchies in future PRs, but this gets the Wizard much further!!! Let's be sure to announce that we officially moved to React 16.3 at the next community meeting and this PR is the first to implement |
|
Rebased, fixed merge conflicts, and updated storybook. @priley86 can you re-approve? |
…izardPattern components BREAKING CHANGE: Wizard structure has changed
|
Rebased again and ran |
|
for fear that Github will no longer load these comments.... i am hitting the approve button now 😸 |
|
Let's merge! @jeff-phillips-18 maybe you can? |
What:
Adding a new component for a Modal Wizard, building on the existing Modal and Wizard components. This is needed for ManageIQ's V2V POC project, and for other consumers from patternfly-react slack.
NOTE: This PR now includes breaking changes to
Wizard. Now that we no longer support an embedded Wizard, we are simplifying things so Wizard always renders a Modal.Link to Storybook (Updated 4/17 @ 6ead9a0):
https://rawgit.com/mturley/patternfly-react/wizard-pattern-sb/index.html?knob-Loading=false&knob-Active%20Step%20Index=0&selectedKind=Wizard%2FPatterns&selectedStory=Stateless%20WizardPattern%20Example&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybooks%2Fstorybook-addon-knobs