-
Notifications
You must be signed in to change notification settings - Fork 4.5k
UI Onboarding Wizards #5196
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
UI Onboarding Wizards #5196
Changes from all commits
01a8428
c9e3d7a
2f97694
c7aa2c9
e9cbc3a
da96f15
4fe492c
9f6f17d
35895bc
aa5632a
da5fd54
d6fd202
8fac4b5
a28a23a
b20d653
d3f068c
adf1dc5
d834740
0efaf40
7795908
89b115b
ed9da83
2753bef
8831321
57ac54c
1c1234a
e5f6a6a
bc23b0e
66b5834
aa0dd69
ac220e4
22033d9
b0ae372
0623602
d623ff9
f2f9f9e
2765521
4545668
6700991
684feb4
1aa32d8
d2baeeb
531ac87
af6464e
088adc2
280d3db
9683b15
e1cd3ba
034fb4a
91f3c5b
4b91462
86b0535
cedca4d
93b1d6b
9fa7c4c
5c05a24
0b2c9ef
a3d361e
1c37437
e84a275
da21b60
dffb4aa
dbefe68
17a778b
c5d4ecc
d7dc93d
8411ad6
12c21b0
23591ba
5fe262f
5ad1570
4b8b1ac
d134040
90077f6
b147aab
57b624f
45bd81e
592cbb4
034ca61
4274b41
e3a5c06
8855fe0
f5f524e
992bde5
e425422
678cc18
fd27ccb
641a015
4f4301c
a7b0697
f02cf86
7cd9624
161830f
5cfb88b
618e746
8916cf6
8ee2470
29408e8
294862a
581e8d4
6c20fa8
79df179
9be891e
e220f88
aa78cfa
2f1132a
3768040
f015d2a
7b8586a
87729a4
6ca733f
2f8178f
a68c639
9ed1921
31bfdb9
7a1c2d6
d316f9f
c1602fa
92cb9ff
e93b6c8
104af27
4bef355
2235ef4
b3ff629
a2dfa79
02269f4
f48e6b4
271642f
ce976cc
9a7115d
9b4d1f9
51796ab
30207d0
aa6e5bf
474e7b8
b38c390
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,15 @@ | ||
| import Ember from 'ember'; | ||
| import { task } from 'ember-concurrency'; | ||
| import { methods } from 'vault/helpers/mountable-auth-methods'; | ||
| import { engines } from 'vault/helpers/mountable-secret-engines'; | ||
|
|
||
| const { inject } = Ember; | ||
| const { inject, computed, Component } = Ember; | ||
| const METHODS = methods(); | ||
| const ENGINES = engines(); | ||
|
|
||
| export default Ember.Component.extend({ | ||
| export default Component.extend({ | ||
| store: inject.service(), | ||
| wizard: inject.service(), | ||
| flashMessages: inject.service(), | ||
| routing: inject.service('-routing'), | ||
|
|
||
|
|
@@ -38,23 +41,29 @@ export default Ember.Component.extend({ | |
| */ | ||
| mountModel: null, | ||
|
|
||
| showConfig: false, | ||
|
|
||
| init() { | ||
| this._super(...arguments); | ||
| const type = this.get('mountType'); | ||
| const modelType = type === 'secret' ? 'secret-engine' : 'auth-method'; | ||
| const model = this.get('store').createRecord(modelType); | ||
| this.set('mountModel', model); | ||
| this.changeConfigModel(model.get('type')); | ||
| }, | ||
|
|
||
| mountTypes: computed('mountType', function() { | ||
| return this.get('mountType') === 'secret' ? ENGINES : METHODS; | ||
| }), | ||
|
|
||
| willDestroy() { | ||
| // if unsaved, we want to unload so it doesn't show up in the auth mount list | ||
| this.get('mountModel').rollbackAttributes(); | ||
| }, | ||
|
|
||
| getConfigModelType(methodType) { | ||
| let mountType = this.get('mountType'); | ||
| let noConfig = ['approle']; | ||
| if (noConfig.includes(methodType)) { | ||
| if (mountType === 'secret' || noConfig.includes(methodType)) { | ||
| return; | ||
| } | ||
| if (methodType === 'aws') { | ||
|
|
@@ -64,27 +73,32 @@ export default Ember.Component.extend({ | |
| }, | ||
|
|
||
| changeConfigModel(methodType) { | ||
| const mount = this.get('mountModel'); | ||
| const configRef = mount.hasMany('authConfigs').value(); | ||
| const currentConfig = configRef.get('firstObject'); | ||
| let mount = this.get('mountModel'); | ||
| if (this.get('mountType') === 'secret') { | ||
| return; | ||
| } | ||
| let configRef = mount.hasMany('authConfigs').value(); | ||
| let currentConfig = configRef.get('firstObject'); | ||
| if (currentConfig) { | ||
| // rollbackAttributes here will remove the the config model from the store | ||
| // because `isNew` will be true | ||
| currentConfig.rollbackAttributes(); | ||
| currentConfig.unloadRecord(); | ||
| } | ||
| const configType = this.getConfigModelType(methodType); | ||
| let configType = this.getConfigModelType(methodType); | ||
| if (!configType) return; | ||
| const config = this.get('store').createRecord(configType); | ||
| let config = this.get('store').createRecord(configType); | ||
| config.set('backend', mount); | ||
| }, | ||
|
|
||
| checkPathChange(type) { | ||
| const mount = this.get('mountModel'); | ||
| const currentPath = mount.get('path'); | ||
| let mount = this.get('mountModel'); | ||
| let currentPath = mount.get('path'); | ||
| let list = this.get('mountTypes'); | ||
| // if the current path matches a type (meaning the user hasn't altered it), | ||
| // change it here to match the new type | ||
| const isUnchanged = METHODS.findBy('type', currentPath); | ||
| if (isUnchanged) { | ||
| let isUnchanged = list.findBy('type', currentPath); | ||
| if (!currentPath || isUnchanged) { | ||
| mount.set('path', type); | ||
| } | ||
| }, | ||
|
|
@@ -101,6 +115,10 @@ export default Ember.Component.extend({ | |
| this.get('flashMessages').success( | ||
| `Successfully mounted ${type} ${this.get('mountType')} method at ${path}.` | ||
| ); | ||
| if (this.get('mountType') === 'secret') { | ||
| yield this.get('onMountSuccess')(type, path); | ||
| return; | ||
| } | ||
| yield this.get('saveConfig').perform(mountModel); | ||
| }).drop(), | ||
|
|
||
|
|
@@ -111,11 +129,16 @@ export default Ember.Component.extend({ | |
| try { | ||
| if (config && Object.keys(config.changedAttributes()).length) { | ||
| yield config.save(); | ||
| this.get('wizard').transitionFeatureMachine( | ||
| this.get('wizard.featureState'), | ||
| 'CONTINUE', | ||
| this.get('mountModel').get('type') | ||
| ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to make a special method for this pattern? Something like... // wizard.js
next(extendedState) {
return this.transitionFeatureMachine(this.get('featureState'), 'CONTINUE', extendedState);
}
// this-file.js
try {
if (config && Object.keys(config.changedAttributes()).length) {
yield config.save()
this.get('wizard').next(this.get('mountModel').get('type'));
}
} catch (err) {
// ...
} |
||
| this.get('flashMessages').success( | ||
| `The config for ${type} ${this.get('mountType')} method at ${path} was saved successfully.` | ||
| ); | ||
| } | ||
| yield this.get('onMountSuccess')(); | ||
| yield this.get('onMountSuccess')(type, path); | ||
| } catch (err) { | ||
| this.get('flashMessages').danger( | ||
| `There was an error saving the configuration for ${type} ${this.get( | ||
|
|
@@ -129,9 +152,27 @@ export default Ember.Component.extend({ | |
| actions: { | ||
| onTypeChange(path, value) { | ||
| if (path === 'type') { | ||
| this.get('wizard').set('componentState', value); | ||
| this.changeConfigModel(value); | ||
| this.checkPathChange(value); | ||
| } | ||
| }, | ||
|
|
||
| toggleShowConfig(value) { | ||
| this.set('showConfig', value); | ||
| if (value === true && this.get('wizard.featureState') === 'idle') { | ||
| this.get('wizard').transitionFeatureMachine( | ||
| this.get('wizard.featureState'), | ||
| 'CONTINUE', | ||
| this.get('mountModel').get('type') | ||
| ); | ||
| } else { | ||
| this.get('wizard').transitionFeatureMachine( | ||
| this.get('wizard.featureState'), | ||
| 'RESET', | ||
| this.get('mountModel').get('type') | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another one! Maybe it also makes sense to "curry" this for two arguments as well as three and one? // wizard.js
// Total control
transitionFeatureMachine(currentState, event, extendedState) {},
// Assume internally tracked state
transition(event, extendedState) {
this.transitionFeatureMachine(this.get('wizard.featureState'), event, extendedState);
},
// Convenience methods for common event types
next(extendedState) { this.transition('CONTINUE', extendedState); },
reset(extendedState) { this.transition('RESET', extendedState); },Just looking for ways to reduce boilerplate and abstract away some internals. |
||
| ); | ||
| } | ||
| }, | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // THIS COMPONENT IS ONLY FOR EXTENDING | ||
| // You should use this component if you want to use outerHTML symantics | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/symantics/semantics |
||
| // in your components - this is the default for upcoming Glimmer components | ||
| import Ember from 'ember'; | ||
|
|
||
| export default Ember.Component.extend({ | ||
| tagName: '', | ||
| }); | ||
|
|
||
| // yep! that's it, it's more of a way to keep track of what components | ||
| // use tagless semantics to make the upgrade to glimmer components easier | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 helpful comment! |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import Ember from 'ember'; | ||
|
|
||
| export default Ember.Component.extend({ | ||
| tagName: '', | ||
| tagName: 'span', | ||
| }); |
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 strikes me as surprising. I wouldn't expect certain icons to get special treatment implicitly.