Merge pull request #4996 from opf/feature/23759/description-styling

[23759] Update relation description styling
pull/5001/head
Oliver Günther 8 years ago committed by GitHub
commit df90c58f04
  1. 41
      app/assets/stylesheets/content/work_packages/inplace_editing/_textareas.sass
  2. 43
      app/assets/stylesheets/content/work_packages/tabs/_relations.sass
  3. 1
      app/assets/stylesheets/open_project_global/_variables.sass
  4. 2
      config/locales/js-en.yml
  5. 4
      frontend/app/components/wp-edit/field-types/wp-edit-wiki-textarea-field.directive.html
  6. 71
      frontend/app/components/wp-relations/wp-relation-row/wp-relation-row.directive.ts
  7. 67
      frontend/app/components/wp-relations/wp-relation-row/wp-relation-row.template.html
  8. 106
      spec/features/work_packages/details/details_relations_spec.rb

@ -37,28 +37,27 @@
font-size: 1rem
line-height: 1.6
.inplace-edit
.textarea-wrapper
// Leave room below textarea for inplace edit controls
position: relative
margin-bottom: 50px
// Don't show all jstb buttons in inplace-edit textareas
.jstHandle, .jstSpacer
.textarea-wrapper
// Leave room below textarea for inplace edit controls
position: relative
margin-bottom: 50px
// Don't show all jstb buttons in inplace-edit textareas
.jstHandle, .jstSpacer
display: none
.jstElements
button
display: none
.jstElements
button
display: none
.jstb_strong,
.jstb_em,
.jstb_ins,
.jstb_del,
.jstb_ul,
.jstb_ol,
.jstb_preview,
.jstb_help
display: inline-flex
justify-content: center
.jstb_strong,
.jstb_em,
.jstb_ins,
.jstb_del,
.jstb_ul,
.jstb_ol,
.jstb_preview,
.jstb_help
display: inline-flex
justify-content: center
// Styles for the Save | Cancel controls below textareas
.inplace-edit--controls

@ -62,9 +62,6 @@
padding: 4px
.wp-edit-field
width: 90%
.description-container
border: 1px dotted lightblue
padding: 4px 0 4px 4px
.controls-container
text-align: right
@ -86,6 +83,19 @@
a:hover
text-decoration: none
.relation-row--remove-btn
visibility: hidden
&.-visible .relation-row--remove-btn
visibility: visible
.wp-relations--icon
color: $content-icon-link-color
// Toggle description icon color when expanded
.wp-relations-controls-section.-expanded .wp-relations--description-icon
color: $content-icon-link-pressed-color
.wp-relations-create
margin-top: 1.5em
@ -104,5 +114,32 @@
.wp-relations--subject-field
word-break: break-all
// Relation description field
.wp-relation--description-read-value
width: 100%
padding: 5px
border: 1px solid transparent
// Wrap newlines in the description
white-space: pre-wrap
&.-placeholder
font-style: italic
&:hover
border-color: $light-gray
cursor: text
// Set min-height to read/write identically
.wp-relation--description-read-value,
.wp-relation--description-textarea
min-height: 60px
font-size: 0.875rem
line-height: $base-line-height
padding-top: 10px
.wp-relation--description-wrapper
width: 100%

@ -144,6 +144,7 @@ $content-link-color: $primary-color-dark !default
$content-link-hover-active-color: $primary-color-dark !default
$content-icon-link-color: #4b4b4b !default
$content-icon-link-hover-color: $primary-color-dark !default
$content-icon-link-pressed-color: $gray-dark !default
$content-icon-color: $primary-color-dark !default

@ -221,6 +221,7 @@ en:
placeholders:
default: '-'
selection: 'Please select'
relation_description: 'Click to add description for this relation'
text_are_you_sure: "Are you sure?"
@ -289,6 +290,7 @@ en:
add_existing_child: "Add existing child"
remove_child: "Remove child"
add_new_relation: "Create new relation"
update_description: "Set or update description of this relation"
remove: "Remove relation"
save: "Save relation"
abort: "Abort"

@ -21,7 +21,7 @@
field-controller="vm"
on-save="vm.handleUserSubmit()"
on-cancel="vm.handleUserCancel()"
save-title="{{ vm.field.text.saveTitle }}"
cancel-title="{{ vm.field.text.cancelTitle }}">
save-title="{{ ::$ctrl.text.save }}"
cancel-title="{{ ::$ctrl.text.cancel }}">
</wp-edit-field-controls>
</div>

@ -19,16 +19,26 @@ class WpRelationRowDirectiveController {
public selectedRelationType: RelationResourceInterface;
public userInputs = {
description:this.relatedWorkPackage.relatedBy.description,
showDescriptionEditForm:false,
newRelationText: '',
showDescriptionEditForm: false,
showRelationTypesForm: false,
showRelationInfo:false
showRelationInfo: false,
showRelationControls: false,
};
// Create a quasi-field object
public fieldController = {
active: true,
field: {
required: false
}
}
public relation: RelationResourceInterface = this.relatedWorkPackage.relatedBy;
public text: Object;
constructor(protected $scope: ng.IScope,
protected $element: ng.IAugmentedJQuery,
protected $timeout:ng.ITimeoutService,
protected $http,
protected wpCacheService: WorkPackageCacheService,
@ -38,8 +48,16 @@ class WpRelationRowDirectiveController {
protected PathHelper: op.PathHelper) {
this.text = {
removeButton:this.I18n.t('js.relation_buttons.remove')
cancel: I18n.t('js.button_cancel'),
save: I18n.t('js.button_save'),
removeButton: I18n.t('js.relation_buttons.remove'),
description_label: I18n.t('js.relation_buttons.update_description'),
placeholder: {
description: I18n.t('js.placeholders.relation_description')
}
};
this.userInputs.newRelationText = this.relation.description || '';
this.availableRelationTypes = wpRelationsService.getRelationTypes(true);
this.selectedRelationType = _.find(this.availableRelationTypes, {'name': this.relation.type});
};
@ -57,15 +75,56 @@ class WpRelationRowDirectiveController {
return this.relatedWorkPackage && this.relatedWorkPackage.$loaded;
}
public startDescriptionEdit() {
this.userInputs.showDescriptionEditForm = true;
this.$timeout(() => {
var textarea = this.$element.find('.wp-relation--description-textarea');
var textlen = textarea.val().length;
// Focus and set cursor to end
textarea.focus();
textarea.prop('selectionStart', textlen);
textarea.prop('selectionEnd', textlen);
});
}
public handleDescriptionKey($event) {
if ($event.which === 27) {
this.cancelDescriptionEdit();
}
}
public cancelDescriptionEdit() {
this.userInputs.showDescriptionEditForm = false;
this.userInputs.newRelationText = this.relation.description || '';
}
public saveDescription() {
this.relation.updateImmediately({
description: this.relation.description
}).then(() => {
description: this.userInputs.newRelationText
}).then((savedRelation) => {
this.relation = savedRelation;
this.relatedWorkPackage.relatedBy = savedRelation;
this.userInputs.showDescriptionEditForm = false;
this.wpNotificationsService.showSave(this.relatedWorkPackage);
});
}
public get showDescriptionInfo() {
// Show when relation info is expanded
if (this.userInputs.showRelationInfo) {
return true;
}
// Show when relation has a description
if (this.relation.description) {
return true;
}
// Show depending on mouseover
return this.userInputs.showRelationControls;
}
public saveRelationType() {
this.relation.updateImmediately({
type: this.selectedRelationType.name

@ -1,7 +1,7 @@
<div class="relation-row"
ng-class="['relation-row-{{ $ctrl.relatedWorkPackage.id }}']"
ng-mouseover="$ctrl.showRelationControls = true"
ng-mouseleave="$ctrl.showRelationControls = false">
ng-mouseover="$ctrl.userInputs.showRelationControls = true"
ng-mouseleave="$ctrl.userInputs.showRelationControls = false">
<div class="grid-block hierarchy-item">
<div class="grid-content medium-3 collapse" aria-hidden="true">
@ -33,20 +33,27 @@
</div>
</div>
<div class="grid-content medium-2 collapse wp-relations-controls-section">
<accessible-by-keyboard ng-show="$ctrl.showRelationControls"
<div class="grid-content medium-2 collapse wp-relations-controls-section"
ng-class="{ '-visible': $ctrl.userInputs.showRelationControls, '-expanded': $ctrl.userInputs.showRelationInfo }">
<accessible-by-keyboard ng-show="$ctrl.showDescriptionInfo"
aria-label="{{ ::$ctrl.text.description_label }}"
link-title="{{ ::$ctrl.text.description_label }}"
link-class="wp-relations--description-btn"
execute="$ctrl.userInputs.showRelationInfo = !$ctrl.userInputs.showRelationInfo">
<icon-wrapper icon-name="info1"
css-class="wp-relations--icon wp-relations--description-icon"
icon-title="Info for related workpackage">
</icon-wrapper>
</accessible-by-keyboard>
<accessible-by-keyboard ng-show="$ctrl.showRelationControls"
ng-if="$ctrl.relation.delete"
<accessible-by-keyboard ng-if="$ctrl.relation.delete"
execute="$ctrl.removeRelation($ctrl.relation)"
aria-hidden="false"
aria-label="{{ ::$ctrl.text.remove }}"
link-title="{{ ::$ctrl.text.remove }}"
link-class="relation-row--remove-btn"
class="-shown-in-accessibility-mode">
<icon-wrapper icon-name="remove"
css-class="wp-relations--icon"
icon-title="{{ ::$ctrl.text.removeButton }}">
</icon-wrapper>
</accessible-by-keyboard>
@ -54,35 +61,27 @@
</div>
<div class="grid-block hierarchy-item description-container" ng-show="$ctrl.userInputs.showRelationInfo" ng-init="editDescription = false">
<div class="grid-content medium-10 collapse">
<div ng-hide="$ctrl.userInputs.showDescriptionEditForm">
{{$ctrl.relation.description || 'no description set'}}
</div>
<div ng-show="$ctrl.userInputs.showDescriptionEditForm">
<div class="wp-edit-field inplace-edit">
<input class="wp-inline-edit--field" type="text" ng-model="$ctrl.relation.description" />
</div>
</div>
<div class="wp-relation--description-read-value"
ng-class="{'-placeholder': !$ctrl.relation.description }"
ng-click="$ctrl.startDescriptionEdit()"
ng-hide="$ctrl.userInputs.showDescriptionEditForm"
ng-bind="$ctrl.relation.description || $ctrl.text.placeholder.description">
</div>
<div class="grid-content medium-2 collapse wp-relations-controls-section" ng-hide="$ctrl.userInputs.showDescriptionEditForm">
<accessible-by-keyboard execute="$ctrl.userInputs.showDescriptionEditForm = true">
<icon-wrapper icon-name="edit"
icon-title="edit description">
</icon-wrapper>
</accessible-by-keyboard>
<div class="wp-relation--description-wrapper textarea-wrapper"
ng-show="$ctrl.userInputs.showDescriptionEditForm">
<textarea
msd-elastic="\n"
autofocus
class="wp-relation--description-textarea"
name="description"
ng-keyup="$ctrl.handleDescriptionKey($event)"
ng-model="$ctrl.userInputs.newRelationText"></textarea>
<wp-edit-field-controls field-controller="$ctrl.fieldController"
on-save="$ctrl.saveDescription()"
on-cancel="$ctrl.cancelDescriptionEdit()"
save-title="{{ vm.field.text.save }}"
cancel-title="{{ vm.field.text.cancel }}">
</wp-edit-field-controls>
</div>
<div class="grid-content medium-2 collapse wp-relations-controls-section" ng-show="$ctrl.userInputs.showDescriptionEditForm">
<accessible-by-keyboard execute="$ctrl.saveDescription()">
<icon-wrapper icon-name="checkmark"
icon-title="save description'">
</icon-wrapper>
</accessible-by-keyboard>
<accessible-by-keyboard execute="$ctrl.userInputs.showDescriptionEditForm = false">
<icon-wrapper icon-name="remove"
icon-title="cancel editing'">
</icon-wrapper>
</accessible-by-keyboard>
</div>
</div>
</div>

@ -32,6 +32,41 @@ describe 'Work package relations tab', js: true, selenium: true do
wait: 10)
end
def add_relation(relation_type, wp)
# Open create form
find('#relation--add-relation').click
# Select relation type
container = find('.wp-relations-create--form', wait: 10)
# Labels to expect
relation_label = I18n.t('js.relation_labels.' + relation_type)
type_upcase = wp.type.name.upcase
select relation_label, from: 'relation-type--select'
# Enter the query and select the child
typeahead = container.find(".wp-relations--autocomplete")
select_typeahead(typeahead, query: wp.subject, select_text: wp.subject)
container.find('.wp-create-relation--save').click
expect(page).to have_selector('.relation-group--header',
text: type_upcase,
wait: 10)
expect(page).to have_selector('.relation-row--type', text: relation_label)
expect(page).to have_selector('.wp-relations--subject-field', text: wp.subject)
## Test if relation exist
work_package.reload
relation = work_package.relations.first
expect(relation.relation_type).to eq('precedes')
expect(relation.from_id).to eq(relatable.id)
expect(relation.to_id).to eq(work_package.id)
end
def remove_hierarchy(selector, removed_text)
expect(page).to have_selector(selector, text: removed_text)
container = find(selector)
@ -156,51 +191,58 @@ describe 'Work package relations tab', js: true, selenium: true do
let!(:relatable) { FactoryGirl.create(:work_package, project: project) }
it 'should allow to manage relations' do
# Open create form
find('#relation--add-relation').click
add_relation('follows', relatable)
# Select relation type
container = find('.wp-relations-create--form', wait: 10)
## Delete relation
created_row = find(".relation-row-#{relatable.id}")
# Labels to expect
follows_label = I18n.t('js.relation_labels.follows')
type_upcase = work_package.type.name.upcase
# Hover row to expose button
created_row.hover
created_row.find('.relation-row--remove-btn').click
select follows_label, from: 'relation-type--select'
expect(page).to have_no_selector('.relation-group--header',
text: relatable.type.name.upcase)
expect(page).to have_no_selector('.wp-relations--subject-field', text: relatable.subject)
# Enter the query and select the child
typeahead = container.find(".wp-relations--autocomplete")
select_typeahead(typeahead, query: relatable.subject, select_text: relatable.subject)
work_package.reload
expect(work_package.relations).to be_empty
end
container.find('.wp-create-relation--save').click
it 'should allow to change relation descriptions' do
add_relation('follows', relatable)
expect(page).to have_selector('.relation-group--header',
text: type_upcase,
wait: 10)
created_row = find(".relation-row-#{relatable.id}")
expect(page).to have_selector('.relation-row--type', text: follows_label)
## Toggle description
created_row.hover
toggle_btn = created_row.find('.wp-relations--description-btn')
toggle_btn.click
expect(page).to have_selector('.wp-relations--subject-field', text: relatable.subject)
# Open textarea
created_row.find('.wp-relation--description-read-value.-placeholder',
text: I18n.t('js.placeholders.relation_description')).click
## Test if relation exist
work_package.reload
relation = work_package.relations.first
expect(relation.relation_type).to eq('precedes')
expect(relation.from_id).to eq(relatable.id)
expect(relation.to_id).to eq(work_package.id)
expect(page).to have_focus_on('.wp-relation--description-textarea')
textarea = created_row.find('.wp-relation--description-textarea')
textarea.set 'my description!'
## Delete relation
created_row = find(".relation-row-#{relatable.id}")
# Save description
created_row.find('.inplace-edit--control--save a').click
created_row.find('.wp-relation--description-read-value',
text: 'my description!').click
# Hover row to expose button
created_row.hover
created_row.find('.relation-row--remove-btn').click
# Cancel edition
created_row.find('.inplace-edit--control--cancel a').click
created_row.find('.wp-relation--description-read-value',
text: 'my description!').click
expect(page).to have_no_selector('.relation-group--header', text: type_upcase)
expect(page).to have_no_selector('.wp-relations--subject-field', text: relatable.subject)
relation = work_package.relations.first
relation.reload
expect(relation.description).to eq('my description!')
work_package.reload
expect(work_package.relations).to be_empty
# Toggle to close
toggle_btn.click
expect(created_row).to have_no_selector('.wp-relation--description-read-value')
end
end
end

Loading…
Cancel
Save