Merge remote-tracking branch 'origin/release/9.0' into release/9.1

[ci skip]
pull/7526/head
Oliver Günther 5 years ago
commit 37a7669b9a
No known key found for this signature in database
GPG Key ID: A3A8BDAD7C0C552C
  1. 1
      app/assets/stylesheets/content/work_packages/timelines/elements/_bar.sass
  2. 42
      app/models/custom_value/formattable_strategy.rb
  3. 2
      app/views/customizable/_form.html.erb
  4. 3
      config/initializers/custom_field_format.rb
  5. 10
      frontend/src/app/components/wp-table/timeline/cells/timeline-cell-renderer.ts
  6. 28
      frontend/src/app/components/wp-table/timeline/cells/wp-timeline-cell.ts
  7. 3
      lib/custom_field_form_builder.rb
  8. 1
      lib/open_project/design.rb
  9. 2
      lib/open_project/form_tag_helper.rb
  10. 8
      spec/features/projects/copy_spec.rb
  11. 29
      spec/features/projects/projects_custom_fields_spec.rb
  12. 3
      spec/lib/custom_field_form_builder_spec.rb

@ -43,6 +43,7 @@
.timeline-element--bg
width: 100%
height: 100%
@include varprop(background-color, timeline--type-fallback-color)
&.-readonly
cursor: not-allowed !important

@ -0,0 +1,42 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 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-2017 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 docs/COPYRIGHT.rdoc for more details.
#++
class
CustomValue::FormattableStrategy < CustomValue::FormatStrategy
def formatted_value
OpenProject::TextFormatting::Renderer.format_text value
end
def typed_value
value.to_s
end
def validate_type_of_value; end
end

@ -32,7 +32,7 @@ See docs/COPYRIGHT.rdoc for more details.
<% form.object.custom_field_values.each do |value| %>
<%= form.fields_for_custom_fields :custom_field_values, value do |value_form| %>
<% required = value.custom_field.is_required? %>
<div class="form--field <%= required ? '-required' : '' %>">
<div class="form--field custom_field_<%= value.custom_field.id %> <%= required ? '-required' : '' %>">
<%# display all fields OR only display required fields OR only display optional fields #%>
<% if all_fields || (only_required && required) || (!only_required && !required) %>
<%= value_form.custom_field(container_class: '-wide', required: required) %>

@ -34,7 +34,8 @@ OpenProject::CustomFieldFormat.map do |fields|
order: 1)
fields.register OpenProject::CustomFieldFormat.new('text',
label: :label_text,
order: 2)
order: 2,
formatter: 'CustomValue::FormattableStrategy')
fields.register OpenProject::CustomFieldFormat.new('int',
label: :label_integer,
order: 3,

@ -59,10 +59,6 @@ export class TimelineCellRenderer {
return 'bar';
}
public get fallbackColor():string {
return 'rgba(50, 50, 50, 0.1)';
}
public canMoveDates(wp:WorkPackageResource) {
return wp.schema.startDate.writable && wp.schema.dueDate.writable && wp.isAttributeEditable('startDate');
}
@ -372,12 +368,6 @@ export class TimelineCellRenderer {
let type = wp.type;
let selectionMode = renderInfo.viewParams.activeSelectionMode;
if (!type && !selectionMode) {
bg.style.backgroundColor = this.fallbackColor;
} else {
bg.style.backgroundColor = '';
}
// Don't apply the class in selection mode or for parents (clamps)
const id = type.id;
if (selectionMode || this.is_parent_with_displayed_children(wp)) {

@ -124,19 +124,19 @@ export class WorkPackageTimelineCell {
return this.cellContainer.find(`.${this.classIdentifier}`);
}
private lazyInit(renderer:TimelineCellRenderer, renderInfo:RenderInfo) {
private lazyInit(renderer:TimelineCellRenderer, renderInfo:RenderInfo):Promise<void> {
const body = this.workPackageTimeline.timelineBody[0];
const cell = this.cellElement;
if (!cell.length) {
return;
return Promise.reject();
}
const wasRendered = this.wpElement !== null && body.contains(this.wpElement);
// If already rendered with correct shape, ignore
if (wasRendered && (this.elementShape === renderer.type)) {
return;
return Promise.resolve();
}
// Remove the element first if we're redrawing
@ -168,6 +168,8 @@ export class WorkPackageTimelineCell {
renderer,
renderInfo);
}
return Promise.resolve();
}
private cellRenderer(workPackage:WorkPackageResource):TimelineCellRenderer {
@ -183,17 +185,17 @@ export class WorkPackageTimelineCell {
const renderer = this.cellRenderer(renderInfo.workPackage);
// Render initial element if necessary
this.lazyInit(renderer, renderInfo);
// Render the upgrade from renderInfo
const shouldBeDisplayed = renderer.update(
this.wpElement as HTMLDivElement,
this.labels,
renderInfo);
this.lazyInit(renderer, renderInfo).then(() => {
// Render the upgrade from renderInfo
const shouldBeDisplayed = renderer.update(
this.wpElement as HTMLDivElement,
this.labels,
renderInfo);
if (!shouldBeDisplayed) {
this.clear();
}
if (!shouldBeDisplayed) {
this.clear();
}
});
}
}

@ -31,6 +31,7 @@ require 'action_view/helpers/form_helper'
class CustomFieldFormBuilder < TabularFormBuilder
include ActionView::Context
# Return custom field html tag corresponding to its format
def custom_field(options = {})
input = custom_field_input(options)
@ -67,7 +68,7 @@ class CustomFieldFormBuilder < TabularFormBuilder
input_options[:class] = (input_options[:class] || '') << ' -augmented-datepicker'
text_field(field, input_options)
when 'text'
text_area(field, input_options.merge(rows: 3))
text_area(field, input_options.merge(rows: 3, with_text_formatting: true))
when 'bool'
formatter = field_format.formatter.new(object)
check_box(field, input_options.merge(checked: formatter.checked?))

@ -199,6 +199,7 @@ module OpenProject
'timeline--header-border-color' => '#aaaaaa',
'timeline--grid-color' => '#dddddd',
'timeline--separator' => '3px solid #E7E7E7',
'timeline--type-fallback-color' => 'rgba(150, 150, 150, 0.8)',
'table-timeline--row-height' => '40px',
'status-selector-bg-color' => '#F99601',
'status-selector-bg-hover-color' => '#E08600',

@ -85,7 +85,7 @@ module OpenProject
##
# Create a wrapper for the text formatting toolbar for this field
def text_formatting_wrapper(target_id, options)
def text_formatting_wrapper(target_id, options = {})
return ''.html_safe unless target_id.present?
helper = ::OpenProject::TextFormatting::Formats.rich_helper.new(self)

@ -136,8 +136,8 @@ describe 'Projects copy',
check 'only_wiki_page_attachments'
# the value of the custom field should be preselected
expect(page)
.to have_field(project_custom_field.name, with: 'some text cf')
editor = ::Components::WysiwygEditor.new ".form--field.custom_field_#{project_custom_field.id}"
editor.expect_value 'some text cf'
click_button 'Copy'
@ -160,8 +160,8 @@ describe 'Projects copy',
selected: parent_project.name)
# copies over the value of the custom field
expect(page)
.to have_field(project_custom_field.name, with: 'some text cf')
editor = ::Components::WysiwygEditor.new ".form--field.custom_field_#{project_custom_field.id}"
editor.expect_value 'some text cf'
# has wp custom fields of original project active
copied_settings_page.visit_tab!('custom_fields')

@ -54,12 +54,39 @@ describe 'Projects custom fields', type: :feature do
end
end
describe 'with long text CF' do
let!(:custom_field) do
FactoryBot.create(:text_project_custom_field)
end
let(:editor) { ::Components::WysiwygEditor.new ".form--field.custom_field_#{custom_field.id}" }
scenario 'allows settings the project boolean CF (regression #26313)', js: true do
visit settings_project_path(id: project.id)
# expect CF and description ckeditor
expect(page).to have_selector('.op-ckeditor--wrapper', count: 2)
# single hash autocomplete
editor.insert_link 'http://example.org/link with spaces'
# Save wiki page
click_on 'Save'
expect(page).to have_selector('.flash.notice')
project.reload
cv = project.custom_values.find_by(custom_field_id: custom_field.id).value
expect(cv).to include '[http://example.org/link with spaces](http://example.org/link%20with%20spaces)'
expect(page).to have_selector('a[href="http://example.org/link%20with%20spaces"]')
end
end
describe 'with boolean CF' do
let!(:custom_field) do
FactoryBot.create(:bool_project_custom_field)
end
scenario 'allows settings the project boolean CF (regression #26313)', js: true do
visit settings_project_path(id: project.id)
expect(page).to have_no_checked_field identifier

@ -103,7 +103,8 @@ describe CustomFieldFormBuilder do
<textarea class="custom-class form--text-area"
id="user#{resource.custom_field_id}"
name="user[#{resource.custom_field_id}]"
rows="3">
rows="3"
with_text_formatting="true">
</textarea>
}).at_path('textarea')
end

Loading…
Cancel
Save