Merge remote-tracking branch 'origin/feature/widgets' into feature/animated_progress_bar

pull/6827/head
Tim Felgentreff 14 years ago
commit 8028a5bbe0
  1. 77
      assets/javascripts/reporting/filters.js
  2. 2
      assets/javascripts/reporting/restore_query.js
  3. 5
      assets/stylesheets/reporting.css
  4. 15
      lib/report/controller.rb
  5. 41
      lib/report/filter/base.rb
  6. 6
      lib/report/group_by/base.rb
  7. 2
      lib/widget/filters/label.rb
  8. 6
      lib/widget/filters/multi_values.rb

@ -208,9 +208,11 @@ Reporting.Filters = {
remove_filter: function (field) {
Reporting.Filters.show_filter(field, { show_filter: false });
var dependents = Reporting.Filters.get_dependents($(field + '_arg_1_val'));
if (dependents.size() !== 0) {
Reporting.Filters.remove_filter(dependents.first());
var dependent = Reporting.Filters.get_dependents($(field + '_arg_1_val'), false).find(function(d) {
return Reporting.Filters.visible_filters().include(d);
});
if (dependent !== undefined) {
Reporting.Filters.remove_filter(dependent);
}
Reporting.Filters.select_option_enabled($("add_filter_select"), field, true);
},
@ -229,9 +231,16 @@ Reporting.Filters = {
});
},
get_dependents: function (element) {
if (element.hasAttribute("data-dependents")) {
return element.getAttribute("data-dependents").replace(/'/g, '"').evalJSON(true);
// Returns an array of dependents of the given element
// get_all -> Boolean: whether to return all dependends (even the
// dependents of this filters dependents) or not
get_dependents: function (element, get_all) {
var dependent_field = "data-all-dependents";
if (get_all === false) {
dependent_field = "data-next-dependents";
}
if (element.hasAttribute(dependent_field)) {
return element.getAttribute(dependent_field).replace(/'/g, '"').evalJSON(true);
} else {
return [];
}
@ -242,35 +251,55 @@ Reporting.Filters = {
// narrowing down their values.
// Param: select [optional] - the select-box of the filter which should activate it's dependents
activate_dependents: function (selectBox, callbackWhenFinished) {
var dependents, active_filters, source;
var all_dependents, next_dependents, dependent, active_filters, source;
if (selectBox === undefined || selectBox.type.toLowerCase() == 'change') {
selectBox = this;
}
if (callbackWhenFinished === undefined) {
callbackWhenFinished = function() {};
}
dependents = Reporting.Filters.get_dependents(selectBox);
source = selectBox.getAttribute("data-filter-name");
all_dependents = Reporting.Filters.get_dependents(selectBox);
next_dependents = Reporting.Filters.get_dependents(selectBox, false);
dependent = Reporting.Filters.which_dependent_shall_i_take(source, next_dependents);
active_filters = Reporting.Filters.visible_filters();
if (!active_filters.include(dependents.first())) {
Reporting.Filters.show_filter(dependents.first(), { slowly: true, insert_after: $(selectBox.up(".filter")) });
if (!active_filters.include(dependent)) {
// in case we run into a situation where the dependent to show is not in the currently selected dependency chain
// we have to remove all filters until we reach the source and add the new dependent
if (next_dependents.any( function(d){ return active_filters.include(d) } )) {
while (active_filters.last() !== source) {
Reporting.Filters.show_filter(active_filters.pop(1), { show_filter: false, slowly: true });
}
}
Reporting.Filters.show_filter(dependent, { slowly: true, insert_after: $(selectBox.up(".filter")) });
// render filter inactive if possible to avoid unintended filtering
$(dependents.first() + '_arg_1_val').value = '<<inactive>>'
Reporting.Filters.operator_changed(dependents.first(), $('operators[' + dependents.first() + ']'));
$(dependent + '_arg_1_val').value = '<<inactive>>'
Reporting.Filters.operator_changed(dependent, $('operators[' + dependent + ']'));
// Hide remove box of dependent
$('rm_box_' + dependents.first()).hide();
$('tr_' + dependents.first()).addClassName("no-border");
$('rm_box_' + dependent).hide();
// Remove border of dependent, so it "merges" with the filter before
active_filters.unshift(dependents.first());
$('tr_' + dependent).addClassName("no-border");
active_filters.unshift(dependent);
}
source = selectBox.getAttribute("data-filter-name");
setTimeout(function () { // Make sure the newly shown filters are in the DOM
var active_dependents = dependents.select(function (d) {
var active_dependents = all_dependents.select(function (d) {
return active_filters.include(d);
});
Reporting.Filters.narrow_values([source], active_dependents, callbackWhenFinished);
Reporting.Filters.narrow_values(Reporting.Filters.dependent_for(source), active_dependents, callbackWhenFinished);
}, 1);
},
// return an array of all filters that depend on the given filter plus the given filter
dependent_for: function(field) {
var deps = $$('.filters-select[data-all-dependents]').findAll(function(selectBox) {
return Reporting.Filters.get_dependents(selectBox).include(field)
}).map(function(selectBox) {
return selectBox.getAttribute("data-filter-name");
});
return deps === undefined ? [ field ] : [ field ].concat(deps)
},
// Select the given values of the selectBox.
// Toggle multi-select state of the selectBox depending on how many values were given.
select_values: function(selectBox, values_to_select) {
@ -293,7 +322,8 @@ Reporting.Filters = {
if (callbackWhenFinished === undefined) {
callbackWhenFinished = function() {};
}
var params = "?narrow_values=1&dependent=" + dependents.first();
var params = document.location.href.include('?') ? '&' : '?'
params = params + "narrow_values=1&dependent=" + dependents.first();
sources.each(function (filter) {
params = params + "&sources[]=" + filter;
});
@ -357,6 +387,13 @@ Reporting.Filters = {
}
}
);
},
// This method may be overridden by the actual application to define custon behavior
// If there are multiple possible dependents to follow.
// The dependent to follow should be returned.
which_dependent_shall_i_take: function(source, dependents) {
return dependents.first();
}
};
@ -392,7 +429,7 @@ Reporting.onload(function () {
Reporting.Filters.value_changed(filter_name);
});
});
$$('.filters-select[data-dependents]').each(function (dependency) {
$$('.filters-select[data-all-dependents]').each(function (dependency) {
dependency.observe("change", Reporting.Filters.activate_dependents);
});
});

@ -76,7 +76,7 @@ Reporting.RestoreQuery = {
Reporting.Filters.operator_changed(filter_name, $("operators[" + filter_name + "]"));
});
// restore values of dependent filters
Reporting.RestoreQuery.initialize_load_dependent_filters($$('.filters-select[data-dependents]').findAll(function(select) {
Reporting.RestoreQuery.initialize_load_dependent_filters($$('.filters-select[data-all-dependents]').findAll(function(select) {
return select.up('tr').visible()
}));
},

@ -110,6 +110,11 @@
vertical-align: top;
}
#query_form_content {
width: 916px;
}
/* Overwriting styling for headlines within the query. */
/* TODO: Font-size seems to be a bit odd. Needs some love. */
.new_report fieldset h3 {

@ -140,8 +140,21 @@ module Report::Controller
:values => params[:values][dependency])
end
query.column(dependent)
values = [[::I18n.t(:label_inactive), '<<inactive>>']] + query.result.collect {|r| r.fields[dependent] }
values = [[::I18n.t(:label_inactive), '<<inactive>>']] + query.result.collect {|r| r.fields[query.group_bys.first.field] }
# replace null-values with corresponding placeholder
values = values.map { |value| value.nil? ? [::I18n.t(:label_none), '<<null>>'] : value }
# try to find corresponding labels to the given values
values = values.map do |value|
filter = report_engine::Filter.const_get(dependent.camelcase.to_sym)
filter_value = filter.label_for_value value
if filter_value && filter_value.first.is_a?(Symbol)
[::I18n.t(filter_value.first), filter_value.second]
elsif filter_value && filter_value.first.is_a?(String)
[filter_value.first, filter_value.second]
else
value
end
end
render :json => values.to_json
end
end

@ -23,7 +23,6 @@ class Report::Filter
false
end
##
# Indicates whether this Filter is a multiple choice filter,
# meaning that the user must select a value of a given set of choices.
def self.is_multiple_choice?
@ -31,26 +30,31 @@ class Report::Filter
end
##
# A Filter may have a depentent filter. See the following example:
# Filter::Project.dependent --> Filter::Issue
# This could result in a UI where, if the Prject-filter was selected,
# the Issue-filter automatically shows up.
# A Filter may have depentent filters. See the following example:
# Filter::Project.dependents --> [Filter::IssueId]
# This could result in a UI where, if the Project-Filter was selected,
# the IssueId-filter automatically shows up.
# Arguments:
# - any subclass of Reporting::Filter::Base which shall be the dependent filter
# or nil, if you want to remove the dependent relationship
# - OR multiple Filters if there are multiple possible dependents and you
# want the application-js to decide which dependent to follow
def self.dependent(*args)
@dependent = args.first unless args.empty?
@dependent
@dependents ||= []
@dependents += args unless args.empty?
@dependents
end
class << self
alias :dependents :dependent
end
def self.has_dependent?
!!@dependent
!dependents.empty?
end
##
# Returns an array of filters of which this filter is a dependent
def self.dependent_from
engine::Filter.all.select { |f| Array(f.dependent).include? self}
engine::Filter.all.select { |f| f.dependents.include? self }
end
##
@ -71,14 +75,11 @@ class Report::Filter
self.cached(:compute_all_dependents)
end
def self.compute_all_dependents
dependents = []
dep = dependent
while !dep.nil? do
dependents << dep
dep = dep.dependent
def self.compute_all_dependents(starting_from = nil)
starting_from ||= dependents
starting_from.inject([]) do |list,dependent|
list + Array(dependent) + dependent.all_dependents
end
dependents
end
def value=(val)
@ -115,7 +116,11 @@ class Report::Filter
end
def self.available_values(params = {})
[]
[] #array of [:label_of_value, value]-kind arrays
end
def self.label_for_value(value)
available_values(:reverse_search => true).find{ |v| v.second == value || v.second.to_s == value }
end
def correct_position?

@ -4,11 +4,6 @@ class Report::GroupBy
inherited_attributes :group_fields, :list => true, :merge => false
def self.inherited(klass)
klass.group_fields klass.field
super
end
def correct_position?
type == :row or !child.is_a?(engine::GroupBy::Base) or child.type == :column
end
@ -71,6 +66,7 @@ class Report::GroupBy
def initialize(child = nil, optios = {})
super
extend aggregation_mixin
group_fields field
end
def result

@ -3,7 +3,7 @@ class Widget::Filters::Label < Widget::Filters::Base
def render
write(content_tag :td, :width => 150 do
options = { :id => filter_class.underscore_name }
if (engine::Filter.all.any? {|f| f.dependent == filter_class})
if (engine::Filter.all.any? {|f| f.dependents.include?(filter_class)})
options.merge! :class => 'dependent-filter-label'
end
content_tag :label, options do

@ -13,8 +13,10 @@ class Widget::Filters::MultiValues < Widget::Filters::Base
# multiple will be disabled/enabled later by JavaScript anyhow.
# We need to specify multiple here because of an IE6-bug.
if filter_class.has_dependent?
dependents = filter_class.all_dependents.map {|d| d.underscore_name}.to_json
select_options.merge! :"data-dependents" => dependents.gsub!('"', "'")
all_dependents = filter_class.all_dependents.map {|d| d.underscore_name}.to_json
select_options.merge! :"data-all-dependents" => all_dependents.gsub!('"', "'")
next_dependents = filter_class.dependents.map {|d| d.underscore_name}.to_json
select_options.merge! :"data-next-dependents" => next_dependents.gsub!('"', "'")
end
# store selected value(s) in data-initially-selected if this filter is a dependent
# of another filter, as we have to restore values manually in the client js

Loading…
Cancel
Save