From 611e42238cd95e85e48a3b31819cc8e458d93968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Wed, 12 Oct 2016 15:20:21 +0200 Subject: [PATCH 1/2] [23896] Submit and close edit mode on enter for most edit fields When editing a single field (except select, checkbox), pressing enter triggers the `submit` handler of the surrounding form. This form submit is disabled when in edit mode, since the wp-edit-field uses that submit also for saving on blur, which we deliberately disable in this case. We can instead capture another event on those fields where it makes sense that submits the field after pressing enter, but only IN edit mode (since without it, submit handles it). https://community.openproject.com/work_packages/23896 --- .../routing/main/work-packages.new.html | 4 +-- .../routing/wp-list/wp.list.new.html | 9 +++--- .../work-package-comment.directive.html | 2 +- .../work-package-comment.directive.ts | 2 +- .../wp-create/wp-create.controller.ts | 15 +++------- .../wp-edit-boolean-field.directive.html | 2 +- .../wp-edit-date-field.directive.html | 5 ++-- .../wp-edit-duration-field.directive.html | 1 + .../wp-edit-float-field.directive.html | 1 + .../wp-edit-integer-field.directive.html | 1 + .../wp-edit-select-field.directive.html | 2 +- .../wp-edit-text-field.directive.html | 1 + ...wp-edit-wiki-textarea-field.directive.html | 2 +- .../wp-edit-field.directive.html | 3 +- .../wp-edit-field/wp-edit-field.directive.ts | 29 +++++++++++++++---- .../wp-edit/wp-edit-form.directive.ts | 12 ++++++++ .../wp-edit/wp-edit-mode-state.service.ts | 11 ++++--- 17 files changed, 66 insertions(+), 36 deletions(-) diff --git a/frontend/app/components/routing/main/work-packages.new.html b/frontend/app/components/routing/main/work-packages.new.html index a2ac8ee4e1..820ba0d06a 100644 --- a/frontend/app/components/routing/main/work-packages.new.html +++ b/frontend/app/components/routing/main/work-packages.new.html @@ -3,12 +3,12 @@ ng-if="$ctrl.newWorkPackage" has-edit-mode="true" wp-edit-form="$ctrl.newWorkPackage" -> + wp-edit-form-on-save="$ctrl.refreshAfterSave(workPackage, 'work-packages.show')">

{{ $ctrl.header }}

diff --git a/frontend/app/components/routing/wp-list/wp.list.new.html b/frontend/app/components/routing/wp-list/wp.list.new.html index d9eaeef271..3a08dcced3 100644 --- a/frontend/app/components/routing/wp-list/wp.list.new.html +++ b/frontend/app/components/routing/wp-list/wp.list.new.html @@ -4,10 +4,9 @@ >
-
+

{{ $ctrl.header }}

@@ -16,7 +15,7 @@
diff --git a/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.html b/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.html index 373c343aac..75beb66f03 100644 --- a/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.html +++ b/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.html @@ -9,7 +9,7 @@
diff --git a/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.ts b/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.ts index 48e2b1cbaf..3a95d42001 100644 --- a/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.ts +++ b/frontend/app/components/work-packages/work-package-comment/work-package-comment.directive.ts @@ -98,7 +98,7 @@ export class CommentFieldDirectiveController { return this.editing = true; } - public submit() { + public handleUserSubmit() { if (this.field.isEmpty()) { return; } diff --git a/frontend/app/components/wp-create/wp-create.controller.ts b/frontend/app/components/wp-create/wp-create.controller.ts index cc49198f62..2263112baf 100644 --- a/frontend/app/components/wp-create/wp-create.controller.ts +++ b/frontend/app/components/wp-create/wp-create.controller.ts @@ -86,19 +86,12 @@ export class WorkPackageCreateController { this.$state.go('work-packages.list', this.$state.params); } - public saveWorkPackage(successState:string):ng.IPromise { - if (this.wpEditModeState.active) { - return this.wpEditModeState.save().then(wp => { - this.newWorkPackage = null; - this.refreshAfterSave(wp, successState); - return wp; - }); - } - - return this.$q.reject(); + public saveWorkPackage():ng.IPromise { + return this.wpEditModeState.save(); } - private refreshAfterSave(wp, successState) { + public refreshAfterSave(wp, successState) { + this.wpEditModeState.onSaved(); this.loadingIndicator.mainPage = this.$state.go(successState, {workPackageId: wp.id}) .then(() => { this.$rootScope.$emit('workPackagesRefreshInBackground'); diff --git a/frontend/app/components/wp-edit/field-types/wp-edit-boolean-field.directive.html b/frontend/app/components/wp-edit/field-types/wp-edit-boolean-field.directive.html index 2285624179..b913fae2d1 100644 --- a/frontend/app/components/wp-edit/field-types/wp-edit-boolean-field.directive.html +++ b/frontend/app/components/wp-edit/field-types/wp-edit-boolean-field.directive.html @@ -3,7 +3,7 @@ wp-edit-field-requirements="vm.field.schema" ng-model="vm.workPackage[vm.fieldName]" ng-false-value="false" - ng-change="vm.submit()" + ng-change="vm.handleUserSubmit()" ng-focus="vm.handleUserFocus()" ng-blur="vm.handleUserBlur()" ng-disabled="vm.workPackage.inFlight" diff --git a/frontend/app/components/wp-edit/field-types/wp-edit-date-field.directive.html b/frontend/app/components/wp-edit/field-types/wp-edit-date-field.directive.html index 2743e8e53b..8c9add9e42 100644 --- a/frontend/app/components/wp-edit/field-types/wp-edit-date-field.directive.html +++ b/frontend/app/components/wp-edit/field-types/wp-edit-date-field.directive.html @@ -1,7 +1,7 @@ diff --git a/frontend/app/components/wp-edit/field-types/wp-edit-wiki-textarea-field.directive.html b/frontend/app/components/wp-edit/field-types/wp-edit-wiki-textarea-field.directive.html index 846229df30..151211b220 100644 --- a/frontend/app/components/wp-edit/field-types/wp-edit-wiki-textarea-field.directive.html +++ b/frontend/app/components/wp-edit/field-types/wp-edit-wiki-textarea-field.directive.html @@ -19,7 +19,7 @@
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html index a0acd30d46..ae8b2802c9 100644 --- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html @@ -13,7 +13,8 @@ ng-click="vm.haltUserFormClick($event)" ng-dblclick="vm.haltUserFormClick($event)" name="vm.fieldForm" - ng-submit="vm.submit()" + ng-submit="vm.handleUserSubmit()" + ng-keydown="vm.handleUserKey($event)" role="form" tabindex="-1"> diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts index dfdcf1a57c..dcc94ef3ee 100644 --- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts @@ -65,6 +65,7 @@ export class WorkPackageEditFieldController { protected NotificationsService, protected ConfigurationService, protected wpCacheService: WorkPackageCacheService, + protected ENTER_KEY, protected I18n) { } @@ -81,11 +82,7 @@ export class WorkPackageEditFieldController { } public submit() { - if (this.inEditMode) { - return this.formCtrl.updateForm(); - } - - this.formCtrl.updateWorkPackage() + this.formCtrl.onFieldSubmit() .finally(() => { this.deactivate(); this._forceFocus = true; @@ -235,6 +232,26 @@ export class WorkPackageEditFieldController { }); } + public handleUserSubmit() { + if (this.inEditMode) { + return this.formCtrl.updateForm(); + } + + return this.submit(); + } + + /** + * Handle users pressing enter inside an edit mode. + * Outside an edit mode, the regular save event is captured by handleUserSubmit (submit event). + * In an edit mode, we can't derive from a submit event wheteher the user pressed enter + * (and on what field he did that). + */ + public handleUserSubmitOnEnter(event) { + if (this.inEditMode && event.which === this.ENTER_KEY) { + return this.submit(); + } + } + public handleUserFocus() { this._hasFocus = true; } @@ -253,7 +270,7 @@ export class WorkPackageEditFieldController { } this.deactivate(); - this.submit(); + this.handleUserSubmit(); } public handleUserCancel() { diff --git a/frontend/app/components/wp-edit/wp-edit-form.directive.ts b/frontend/app/components/wp-edit/wp-edit-form.directive.ts index acd6c9cedd..2e7eb7442f 100644 --- a/frontend/app/components/wp-edit/wp-edit-form.directive.ts +++ b/frontend/app/components/wp-edit/wp-edit-form.directive.ts @@ -120,6 +120,18 @@ export class WorkPackageEditFormController { }); } + /** + * Handle submission event of a single work package field that may or + * may not be involved inside an active edit mode. + */ + public onFieldSubmit() { + if (this.wpEditModeState.active) { + return this.wpEditModeState.save(); + } + + return this.updateWorkPackage(); + } + public updateWorkPackage() { if (!(this.workPackage.dirty || this.workPackage.isNew)) { return this.$q.when(this.workPackage); diff --git a/frontend/app/components/wp-edit/wp-edit-mode-state.service.ts b/frontend/app/components/wp-edit/wp-edit-mode-state.service.ts index 423d6741a8..7d93573e94 100644 --- a/frontend/app/components/wp-edit/wp-edit-mode-state.service.ts +++ b/frontend/app/components/wp-edit/wp-edit-mode-state.service.ts @@ -80,13 +80,16 @@ export class WorkPackageEditModeStateService { return this._active = false; } + public onSaved() { + // Doesn't use cancel() since that resets all values + this.form.closeAllFields(); + this._active = false; + } + public save() { if (this.active) { return this.form.updateWorkPackage().then(wp => { - // Doesn't use cancel() since that resets all values - this.form.closeAllFields(); - this._active = false; - + this.onSaved(); return wp; }); } From 3f684a770a0a8e355242b0d0ee8a4451d1562efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Wed, 12 Oct 2016 16:00:38 +0200 Subject: [PATCH 2/2] Extend spec for enter submission --- .../work_packages/edit_work_package_spec.rb | 12 ++++++++++++ .../work_packages/new_work_package_spec.rb | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/spec/features/work_packages/edit_work_package_spec.rb b/spec/features/work_packages/edit_work_package_spec.rb index 0ce45c2c73..0a8c84ddc3 100644 --- a/spec/features/work_packages/edit_work_package_spec.rb +++ b/spec/features/work_packages/edit_work_package_spec.rb @@ -227,4 +227,16 @@ describe 'edit work package', js: true do wp_page.expect_notification message: 'Subject is too long (maximum is 255 characters)', type: 'error' end + + it 'submits the edit mode when pressing enter' do + page.click_button(I18n.t('js.button_edit')) + subject_field = wp_page.edit_field(:subject) + + subject_field.set_value 'My new subject!' + subject_field.input_element.send_keys(:return) + + wp_page.expect_notification(message: 'Successful update') + subject_field.expect_inactive! + subject_field.expect_state_text 'My new subject!' + end end diff --git a/spec/features/work_packages/new_work_package_spec.rb b/spec/features/work_packages/new_work_package_spec.rb index f2f4723073..d931def332 100644 --- a/spec/features/work_packages/new_work_package_spec.rb +++ b/spec/features/work_packages/new_work_package_spec.rb @@ -96,6 +96,20 @@ describe 'new work package', js: true do selected: 'Bug') end + it 'saves the work package with enter' do + subject_field = wp_page.subject_field + subject_field.set(subject) + subject_field.send_keys(:return) + + # safegurards + wp_page.dismiss_notification! + wp_page.expect_no_notification( + message: 'Successful creation. Click here to open this work package in fullscreen view.' + ) + + wp_page.expect_subject + end + context 'with missing values' do it 'shows an error when subject is missing' do wp_page.description_field.set(description)