Skip to content

Commit

Permalink
Added multiple select functionality to new dc.selectMenu
Browse files Browse the repository at this point in the history
  • Loading branch information
madebydna committed Dec 31, 2014
1 parent a73dda5 commit 18b153d
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 33 deletions.
98 changes: 74 additions & 24 deletions spec/select-menu-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ describe('dc.selectMenu', function() {
it('creates select tag', function() {
expect(chart.selectAll("select").length).toEqual(1);
});
it('select tag is not a multiple select by default', function() {
expect(chart.selectAll("select").attr("multiple")).toBeNull();
});
it('can be made into a multiple', function() {
chart.multiple(true).redraw();
expect(chart.selectAll("select").attr("multiple")).toBeTruthy();
});
it('creates prompt option with empty value', function() {
var option = chart.selectAll("option")[0][0];
expect(option).toBeTruthy();
Expand Down Expand Up @@ -76,36 +83,82 @@ describe('dc.selectMenu', function() {
last_option = getOption(chart,last_index);
expect(last_option.text).toEqual("Mississippi: 2");
});
})
});

describe('regular single select', function() {
describe('selecting an option', function () {
it('filters dimension based on selected option\'s value', function(){
chart.onChange(stateGroup.all()[0].key);
expect(chart.filter()).toEqual("California");
});
it('replaces filter on second selection', function(){
chart.onChange(stateGroup.all()[0].key);
chart.onChange(stateGroup.all()[1].key);
expect(chart.filter()).toEqual("Colorado");
expect(chart.filters().length).toEqual(1);
});
it('actually filters dimension', function(){
chart.onChange(stateGroup.all()[0].key);
expect(regionGroup.all()[0].value).toEqual(0);
expect(regionGroup.all()[3].value).toEqual(2);
});
it('removes filter when prompt option is selected', function(){
chart.onChange(null);
expect(chart.hasFilter()).not.toBeTruthy();
expect(regionGroup.all()[0].value).toEqual(1);
});
});

describe('redraw with existing filter', function () {
it('selects option corresponding to active filter', function(){
chart.onChange(stateGroup.all()[0].key);
chart.redraw();
expect(chart.selectAll("select")[0][0].value).toEqual("California");
});
});

afterEach(function () {
chart.onChange(null);
});
});

describe('selecting an option', function () {
it('filters dimension based on selected option\'s value', function(){
chart.onChange(stateGroup.all()[0].key);
expect(chart.filter()).toEqual("California");
describe('multiple select', function () {
beforeEach(function () {
chart.multiple(true);
chart.onChange([stateGroup.all()[0].key, stateGroup.all()[1].key]);
});
it('replaces filter on second selection', function(){
chart.onChange(stateGroup.all()[0].key);
chart.onChange(stateGroup.all()[1].key);
expect(chart.filter()).toEqual("Colorado");
expect(chart.filters().length).toEqual(1);
it('adds filters based on selections', function(){
expect(chart.filters()).toEqual(["California", "Colorado"]);
expect(chart.filters().length).toEqual(2);
});
it('actually filters dimension', function(){
chart.onChange(stateGroup.all()[0].key);
expect(regionGroup.all()[0].value).toEqual(0);
expect(regionGroup.all()[3].value).toEqual(2);
expect(regionGroup.all()[4].value).toEqual(2);
});
it('removes filter when prompt option is selected', function(){
chart.onChange('');
it('removes all filters when prompt option is selected', function(){
chart.onChange(null);
expect(chart.hasFilter()).not.toBeTruthy();
expect(regionGroup.all()[0].value).toEqual(1);
});
});
it('selects all options corresponding to active filters on redraw', function(){
var selectedOptions = chart.selectAll("select").selectAll("option")[0].filter(function(d) {
return d.selected;
});
expect(selectedOptions.length).toEqual(2);
expect(selectedOptions.map(function(d){ return d.value; })).toEqual(["California", "Colorado"]);
});
it('does not deselect previously filtered options when new option is added', function(){
chart.onChange([stateGroup.all()[0].key, stateGroup.all()[1].key, stateGroup.all()[5].key]);

describe('redraw with existing filter', function () {
it('selects option corresponding to active filter', function(){
chart.onChange(stateGroup.all()[0].key);
chart.redraw();
expect(chart.selectAll("select")[0][0].value).toEqual("California");
var selectedOptions = chart.selectAll("select").selectAll("option")[0].filter(function(d) {
return d.selected;
});
expect(selectedOptions.length).toEqual(3);
expect(selectedOptions.map(function(d){ return d.value; })).toEqual(["California", "Colorado", "Ontario"]);
});

afterEach(function () {
chart.onChange(null);
});
});

Expand Down Expand Up @@ -136,7 +189,4 @@ describe('dc.selectMenu', function() {
function getOption(chart, i){
return chart.selectAll("option.dc-select-option")[0][i];
}


});

});
68 changes: 59 additions & 9 deletions src/select-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
## Select Menu
Includes: [Base Mixin](#base-mixin)
The select menu is a simple widget that allows filtering a dimension by selecting an option from an HTML <select/> menu.
The select menu is a simple widget designed to filter a dimension by selecting an option from
an HTML <select/> menu. The menu can be optionally turned into a multiselect.
#### dc.selectMenu(parent[, chartGroup])
Create a select menu instance and attach it to the given parent element.
Expand All @@ -23,7 +24,7 @@ The select menu is a simple widget that allows filtering a dimension by selectin
.dimension(states)
.group(stateGroup);
// the option text can be set via the title function
// the option text can be set via the title() function
// by default the option text is '`key`: `value`'
select.title(function(d){
return 'STATE: ' + d.key;
Expand All @@ -39,6 +40,7 @@ dc.selectMenu = function (parent, chartGroup) {

var _select;
var _promptText = 'Select all';
var _multiple = false;
var _order = function (a, b) {
return _chart.keyAccessor()(a) > _chart.keyAccessor()(b) ?
1 : _chart.keyAccessor()(b) > _chart.keyAccessor()(a) ?
Expand All @@ -55,16 +57,27 @@ dc.selectMenu = function (parent, chartGroup) {

_chart._doRender = function () {
_chart.select('select').remove();
_select = _chart.root().append('select').classed(SELECT_CSS_CLASS, true);
_select = _chart.root().append('select')
.classed(SELECT_CSS_CLASS, true);

switchMultipleSelectOption();

_select.append('option').text(_promptText).attr('value', '');
renderOptions();
return _chart;
};

_chart._doRedraw = function () {
switchMultipleSelectOption();
renderOptions();
// select the option that corresponds to current filter
if (_chart.hasFilter()) {
// select the option(s) corresponding to current filter(s)
if (_chart.hasFilter() && _multiple) {
_select.selectAll('option')
.filter(function (d) {
return d && _chart.filters().indexOf(String(_chart.keyAccessor()(d))) >= 0;
})
.property('selected', true);
} else if (_chart.hasFilter()) {
_select.property('value', _chart.filter());
} else {
_select.property('value', '');
Expand All @@ -89,12 +102,24 @@ dc.selectMenu = function (parent, chartGroup) {
return options;
}

function onChange () {
_chart.onChange(this.value);
function onChange (d , i) {
var selectedOptions = Array.prototype.slice.call(d3.event.target.selectedOptions);
var values = selectedOptions.map(function (d) {
return d.value;
});
// check if only prompt option is selected
if (values.length === 1 && values[0] === '') {
values = null;
} else if (values.length === 1) {
values = values[0];
}
_chart.onChange(values);
}

_chart.onChange = function (val) {
if (val) {
if (val && _multiple) {
_chart.replaceFilter([val]);
} else if (val) {
_chart.replaceFilter(val);
} else {
_chart.filterAll();
Expand All @@ -104,6 +129,14 @@ dc.selectMenu = function (parent, chartGroup) {
});
};

function switchMultipleSelectOption () {
if (_multiple) {
_select.attr('multiple', true);
} else {
_select.attr('multiple', null);
}
}

/**
#### .order([function])
Get or set the function that controls the ordering of option tags in the
Expand Down Expand Up @@ -159,5 +192,22 @@ dc.selectMenu = function (parent, chartGroup) {
return _chart;
};

/**
#### .multiple([bool])
Controls the type of select menu (single select is default). Setting it to true converts the underlying
HTML tag into a multiple select.
```
chart.multiple(true);
```
**/
_chart.multiple = function (_) {
if (!arguments.length) {
return _multiple;
}
_multiple = _;

return _chart;
};

return _chart.anchor(parent, chartGroup);
};
};

0 comments on commit 18b153d

Please sign in to comment.