2

I have a chart directive in my angular application that makes use of the d3.js library.

Part of the directive adds mouse event handlers to some svg elements like so

svg.selectAll('g rect')
    .on('click', function(d) {
        scope.clickHandler(d, scope.data);
    })
    .on('mouseover', function () {
        d3.select(this).classed('data-item-hover', true);
    })
    .on('mouseout', function () {
        d3.select(this).classed('data-item-hover', false);
    });

I want to cover these handlers in some unit tests, but am struggling with how to actually invoke these events dynamically.

I am attempting this in my test spec

describe('scope.clickHandler', function() {
    var rect;
    beforeEach(function(){
        spyOn(scope, 'clickHandler');
        rect = svg.select('g rect')[0][0];
        rect.on('click')();
    });
    it('should call scope.render after a window resize event occurs', function () {
        expect(scope.clickHandler).toHaveBeenCalled();
    });
});

However this throws an error

TypeError: undefined is not a constructor (evaluating 'rect.on('click')'

I came across this SO How to invoke "click" event programmatically in d3? and the second answer is what has led me to trying this technique.

How can I call a mouse event on the svg elements that I have registered events with in d3.js?

Thanks

Community
  • 1
  • 1
mindparse
  • 6,115
  • 27
  • 90
  • 191
  • See http://stackoverflow.com/questions/9063383/how-to-invoke-click-event-programmatically-in-d3 – jarandaf Mar 30 '16 at 16:05
  • That's the exact link I have referenced in the bottom of my question – mindparse Mar 30 '16 at 16:06
  • My bad :) I can only see a subtle difference: `svg.select('g rect')[0][0]` is a DOM element, while d3 binds methods to the selection array itself, hence the error – jarandaf Mar 30 '16 at 16:22

1 Answers1

4

The main issue is that svg.select('g rect')[0][0] is a DOM element and not a proper D3 selection, that's why you get undefined error. From the docs:

Selections are arrays of elements—literally (maybe not literally...). D3 binds additional methods to the array so that you can apply operators to the selected elements...

You can actually check that since a DOM element implements the Element interface:

(svg.select('g rect')[0][0] instanceof Element) // true

So in order to trigger some event on a certain element, you could do the following:

var rect = svg.select('g rect') // first rect element found is assumed
rect.on('click').call(rect.node(), rect.datum()); // as commented in above link

You can find a simple working fiddle here.

jarandaf
  • 4,297
  • 6
  • 38
  • 67