Merge branch 'develop' into copy-button-using-clipboardjs

This commit is contained in:
Randall Morey authored and GitHub committed 2020-09-30 11:29:37 -04:00
commit 4477d1701d
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;
}
}
+3 -2
View File
@@ -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 });
+2 -1
View File
@@ -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;
}
}
}
+2 -1
View File
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
+2 -1
View File
@@ -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;
}
}
+2 -1
View File
@@ -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;
}
}