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) { remove_filter: function (field) {
Reporting.Filters.show_filter(field, { show_filter: false }); Reporting.Filters.show_filter(field, { show_filter: false });
var dependents = Reporting.Filters.get_dependents($(field + '_arg_1_val')); var dependent = Reporting.Filters.get_dependents($(field + '_arg_1_val'), false).find(function(d) {
if (dependents.size() !== 0) { return Reporting.Filters.visible_filters().include(d);
Reporting.Filters.remove_filter(dependents.first()); });
if (dependent !== undefined) {
Reporting.Filters.remove_filter(dependent);
} }
Reporting.Filters.select_option_enabled($("add_filter_select"), field, true); Reporting.Filters.select_option_enabled($("add_filter_select"), field, true);
}, },
@ -229,9 +231,16 @@ Reporting.Filters = {
}); });
}, },
get_dependents: function (element) { // Returns an array of dependents of the given element
if (element.hasAttribute("data-dependents")) { // get_all -> Boolean: whether to return all dependends (even the
return element.getAttribute("data-dependents").replace(/'/g, '"').evalJSON(true); // 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 { } else {
return []; return [];
} }
@ -242,35 +251,55 @@ Reporting.Filters = {
// narrowing down their values. // narrowing down their values.
// Param: select [optional] - the select-box of the filter which should activate it's dependents // Param: select [optional] - the select-box of the filter which should activate it's dependents
activate_dependents: function (selectBox, callbackWhenFinished) { 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') { if (selectBox === undefined || selectBox.type.toLowerCase() == 'change') {
selectBox = this; selectBox = this;
} }
if (callbackWhenFinished === undefined) { if (callbackWhenFinished === undefined) {
callbackWhenFinished = function() {}; 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(); 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 // render filter inactive if possible to avoid unintended filtering
$(dependents.first() + '_arg_1_val').value = '<<inactive>>' $(dependent + '_arg_1_val').value = '<<inactive>>'
Reporting.Filters.operator_changed(dependents.first(), $('operators[' + dependents.first() + ']')); Reporting.Filters.operator_changed(dependent, $('operators[' + dependent + ']'));
// Hide remove box of dependent // Hide remove box of dependent
$('rm_box_' + dependents.first()).hide(); $('rm_box_' + dependent).hide();
$('tr_' + dependents.first()).addClassName("no-border");
// Remove border of dependent, so it "merges" with the filter before // 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 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); 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); }, 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. // Select the given values of the selectBox.
// Toggle multi-select state of the selectBox depending on how many values were given. // Toggle multi-select state of the selectBox depending on how many values were given.
select_values: function(selectBox, values_to_select) { select_values: function(selectBox, values_to_select) {
@ -293,7 +322,8 @@ Reporting.Filters = {
if (callbackWhenFinished === undefined) { if (callbackWhenFinished === undefined) {
callbackWhenFinished = function() {}; 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) { sources.each(function (filter) {
params = params + "&sources[]=" + 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); 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); dependency.observe("change", Reporting.Filters.activate_dependents);
}); });
}); });

@ -76,7 +76,7 @@ Reporting.RestoreQuery = {
Reporting.Filters.operator_changed(filter_name, $("operators[" + filter_name + "]")); Reporting.Filters.operator_changed(filter_name, $("operators[" + filter_name + "]"));
}); });
// restore values of dependent filters // 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() return select.up('tr').visible()
})); }));
}, },

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

@ -140,8 +140,21 @@ module Report::Controller
:values => params[:values][dependency]) :values => params[:values][dependency])
end end
query.column(dependent) 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 } 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 render :json => values.to_json
end end
end end

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

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

@ -3,7 +3,7 @@ class Widget::Filters::Label < Widget::Filters::Base
def render def render
write(content_tag :td, :width => 150 do write(content_tag :td, :width => 150 do
options = { :id => filter_class.underscore_name } 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' options.merge! :class => 'dependent-filter-label'
end end
content_tag :label, options do 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. # multiple will be disabled/enabled later by JavaScript anyhow.
# We need to specify multiple here because of an IE6-bug. # We need to specify multiple here because of an IE6-bug.
if filter_class.has_dependent? if filter_class.has_dependent?
dependents = filter_class.all_dependents.map {|d| d.underscore_name}.to_json all_dependents = filter_class.all_dependents.map {|d| d.underscore_name}.to_json
select_options.merge! :"data-dependents" => dependents.gsub!('"', "'") 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 end
# store selected value(s) in data-initially-selected if this filter is a dependent # 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 # of another filter, as we have to restore values manually in the client js

Loading…
Cancel
Save