Merge pull request #4225 from oliverguenther/feature/expand-error-columns
[22556] Error handling for other required/invalid fields of a WP rowpull/4233/head
commit
c6844255f9
@ -0,0 +1,63 @@ |
||||
//-- copyright |
||||
// OpenProject is a project management system. |
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF) |
||||
// |
||||
// This program is free software; you can redistribute it and/or |
||||
// modify it under the terms of the GNU General Public License version 3. |
||||
// |
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
// Copyright (C) 2010-2013 the ChiliProject Team |
||||
// |
||||
// This program is free software; you can redistribute it and/or |
||||
// modify it under the terms of the GNU General Public License |
||||
// as published by the Free Software Foundation; either version 2 |
||||
// of the License, or (at your option) any later version. |
||||
// |
||||
// This program is distributed in the hope that it will be useful, |
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
// GNU General Public License for more details. |
||||
// |
||||
// You should have received a copy of the GNU General Public License |
||||
// along with this program; if not, write to the Free Software |
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
// |
||||
// See doc/COPYRIGHT.rdoc for more details. |
||||
//++ |
||||
|
||||
.-editable |
||||
.wp-edit-field |
||||
&.-active |
||||
padding: 0 |
||||
|
||||
&.-error |
||||
background: $nm-color-error-background |
||||
border-color: $nm-color-error-border |
||||
|
||||
&:hover |
||||
border-color: lighten($nm-color-error-border, 10%) |
||||
|
||||
form |
||||
width: 100% |
||||
|
||||
&:hover .wp-edit-field.-error:hover |
||||
border-color: $nm-color-error-border |
||||
|
||||
.wp-table--cell |
||||
cursor: not-allowed |
||||
|
||||
&.-editable |
||||
cursor: pointer |
||||
|
||||
.work-package-table--container .generic-table tbody |
||||
td |
||||
.wp-edit-field .-hidden-overflow |
||||
overflow: hidden |
||||
text-overflow: ellipsis |
||||
&.-editable |
||||
padding-top: 0 |
||||
padding-bottom: 0 |
||||
display: table-cell |
||||
width: auto |
||||
|
@ -0,0 +1,68 @@ |
||||
//-- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
//++
|
||||
|
||||
function errorResource(HalResource:typeof op.HalResource, NotificationsService:any) { |
||||
class ErrorResource extends HalResource { |
||||
public errors:any[]; |
||||
public message:string; |
||||
public details:any; |
||||
public errorIdentifier:string; |
||||
|
||||
public get errorMessages():string[] { |
||||
if (this.isMultiErrorMessage()) { |
||||
return this.errors.map(error => error.message); |
||||
} else { |
||||
return [this.message]; |
||||
} |
||||
} |
||||
|
||||
public isMultiErrorMessage() { |
||||
return this.errorIdentifier === 'urn:openproject-org:api:v3:errors:MultipleErrors'; |
||||
} |
||||
|
||||
public showErrorNotification() { |
||||
var messages = this.errorMessages; |
||||
if (messages.length > 1) { |
||||
NotificationsService.addError('', messages); |
||||
} else { |
||||
NotificationsService.addError(messages[0]); |
||||
} |
||||
} |
||||
|
||||
public getInvolvedColumns():string[] { |
||||
var columns = this.details ? [{ details: this.details }] : this.errors; |
||||
return columns.map(field => field.details.attribute); |
||||
} |
||||
} |
||||
|
||||
return ErrorResource; |
||||
} |
||||
|
||||
angular |
||||
.module('openproject.api') |
||||
.factory('ErrorResource', errorResource); |
@ -0,0 +1,117 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe 'Inline editing work packages', js: true do |
||||
let(:dev_role) do |
||||
FactoryGirl.create :role, |
||||
permissions: [:view_work_packages, |
||||
:add_work_packages] |
||||
end |
||||
let(:dev) do |
||||
FactoryGirl.create :user, |
||||
firstname: 'Dev', |
||||
lastname: 'Guy', |
||||
member_in_project: project, |
||||
member_through_role: dev_role |
||||
end |
||||
let(:manager_role) do |
||||
FactoryGirl.create :role, |
||||
permissions: [:view_work_packages, |
||||
:edit_work_packages] |
||||
end |
||||
let(:manager) do |
||||
FactoryGirl.create :user, |
||||
firstname: 'Manager', |
||||
lastname: 'Guy', |
||||
member_in_project: project, |
||||
member_through_role: manager_role |
||||
end |
||||
let(:type) { FactoryGirl.create :type } |
||||
let(:type2) { FactoryGirl.create :type } |
||||
let(:project) { FactoryGirl.create(:project, types: [type, type2]) } |
||||
let(:work_package) { |
||||
FactoryGirl.create(:work_package, |
||||
author: dev, |
||||
project: project, |
||||
type: type, |
||||
subject: 'Foobar') |
||||
} |
||||
|
||||
let(:wp_table) { Pages::WorkPackagesTable.new(project) } |
||||
let(:fields) { InlineEditField.new(wp_table, work_package) } |
||||
|
||||
let(:priority2) { FactoryGirl.create :priority } |
||||
let(:status2) { FactoryGirl.create :status } |
||||
let(:workflow) do |
||||
FactoryGirl.create :workflow, |
||||
type_id: type2.id, |
||||
old_status: work_package.status, |
||||
new_status: status2, |
||||
role: manager_role |
||||
end |
||||
let(:version) { FactoryGirl.create :version, project: project } |
||||
let(:category) { FactoryGirl.create :category, project: project } |
||||
|
||||
before do |
||||
login_as(manager) |
||||
|
||||
manager |
||||
dev |
||||
priority2 |
||||
workflow |
||||
|
||||
wp_table.visit! |
||||
wp_table.expect_work_package_listed(work_package) |
||||
end |
||||
|
||||
it 'allows updating and seeing the results' do |
||||
subject_field = wp_table.edit_field(work_package, :subject) |
||||
subject_field.expect_text('Foobar') |
||||
|
||||
subject_field.activate! |
||||
|
||||
subject_field.set_value('New subject!') |
||||
|
||||
expect(UpdateWorkPackageService).to receive(:new).and_call_original |
||||
subject_field.save! |
||||
subject_field.expect_text('New subject!') |
||||
|
||||
work_package.reload |
||||
expect(work_package.subject).to eq('New subject!') |
||||
end |
||||
|
||||
it 'allows to subsequently edit multiple fields' do |
||||
subject_field = wp_table.edit_field(work_package, :subject) |
||||
priority_field = wp_table.edit_field(work_package, :priority) |
||||
|
||||
expect(UpdateWorkPackageService).to receive(:new).and_call_original |
||||
subject_field.activate! |
||||
subject_field.set_value('Other subject!') |
||||
|
||||
priority_field.activate! |
||||
priority_field.set_value(priority2.name) |
||||
priority_field.expect_inactive! |
||||
|
||||
subject_field.expect_text('Other subject!') |
||||
priority_field.expect_text(priority2.name) |
||||
|
||||
work_package.reload |
||||
expect(work_package.subject).to eq('Other subject!') |
||||
expect(work_package.priority.id).to eq(priority2.id) |
||||
end |
||||
|
||||
it 'provides error handling' do |
||||
subject_field = wp_table.edit_field(work_package, :subject) |
||||
subject_field.expect_text('Foobar') |
||||
|
||||
subject_field.activate! |
||||
|
||||
subject_field.set_value('') |
||||
|
||||
expect(UpdateWorkPackageService).to receive(:new).and_call_original |
||||
subject_field.save! |
||||
subject_field.expect_error |
||||
|
||||
work_package.reload |
||||
expect(work_package.subject).to eq('Foobar') |
||||
end |
||||
end |
@ -0,0 +1,85 @@ |
||||
class InlineEditField |
||||
include Capybara::DSL |
||||
include RSpec::Matchers |
||||
|
||||
attr_reader :work_package, :attribute, :element, :selector |
||||
|
||||
def initialize(work_package, attribute, field_type: nil) |
||||
@work_package = work_package |
||||
@attribute = attribute |
||||
@field_type = field_type |
||||
|
||||
@selector = "#work-package-#{work_package.id} .#{attribute}" |
||||
@element = page.find(selector) |
||||
end |
||||
|
||||
def expect_text(text) |
||||
expect(page).to have_selector(selector, text: text) |
||||
end |
||||
|
||||
## |
||||
# Activate the field and check it opened correctly |
||||
def activate! |
||||
edit_field.click |
||||
expect_active! |
||||
end |
||||
|
||||
## |
||||
# Set or select the given value. |
||||
# For fields of type select, will check for an option with that value. |
||||
def set_value(content) |
||||
if field_type == 'select' |
||||
input_field.find(:option, content).select_option |
||||
else |
||||
input_field.set(content) |
||||
end |
||||
end |
||||
|
||||
def expect_error |
||||
expect(page).to have_selector("#{field_selector}.-error") |
||||
end |
||||
|
||||
def expect_active! |
||||
expect(edit_field).to have_selector(field_type) |
||||
end |
||||
|
||||
def expect_inactive! |
||||
expect(edit_field).to have_no_selector(field_type) |
||||
end |
||||
|
||||
def save! |
||||
input_field.native.send_keys(:return) |
||||
reset_field |
||||
end |
||||
|
||||
def edit_field |
||||
@edit_field ||= @element.find('.wp-edit-field') |
||||
end |
||||
|
||||
def input_field |
||||
@input_field ||= edit_field.find(field_type) |
||||
end |
||||
|
||||
private |
||||
|
||||
def field_selector |
||||
"#{selector} .wp-edit-field" |
||||
end |
||||
|
||||
## |
||||
# Reset the input field e.g., after saving |
||||
def reset_field |
||||
@input_field = nil |
||||
end |
||||
|
||||
def field_type |
||||
@field_type ||= begin |
||||
case attribute |
||||
when :assignee, :priority, :status |
||||
:select |
||||
else |
||||
:input |
||||
end.to_s |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue