diff --git a/addons/rose/addon/components/rose/form/actions/edit-toggle/index.hbs b/addons/rose/addon/components/rose/form/actions/edit-toggle/index.hbs new file mode 100644 index 000000000..f052b495c --- /dev/null +++ b/addons/rose/addon/components/rose/form/actions/edit-toggle/index.hbs @@ -0,0 +1,8 @@ +
+ + {{@enableEditText}} + +
diff --git a/addons/rose/addon/components/rose/form/index.hbs b/addons/rose/addon/components/rose/form/index.hbs index f37f36d09..045ad70c4 100644 --- a/addons/rose/addon/components/rose/form/index.hbs +++ b/addons/rose/addon/components/rose/form/index.hbs @@ -6,12 +6,25 @@ autocomplete="off" {{on "submit" this.handleSubmit}} > + {{!-- + Only show the edit toggle button if the toggle is enabled in the first place + AND the form is not already in editable mode. + --}} {{yield (hash - radioGroup=(component 'rose/form/radio/group' disabled=@disabled) - checkbox=(component 'rose/form/checkbox' disabled=@disabled) - input=(component 'rose/form/input' disabled=@disabled) - select=(component 'rose/form/select' disabled=@disabled) - textarea=(component 'rose/form/textarea' disabled=@disabled) - actions=(component 'rose/form/actions' disabled=@disabled cancel=@cancel) + radioGroup=(component 'rose/form/radio/group' disabled=this.disabled) + checkbox=(component 'rose/form/checkbox' disabled=this.disabled) + input=(component 'rose/form/input' disabled=this.disabled) + select=(component 'rose/form/select' disabled=this.disabled) + textarea=(component 'rose/form/textarea' disabled=this.disabled) + actions=(component + (if this.showEditToggleButton + 'rose/form/actions/edit-toggle' + 'rose/form/actions' + ) + submitDisabled=this.disabled + cancelDisabled=(if @showEditToggle false this.disabled) + cancel=this.handleCancel + enableEdit=this.enableEdit + ) )}} diff --git a/addons/rose/addon/components/rose/form/index.js b/addons/rose/addon/components/rose/form/index.js index 24bb8e0c2..249cd9fbd 100644 --- a/addons/rose/addon/components/rose/form/index.js +++ b/addons/rose/addon/components/rose/form/index.js @@ -1,8 +1,40 @@ import Component from '@glimmer/component'; -import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { computed, action } from '@ember/object'; export default class RoseFormComponent extends Component { + // =attributes + + /** + * This field indicates whether edit mode is toggled + * and only applies if `@showEditToggle` is true. + * @type {boolean} + */ + @tracked isEditable = false; + + /** + * By default, this field is unconditionally equal to the value of the passed + * `@disabled` argument. If desired the form may be "locked" from editing + * until the user intentionally enables edit mode (`@showEditToggle` is true), + * in which case the value is dependent on `@disabled` and `this.isEditable`. + * @type {boolean} + */ + @computed('args.disabled', 'args.showEditToggle', 'isEditable') + get disabled() { + if (this.args.showEditToggle) return this.args.disabled || !this.isEditable; + return this.args.disabled; + } + + /** + * True if showEditToggle is true and edit mode is enabled. + * @type {boolean} + */ + @computed('args.showEditToggle', 'isEditable') + get showEditToggleButton() { + return this.args.showEditToggle && !this.isEditable; + } + // =actions /** @@ -18,4 +50,30 @@ export default class RoseFormComponent extends Component { return false; } + /** + * Calls the passed `cancel` function. + * @param {Event} e + */ + @action + handleCancel() { + this.disableEdit(); + this.args.cancel(); + } + + /** + * Enables edit mode, which only applies when `@showEditToggle` is true. + */ + @action + enableEdit() { + this.isEditable = true; + } + + /** + * Disables edit mode, which only applies when `@showEditToggle` is true. + */ + @action + disableEdit() { + this.isEditable = false; + } + } diff --git a/addons/rose/addon/components/rose/form/index.stories.mdx b/addons/rose/addon/components/rose/form/index.stories.mdx index f57f85da2..f3498eaa2 100644 --- a/addons/rose/addon/components/rose/form/index.stories.mdx +++ b/addons/rose/addon/components/rose/form/index.stories.mdx @@ -16,6 +16,7 @@ and actions. - + `, context: { + showEditToggle: boolean('showEditToggle', false), disabled: boolean('disabled', false), submit: action('submit'), cancel: action('cancel'), @@ -77,6 +83,7 @@ and actions. @onSubmit={{this.submit}} @cancel={{this.cancel}} @disabled={{false}} + @showEditToggle={{true}} as |form|> - + ``` diff --git a/addons/rose/addon/templates/components/rose/form/actions.hbs b/addons/rose/addon/templates/components/rose/form/actions.hbs index 47557fb7e..184f36893 100644 --- a/addons/rose/addon/templates/components/rose/form/actions.hbs +++ b/addons/rose/addon/templates/components/rose/form/actions.hbs @@ -1,6 +1,6 @@
@@ -9,7 +9,7 @@ {{#if this.showCancel}} diff --git a/addons/rose/app/components/rose/form/actions/edit-toggle.js b/addons/rose/app/components/rose/form/actions/edit-toggle.js new file mode 100644 index 000000000..e0790458d --- /dev/null +++ b/addons/rose/app/components/rose/form/actions/edit-toggle.js @@ -0,0 +1 @@ +export { default } from 'rose/components/rose/form/actions/edit-toggle'; \ No newline at end of file diff --git a/addons/rose/tests/integration/components/rose/form-test.js b/addons/rose/tests/integration/components/rose/form-test.js index f8d682fdd..7c33aa290 100644 --- a/addons/rose/tests/integration/components/rose/form-test.js +++ b/addons/rose/tests/integration/components/rose/form-test.js @@ -1,6 +1,6 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { render, find, click } from '@ember/test-helpers'; +import { render, find, findAll, click } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; module('Integration | Component | rose/form', function (hooks) { @@ -64,4 +64,42 @@ module('Integration | Component | rose/form', function (hooks) { assert.ok(find('[name="checkbox-field"]')); assert.ok(find('[name="radio-group-field"]')); }); + + test('it supports an editability toggle when @showEditToggle is true', async function (assert) { + assert.expect(12); + this.cancel = () => { + assert.ok(true, 'cancel function may still be passed even for @showEditToggle'); + }; + this.onSubmit = () => {}; + await render(hbs` + + + + + `); + // 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 canceling, fields are disabled again and the edit mode button is displayed + await click('button:not([type="submit"])'); + assert.equal(findAll('input[disabled]').length, 1); + assert.equal(findAll('button').length, 1); + assert.equal(find('button').textContent.trim(), 'Edit'); + }); }); diff --git a/addons/rose/tests/integration/components/rose/form/actions-test.js b/addons/rose/tests/integration/components/rose/form/actions-test.js index fa018c7c6..0463d1247 100644 --- a/addons/rose/tests/integration/components/rose/form/actions-test.js +++ b/addons/rose/tests/integration/components/rose/form/actions-test.js @@ -14,10 +14,10 @@ module('Integration | Component | rose/form/actions', function (hooks) { assert.ok(find('button:not([type="submit"])')); }); - test('it can disabled the buttons', async function (assert) { + test('it can disable the buttons', async function (assert) { assert.expect(2); this.cancel = () => {}; - await render(hbs`{{rose/form/actions cancel=this.cancel disabled=true}}`); + await render(hbs`{{rose/form/actions cancel=this.cancel submitDisabled=true cancelDisabled=true}}`); assert.ok(find('[type="submit"]:disabled')); assert.ok(find('button:not([type="submit"]):disabled')); }); diff --git a/addons/rose/tests/integration/components/rose/form/actions/edit-toggle-test.js b/addons/rose/tests/integration/components/rose/form/actions/edit-toggle-test.js new file mode 100644 index 000000000..436a14b03 --- /dev/null +++ b/addons/rose/tests/integration/components/rose/form/actions/edit-toggle-test.js @@ -0,0 +1,22 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, find, click } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Component | rose/form/actions/edit-toggle', function (hooks) { + setupRenderingTest(hooks); + + test('it renders a regular button', async function (assert) { + assert.expect(1); + this.enableEdit = () => {}; + await render(hbs`{{rose/form/actions/edit-toggle enableEdit=this.enableEdit}}`); + assert.ok(find('button')); + }); + + test('it executes a function on button click', async function (assert) { + assert.expect(1); + this.enableEdit = () => assert.ok(true, 'edit was clicked'); + await render(hbs`{{rose/form/actions/edit-toggle enableEdit=this.enableEdit}}`); + await click('button'); + }); +});