Merge branch 'develop' into copy-button-using-clipboardjs
This commit is contained in:
14 files changed
+84
-16
No files matched your search
@@ -40,13 +40,27 @@ export default class RoseFormComponent extends Component {
|
||||
/**
|
||||
* Calls the passed `onSubmit` function while disabling the default form
|
||||
* submit behavior.
|
||||
*
|
||||
* Submit handlers _may optionally return a promise_. If they do, the promise
|
||||
* is assumed to reflect the state of the submission operation,
|
||||
* i.e. if the promise resolves the submission was successful and if it
|
||||
* rejects, the submission failed.
|
||||
* @param {Event} e
|
||||
*/
|
||||
@action
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.args.onSubmit();
|
||||
const submitResult = this.args.onSubmit();
|
||||
// If the submit handler returns a promise, we want to handle success
|
||||
// by re-enabling read-only mode on the form (if applicable).
|
||||
// Since submit is allowed to fail in this context and we don't want
|
||||
// unhandled rejection warnings, we have a no-op catch.
|
||||
if (submitResult?.then) {
|
||||
submitResult
|
||||
.then(() => this.isEditable = false)
|
||||
.catch(() => {});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find, findAll, click } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { resolve, reject } from 'rsvp';
|
||||
|
||||
module('Integration | Component | rose/form', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
@@ -70,7 +71,7 @@ module('Integration | Component | rose/form', function (hooks) {
|
||||
this.cancel = () => {
|
||||
assert.ok(true, 'cancel function may still be passed even for @showEditToggle');
|
||||
};
|
||||
this.onSubmit = () => {};
|
||||
this.submit = () => {};
|
||||
await render(hbs`
|
||||
<Rose::Form
|
||||
@onSubmit={{this.submit}}
|
||||
@@ -102,4 +103,46 @@ module('Integration | Component | rose/form', function (hooks) {
|
||||
assert.equal(findAll('button').length, 1);
|
||||
assert.equal(find('button').textContent.trim(), 'Edit');
|
||||
});
|
||||
|
||||
test('it re-enables read-only mode if the submit handler returns a resolving promise', async function (assert) {
|
||||
assert.expect(15);
|
||||
this.cancel = () => {};
|
||||
await render(hbs`
|
||||
<Rose::Form
|
||||
@onSubmit={{this.submit}}
|
||||
@cancel={{this.cancel}}
|
||||
@showEditToggle={{true}}
|
||||
as |form|
|
||||
>
|
||||
<form.input @label="Label" @value="value" />
|
||||
<form.actions
|
||||
@submitText="Save"
|
||||
@cancelText="Cancel"
|
||||
@enableEditText="Edit" />
|
||||
</Rose::Form>
|
||||
`);
|
||||
// Before enabling edit mode, fields are disabled and the edit mode button is displayed
|
||||
assert.ok(find('.rose-form'));
|
||||
assert.equal(findAll('input[disabled]').length, 1);
|
||||
assert.equal(findAll('button').length, 1);
|
||||
assert.equal(find('button').textContent.trim(), 'Edit');
|
||||
// After entering edit mode, fields are enabled and save/cancel buttons are displayed
|
||||
await click('button');
|
||||
assert.equal(findAll('input[disabled]').length, 0);
|
||||
assert.equal(findAll('button').length, 2);
|
||||
assert.equal(find('[type="submit"]').textContent.trim(), 'Save');
|
||||
assert.equal(find('button:not([type="submit"])').textContent.trim(), 'Cancel');
|
||||
// After saving with failure, fields are enabled and save/cancel buttons are displayed
|
||||
this.submit = () => reject();
|
||||
assert.equal(findAll('input[disabled]').length, 0);
|
||||
assert.equal(findAll('button').length, 2);
|
||||
assert.equal(find('[type="submit"]').textContent.trim(), 'Save');
|
||||
assert.equal(find('button:not([type="submit"])').textContent.trim(), 'Cancel');
|
||||
// After saving with success, fields are disabled again and the edit mode button is displayed
|
||||
this.submit = () => resolve();
|
||||
await click('button[type="submit"]');
|
||||
assert.equal(findAll('input[disabled]').length, 1);
|
||||
assert.equal(findAll('button').length, 1);
|
||||
assert.equal(find('button').textContent.trim(), 'Edit');
|
||||
});
|
||||
});
|
||||
@@ -32,6 +32,6 @@ export default class FormAccountPasswordIndexComponent extends Component {
|
||||
submit(fn) {
|
||||
const password = this.password;
|
||||
next(() => this.resetPassword());
|
||||
this.args.model.isNew ? fn(password) : fn();
|
||||
return this.args.model.isNew ? fn(password) : fn();
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
} catch (error) {
|
||||
//TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,14 +50,15 @@ export default class ScopesScopeGroupsRoute extends Route {
|
||||
const { isNew } = group;
|
||||
try {
|
||||
await group.save();
|
||||
await this.transitionTo('scopes.scope.groups.group', group);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
this.transitionTo('scopes.scope.groups.group', group);
|
||||
} catch (error) {
|
||||
//TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ export default class ScopesScopeGroupsRoute extends Route {
|
||||
await group.destroyRecord();
|
||||
this.refresh();
|
||||
this.notify.success(this.intl.t('notifications.delete-success'));
|
||||
this.transitionTo('scopes.scope.groups');
|
||||
await this.transitionTo('scopes.scope.groups');
|
||||
} catch (error) {
|
||||
//TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
|
||||
@@ -54,14 +54,15 @@ export default class ScopesScopeOrgsRoute extends Route {
|
||||
const { isNew } = org;
|
||||
try {
|
||||
await org.save();
|
||||
await this.transitionTo('scopes.scope', org);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
this.transitionTo('scopes.scope', org);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,14 +66,15 @@ export default class ScopesScopeProjectsRoute extends Route {
|
||||
const { isNew } = project;
|
||||
try {
|
||||
await project.save();
|
||||
await this.transitionTo('scopes.scope.projects.project', project);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
this.transitionTo('scopes.scope.projects.project', project);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,17 +44,18 @@ export default class ScopesScopeProjectsProjectHostCatalogsRoute extends Route {
|
||||
const { isNew } = hostCatalog;
|
||||
try {
|
||||
await hostCatalog.save();
|
||||
await this.transitionTo(
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog',
|
||||
hostCatalog
|
||||
);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
this.transitionTo(
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog',
|
||||
hostCatalog
|
||||
);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -53,13 +53,14 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRo
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set',
|
||||
hostSet
|
||||
);
|
||||
await this.refresh();
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -53,13 +53,14 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog.hosts.host',
|
||||
host
|
||||
);
|
||||
await this.refresh();
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,14 +44,15 @@ export default class ScopesScopeProjectsProjectTargetsRoute extends Route {
|
||||
const { isNew } = target;
|
||||
try {
|
||||
await target.save();
|
||||
await this.transitionTo('scopes.scope.projects.project.targets.target', target);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
this.transitionTo('scopes.scope.projects.project.targets.target', target);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,14 +50,15 @@ export default class ScopesScopeRolesRoute extends Route {
|
||||
const { isNew } = role;
|
||||
try {
|
||||
await role.save();
|
||||
await this.transitionTo('scopes.scope.roles.role', role);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
this.transitionTo('scopes.scope.roles.role', role);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,14 +51,15 @@ export default class ScopesScopeUsersRoute extends Route {
|
||||
const { isNew } = user;
|
||||
try {
|
||||
await user.save();
|
||||
await this.transitionTo('scopes.scope.users.user', user);
|
||||
this.refresh();
|
||||
this.notify.success(
|
||||
this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success')
|
||||
);
|
||||
this.transitionTo('scopes.scope.users.user', user);
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user