Giter Club home page Giter Club logo

react-faux-dom's Introduction

react-faux-dom npm version CDNJS Build Status js-standard-style Join the chat at https://gitter.im/Olical/react-faux-dom

DOM like data structure to be mutated by D3 et al, then rendered to React elements.

ReactFauxDOM supports a wide range of DOM operations and will fool most libraries but it isn't exhaustive (the full DOM API is ludicrously large). It supports enough to work with D3 but will require you to fork and add to the project if you encounter something that's missing.

You can think of this as a bare bones jsdom that's built to bridge the gap between the declarative React and the imperative JavaScript world. We just need to expand it as we go along since jsdom is a huge project that solves different problems.

I'm trying to keep it light so as not to slow down your render function. I want efficient, declarative and stateless code, but I don't want to throw away previous tools to get there.

Example

Default

This style of calling createElement and toReact is the default API, it's easy to use and fits into the normal React flow but won't allow you to perform animations or D3 force layouts, for example.

class SomeChart extends React.Component {
  render () {
    // Create your element.
    var el = ReactFauxDOM.createElement('div')

    // Change stuff using actual DOM functions.
    // Even perform CSS selections!
    el.style.setProperty('color', 'red')
    el.setAttribute('class', 'box')

    // Render it to React elements.
    return el.toReact()
  }
}

// Yields: <div style='color: red;' class='box'></div>

With animation helper

Complex usage with D3, ES6 modules and animations. Clone it from here, or try on in codesandbox.

import React from 'react'
import * as d3 from 'd3'
import {withFauxDOM} from 'react-faux-dom'

class MyReactComponent extends React.Component {
  componentDidMount () {
    const faux = this.props.connectFauxDOM('div', 'chart')
    d3.select(faux)
      .append('div')
      .html('Hello World!')
    this.props.animateFauxDOM(800)
  }

  render () {
    return (
      <div>
        <h2>Here is some fancy data:</h2>
        <div className='renderedD3'>
          {this.props.chart}
        </div>
      </div>
    )
  }
}

MyReactComponent.defaultProps = {
  chart: 'loading'
}

export default withFauxDOM(MyReactComponent)

Limitations

This library is intended to be used to build a React DOM tree from some mutable intermediate value which is then thrown away inside a render function. This means things that require mutation of the DOM, such as D3's animations, zooming, dragging and brushing will not work.

Static charts will work fine out of the box, you can use this tool to translate SVG tools into DOM managed by React easily. If you wish to start adding in animations you'll have to use the withFauxDOM higher order component mentioned above and in a few of the examples.

Before you go to use this tool, stop and think:

  • Am I trying to build static DOM within a render method? - This is fine.
  • Am I trying to hook up timers and event listeners with a 3rd party tool to manipulate some DOM after I have inserted it into the page? - This is not going to work.

Installation

You can install the package react-faux-dom from npm as you usually would. Then use webpack or browserify (etc) to bundle the source into your build. If you need a pre-built UMD version you can use unpkg.

You can find the latest version of the UMD version at https://unpkg.com/react-faux-dom/dist/ReactFauxDOM.min.js

Independent documents

By default all Elements share an emulated window at el.ownerDocument.defaultView you can create independent documents with:

import React from 'react'
import rfdFactory from 'react-faux-dom/lib/factory'

function getParagraph() {
  const ReactFauxDOM = rfdFactory();
  return new ReactFauxDOM.Element('p', someDiv);
}

const p1 = getParagraph();
const p2 = getParagraph();

assert(p1.ownerDocument !== p2.ownerDocument);

More usage info:

Related articles:

Development

# Fetch the dependencies
make bootstrap

# Test
make test

# Test continually
make test-watch

Maintainers

This project is actively maintained by the following developers. Feel free to reach out if you'd like to join us and help out!

Unlicenced

Find the full unlicense in the UNLICENSE file, but here's a snippet.

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

Do what you want. Learn as much as you can. Unlicense more software.

react-faux-dom's People

Contributors

alexyuly avatar brettstatman avatar futuraprime avatar georgestrakhov avatar gitter-badger avatar graingert avatar kitten avatar krawaller avatar mrfratello avatar nickbalestra avatar npmcdn-to-unpkg-bot avatar olical avatar pemrouz avatar sebinsua avatar sergi avatar tibotiber avatar wildmolasses avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-faux-dom's Issues

Support for JSX?

I'm a bit surprised that the faux DOM createElement method doesn't support the same method signature as regular React.createElement:

ReactElement.createElement = function (type, config, children) {

ReactFauxDOM:

  createElement: function (nodeName) {

As a result, it appears it isn't possible to actually swap ReactFauxDOM in for React, and use JSX:

  render () {
    var _React = React;
    React = ReactFauxDOM;

    var result = (
      <div className="ViewsHistogram box">
        Hello
      </div>
    );

    React = _React;
    return result.toReact();
  }

(The className and Hello text are ignored by ReactFauxDOM).

Is there a reason for this? Why not use the same method signature, and get JSX support for free?

'toReact' should not set default React 'key'

Currently, toReact will set faux-dom-0 as the default key on the react element, when no index was provided, leading to children with same key issues - at least when creating multiple elements without index and passing them as an array. There are situations where it is perfectly fine not to set a key, as React will simply use the array index or similar.

I thus propose to either:

  • not set the key at all attribute if index was not provided
  • or to set a uuid as the key to prevent collisions (again if index was not provided)

What do you think?

Great library btw. Thanks a lot.

d3.mouse always returns [NaN, NaN]

Version: latest react-faux-dom (2.7.1) and React 15.1.0.

When I do:

    const node = ReactFauxDOM.createElement('svg');

    const svg = d3.select(node)
        .attr('width', width + margin.left + margin.right)
        .attr('height', h + margin.top + margin.bottom)
      .append('g')
        .attr('transform', `translate(${margin.left}, ${margin.top})`);
    ...

    svg.append('rect')
      .attr('width', width)
      .attr('height', h)
      .style('fill', 'none')
      .style('pointer-events', 'all')
      .on('mousemove', function() {
        const pos = d3.mouse(this);
        console.log(pos);
      });

pos is always [NaN, NaN].

I can confirm that this inside the mousemove callback is a DOM element (e.g. Element {nodeName: "rect", parentNode: Element, childNodes: Array[0], eventListeners: Object, text: ""…}).

Any ideas why?

app.js:7851Uncaught TypeError: Cannot read property 'select' of undefined

import './style.less';

import React from 'react';

import d3 from 'd3';
import ReactFauxDOM from 'react-faux-dom';

const Component = React.createClass({
render: function () {
const svg = ReactFauxDOM.createElement('svg');

d3.select(svg)
    .attr({
      width: 300,
      height: 300
    })
    .append('rect')
    .attr({
      width: 300,
      height: 300,
      fill: 'green'
    });

return svg.toReact()

}
});

export default Component;

Not able to set Children

Not able to set Children

According to the documentation, there are several ways to set children to a ReactFauxDOM.Element, also, children can be either a ReactFauxDOM.Element or a React.Component. Yet, this isn't working for me.

Problem

What am I trying to accomplish?

I'm using D3 (v4) to visualise a force layout of nodes, next to D3, I'm also using react-bootstrap as they have some handy components for Tooltips. For such a tooltip to work, you need to specify the react-bootstrap component as the parent of the component you'd like a tooltip over. As such, I need a way to insert children in the ReactFauxDOM structure, in order to set their children.

Here's the code I have so far, that I know works:

const faux_svg = new ReactFauxDOM.Element('svg')
faux_svg.appendChild(new ReactFauxDOM.Element('g'))

This generates something like this:

<svg>
  <g></g>
</svg>

Now, in stead of a <g></g> element, I need this react-bootstrap component:

const faux_svg = new ReactFauxDOM.Element('svg')
faux_svg.appendChild(React.createElement('OverlayTrigger'))

This doesn't work, no errors are thrown, it just doesn't work.
Some alternatives I tried:

faux_svg.appendChild(React.createElement('g'))

faux_svg.appendChild(<OverlayTrigger />)

const ot = <OverlayTrigger />
faux_svg.appendChild(ot)

const ot = new ReactFauxDOM.Element('g', faux_svg) // even calling children this way, doesn't work

I'm not sure what I'm doing wrong here. Hope someone can point me in the right direction.

PS: Love the library!

.enter().append('circle') causes error "Cannot read property 'createElementNS' of undefined"

My code is below. It is possible I'm not doing something correctly. I'm new to everything here, except React.

Note: the error only occurs after the array has 2 or more elements. Also when the array "products" has only one item, no circles show up. Oh and the format of the elements are {id, product}.
ie: [{id: 0, product: 24}], {id:1, product: 36}]

import React from 'react';
import { connect } from 'react-redux';
import ReactFauxDom from 'react-faux-dom'; 
import d3 from 'd3';

let Visualization = ({products}) => {
    let margin = {top: 20, right: 20, bottom: 30, left: 50}
    let width = 960 - margin.left - margin.right
    let height = 500 - margin.top - margin.bottom

    let max = products.reduce((carry, product) => Math.max(product.product, carry), 0);

    let x = d3.scale.linear().range([0, width]);
    let y = d3.scale.linear().range([0, height]);

    let xAxis = d3.svg.axis().scale(x).orient('bottom');
    let yAxis = d3.svg.axis().scale(y).orient('left');

    let node = ReactFauxDom.createElement('svg');
    let svg = d3.select(node)
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ', ' + margin.top+ ')');

    x.domain(d3.extent(products, (d) => d.products));
    y.domain(d3.extent(products, (d) => d.products));

    svg.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0,' + height + ')')
        .call(xAxis)

    svg.append('g')
        .attr('class', 'y axis')
        .call(yAxis)

    console.log('products: ', products);
    svg.data(products).enter()
        .append('circle') //******HERE********//
        .attr('cx', (product) => +product.product)
        .attr('cy', (product) => +product.product)
        .attr('r', 20);

    return node.toReact();
}

Visualization = connect(({products}) => ({products}))(Visualization);

export default Visualization;

Backbone View toReact?

Hi Oliver,

Most recently I have been in a situation where I need to "convert" the BackboneView to React views. I am wondering if you have any experience of using this project's idea to convert the BackboneView.

Initially, I have to make a "BackboneView | React View|BackboneView | React View" type of component in order to reuse the existing well-maintained in-house BackboneView components, but gradually I found such sandwich format would run into some issue, for example, when we tried to insert the BackboneView component during the componentDidMount and after the state changed the Backbone component would disappear. Arguably we could re-insert the Backbone View component in a hacky way but not ideal. And rewriting the whole Backbone Component codebase is way more time-consuming on our side.

Thanks in advance,

Hao

toReact mutates the faux element

Hi,

when calling 'toReact()' it mutates the faux element (at least it seems to delete some functions on the style property) and therefore the faux element can not be reused between renderings and has to be recreated on every render.

Does this not stop us from using the enter-update-exit pattern in d3 since we are always starting with a new faux element?

cheers,
mg

Support for Brush

I'm trying to implement a Brush in a chart, but I get an undefined is not a valid argument for 'in' (evaluating 'name in object') error from the ReactEventListener.
I believe the cause is the brush event from d3.
Could this be implemented in the react-faux-dom?

state, render and d3

Hey !

I had just a 'under the hood' question, imagine I have a state { width: 100 } that I apply to my d3 svg container to define its width. When the state width is updated, is the full svg redrawed or is just the attribute width updated ?

Images do not show up

I'm trying to recreate a simple JSFiddle: http://jsfiddle.net/am8ZB/
Below is my component, I used the same code in render, but with FauxDOM for creating the svg.
No luck with using png files either.

import React from 'react'
import * as d3 from 'd3'
import ReactFauxDOM from 'react-faux-dom'

class D3SLDiagram extends React.Component {

  constructor(props) {
    super(props)
    this.state = {}
  }

  render () {
    var width = 800, height = 800;

    var svg = d3.select(ReactFauxDOM.createElement('svg'))
        .attr("width", width)
        .attr("height", height);

    var g = svg.append("g");

    var img = g.append("svg:image")
        .attr("xlink:href", "http://www.clker.com/cliparts/1/4/5/a/1331068897296558865Sitting%20Racoon.svg")
        .attr("width", 200)
        .attr("height", 200)
        .attr("x", 228)
        .attr("y",53);

    return (
      <div className='network D3SLDiagram'>
        {svg.node().toReact()}
      </div>
    )
  }
}

export default D3SLDiagram

Result:
ss

I'm totally stuck, any help would be appreciated, thanks!

ES6 Classes

Is there a way to use the mixins in an ES6 class if not is there an update coming that works with ES6 classes.

How to approach animations and compositions with react-faux-dom?

I'm working on a couple components using react-faux-dom. It literally just works out the box thank you for this. As it stands I have a line chart, bar chart, area chart, scatter-plot etc. However all of these each come with their own x/y-axis, grid, legend. I don't want to duplicate the code, as it makes more sense to abstract these out of the different charts into their own components. I'm not exactly sure how to go about composing the x/y-axis, legend, and the chart chosen together.

Some code may make more sense:

   // What I have in my head
  <XYAxis grid={true}>
    <LineChart {...}/>
    <Legend  {...} />
  </XYAxis>

  <XYAxis grid={false}>
    <BarChart {...}/>
    <Legend  {...} />
  </XYAxis>

I'm not sure exactly how to go about this in react-dom-faux, if you've done something like this with your library I'd like to know what approach might have worked best for you.

I'd also like to add Animations using React Motion, for things like updates in the data set, filtering data, tool-tips, etc. What would you suggest be the best approach for adding animations with 'react-faux-dom'?

I wouldn't mind adding some more example docs for your library as It has really made my life easier. If theres anything specific you want to add but haven't had the time let me know 👍🏽

D3 Animations

Hey,

Great work on the library - it worked first time for me, hardly any code required to get my D3 chart rendering in react!

The only issue I have is that animation doesn't work. I know you mentioned on your blog that this is something you might work on in the future - any update on this?

Cheers,

Prash

Returning Refs to react

Consider;

/*.. imports ..*/ 

class myClass extends React.Component {

  render( ) {
     this.container = ReactFauxDOM.createElement( 'svg' );
     return { this.rendition.toReact() }
  }

  rendition(){ 
   var myRect = d3.select( this.container ).append( 'rect' );
   ReactFauxDOM.toRef( myRect, 'rect' ); 
  }

  componentDidMount() {
    console.log( this.refs.rect )
  }

}

is it logical/sound from your perspective to provide a means such as the above so as to gain ReactJS refs from the faux dom when toReact is called?

getBoundingClientRect returns undefined

Use the example code (animate-d3-with-mixin), put a break point on line 88.
In the debugger execute
rect[0][0].getBoundingClientRect()
returns undefined.
Stepping into Element.prototype.getBoundingClientRect
I see in the first line that the Element doesn't have a component

if (!this.component) {  
  return undefined  
  } 

not sure what that means(no component) or if I'm using the new api correctly....

Allow React components to be embedded into the faux DOM tree

This will allow you to embed Spring animations as mentioned in #2 for example. Although spring requires you to set a child to a function I think, so that should be taken into account.

Basically let the user pass things other than strings to createElement that will be rendered when the tree is converted to React elements. Instead of giving a name like div, you give a function to execute.

Improve docs: ES6 usage example

First, thanks for great work!

Second, I followed the example and struggled for a few minutes to get this to work in ES6 before I realising that I needed to do import * as d3 from 'd3' instead of `import d3 from 'd3', as per this thread.

@Olical, do you have any plans to update the docs to include some ES6 usage example, or should I do a simple PR with an ES6 example?

Factor out non-React faux DOM into separate project?

This a lightweight alternative to jsdom for rendering d3 but assumes/requires React.

Perhaps the non-React bits could be factored out into a separate project (faux-dom? d3-faux-dom?) and then build react-faux-dom on that.

I'm not intimately familiar with the code, but it seems like this lib could import d3-faux-dom and then decorate Element.prototype with toReact, add the mixins, etc.

Binds To Global

Problem here?

I think this might need to be bound, i.e. change line to

setTimeout(this.drawFauxDOM.bind(this))


Thanks for the lib.

D3 v4.0.0

So D3 v4.0.0 is out now which includes a number of changes, including ES6 modules so you don't have to pull in everything anymore.

I'm wondering if they changed the use of the DOM significantly though, which would not work with ReactFauxDOM without modification. I haven't tried it yet and maybe it'll Just WorkTM.

Let me know here if you encounter blocking issues. Then if anyone wants to help shim those new methods that'd be amazing. 😄

integrating support for animations

The createHook animation helper

In this blog post I detail a solution on how to make D3 animations work with react-faux-dom, using a small helper function createHook. You call it with a reference to the component, the faux element you intend to feed to D3, and the state prop name you want the resulting virtual DOM to end up in:

let hook = createHook(this,fauxnode,"chart");

The returned hook is then attached to all D3 animations like this:

rect.transition()
    .duration(500)
    .attr("y", function(d) { return y(d.y0 + d.y); })
    .call(hook);

This will ensure that the following line of code runs while the animation is playing:

component.setState({[statename]:fauxelement.toReact()});

...allowing your render function to simply output this.state.chart (or whatever statename you chose).

An integration proposal

This helper could be added to react-faux-dom itself, however it kind of itches to add something D3-specific to an otherwise library-agnostic library, intended to work with any DOM-manipulating lib.

Here's my current thinking - the only reason right now to attach stuff to the D3 anims is to be able to stop updating state when the animation is done playing. However, I haven't managed to make the cleanup fool-proof; if you spam transitions, sometimes the relevant listeners won't fire, and my little helper lib will update state indefinitely.

So now I'm thinking we could make a general version that works like this:

You connect a React component to a faux element using the same signature as createHook:

ReactFauxDOM.connect(this,faux,"chart");

This will give you an updateFauxDOM method on the component which you can call whenever you like (and probably the connect call should schedule an automatic first call much like createHook works).

// after doing something to the faux node:
this.updateFauxDOM();

And now for the killer feature - the update method takes an optional millisecond argument, which will update the DOM for that amount of time!

rect.transition()
    .duration(500)
    .delay(function(d, i) { return i * 10; })
    .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })

this.updateFauxDOM(1000); // allowing for duration + max delay, with a bit of a margin

So no more brittle cleanup, and no more D3-specific stuff - we simply let the developer tell us how long to animate for. Super easy to implement in a safe way. This could be coupled with an option to animate forever and a .stopAnimating method, and why not also the isAnimating method that createHook added.

Potentially we could make this into a mixin which adds some automagic cleanup in a componentWillUnmount call.

What do you think, @Olical?

slider using react-faux-dom.

Hi,
I am trying to add slider using react-faux-dom.
As I am absolute beginner with react, I am not sure what to do in handleDrag() function.
Any suggestions on updating values after a slider is dragged?
Thank you

import d3 from 'd3'
import React from 'react'
import ReactFauxDOM from 'react-faux-dom'

class Slider extends React.Component {
  componentDidMount() {
    var drag = d3.behavior.drag()
      .on("drag", this.handleDrag)
    var dom = React.findDOMNode(this);
    d3.select(dom).call(drag)
  }

  handleDrag() {
    var posY = d3.mouse(React.findDOMNode(this)),
      newValue = Math.max(0, Math.min(1,posY/400))
    //this.onChange(newValue);
    //not sure what to do here
    //need advice
  }

  render() {
    const {width, height, value} = this.props
    const el = d3.select(ReactFauxDOM.createElement('svg'))
      .attr('width',width)
      .attr('height',height)

    el.append("rect")
      .attr('width',width*value)
      .attr('height',height)
      .attr('class',"Slider-fill")

    el.append("rect")
      .attr('width',width)
      .attr('height',height)
      .attr('class',"Slider-border")

    el.append("text")
      .attr('x',width/2)
      .attr('y',height/2)
      .text(value)

    return el.node().toReact();
  }
}

export default Slider

support Element.childNodes, Element.hasAttribute(name)

d3fc.layout iterate DOM by childNodes. Thanks.

// creates the structure required by the layout engine
function createNodes(el) {
    function getChildNodes() {
        var children = [];
        for (var i = 0; i < el.childNodes.length; i++) {
            var child = el.childNodes[i];
            if (child.nodeType === 1) {
                if (child.getAttribute('layout-style')) {
                    children.push(createNodes(child));
                }
            }
        }
        return children;
    }
    return {
        style: parseStyle(el.getAttribute('layout-style')),
        children: getChildNodes(el),
        element: el
    };
}

error with nvd3

I'm trying to use with nvd3, and I get an error "TypeError: Cannot read property 'replace' of undefined(…)" on the line var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""), 10);

I can provide code if needed

npm ERR! peerinvalid Peer [email protected] wants [email protected]

package.json#L44

  "peerDependencies": {
    "react": "0.x"
  }

This doesn't match with 0.14.0-rc1 since npm doesn't versions containing [a-Z] characters as releases. (as long as I can remember)

npm ERR! node v0.12.2
npm ERR! npm  v2.7.4
npm ERR! code EPEERINVALID

npm ERR! peerinvalid The package react does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer [email protected] wants react@^0.14.0-rc1
npm ERR! peerinvalid Peer [email protected] wants react@>=0.13.3 || ^0.14.0-beta3 || ^0.14.0-rc1
npm ERR! peerinvalid Peer [email protected] wants react@>=0.13.2 || ^0.14.0-rc1
npm ERR! peerinvalid Peer [email protected] wants [email protected]

append does not chain correctly

Basic D3 example (not using react-faux-dom)

    // #test is: <svg id="test"></svg>
    const svg = d3.select("#test")
      .attr({width, height})
      .append("circle")
      .attr("r", 4.5)
      .attr("cx", 60)
      .attr("cy", 60)
      .style("fill", "steelblue");

This works of course. This however, does not:

    const svg = d3.select(ReactFauxDOM.createElement('svg'))
      .attr({width, height})
      .append("circle")
      .attr("r", 4.5)
      .attr("cx", 60)
      .attr("cy", 60)
      .style("fill", "steelblue");
    return svg.node().toReact();

I think the reason for this is that append does not correctly chain. What should happen is that the append acts like a selector and the following attr operates on the circle, but that is not what happens, they operate on the svg.

This works fine:

    const svg = d3.select(ReactFauxDOM.createElement('svg'))
      .attr({width, height});
    svg.append("circle")
      .attr("r", 4.5)
      .attr("cx", 60)
      .attr("cy", 60)
      .style("fill", "steelblue");
    return svg.node().toReact();

I am not experienced with D3, but it would seem to me that usage of react-faux-dom is the only difference between the examples so I guess the problem lies there.

Cheers,

Douglas

Fake DOM element has no getBoundingClientRect method

To reproduce, select a react-faux-dom div with D3, append an svg, and then call a behavior, e.g.:

import d3 from 'd3';
import ReactDOM from 'react-dom';
import { createElement } from 'react-faux-dom';

const target = createElement('div');
d3.select(target).append('svg').call(d3.behavior.drag());
ReactDOM.render(target.toReact(), document.getElementById('render-target'));

The browser throws an error when you click/drag the svg element:
d3.js: Uncaught TypeError: x.getBoundingClientRect is not a function

Similar code using a real DOM element throws no error:

const target = document.createElement('div');
d3.select(target).append('svg').call(d3.behavior.drag());
document.getElementById('render-target').appendChild(target);

The desired behavior is that the fake element supports getBoundingClientRect, like a real DOM element.

app.js:7851Uncaught TypeError: Cannot read property 'select' of undefined

import './style.less';

import React from 'react';

import d3 from 'd3';
import ReactFauxDOM from 'react-faux-dom';

const Component = React.createClass({
  render() {
    var list = ReactFauxDOM.createElement('ul');

    d3.select(list)
        .selectAll('li')
        .data([1,2,3])
        .enter()
        .append('li')
        .text((d) => d);

    return list.toReact();
  }
});

export default Component;

How to integrate with React-Motion for animation?

Dear @Olical
Glad to see this progress of the integration between D3 and React. I ever intended to use dc.js and react.js for analyzing data of stocks. When I want to show changes of data, animation does matter. In your approach, how to show animation? I suggest React-Motion which is a promising package.

https://github.com/dc-js/dc.js
https://github.com/chenglou/react-motion

how to generate jsx in D3 manner? Maybe something like:

// Create your element.
var el = ReactFauxDOM.createElement('div')

// Change stuff using actual DOM functions.
// Even perform CSS selections.
el.style.setProperty('color', 'red')
el.setAttribute('class', 'box')
el.Spring.setDefaultValue({val: 0})
el.Spring.setEndValue({val: this.state.open?  400 : 0})

// Render it to React elements.
return el.toReact()

// Yields: 
<Spring defaultValue={{val: 0}} endValue={{val: this.state.open ? 400 : 0}}>
    {interpolated =>
        <div className='box' style={{
              transform: `translate3d(${interpolated.val}px, 0, 0)`,
              color: 'red'
            }} ></div>
    }
</Spring>

Trying to make a sankey using react-faux-dom

import d3 from 'd3'
import React from 'react'
import ReactFauxDOM from 'react-faux-dom'
import * as sankey from './util/sankey'

class Sankey extends React.Component {
  static propTypes = {
    width: React.PropTypes.number,
    height: React.PropTypes.number,
    title: React.PropTypes.string
    //data: React.PropTypes.array.isRequired,
  }

  render() {
    var units = "Widgets";

    var margin = {top: 10, right: 10, bottom: 10, left: 10},
        width = 1000 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;

    var formatNumber = d3.format(",.0f"),    // zero decimal places
        format = function (d) {
          return formatNumber(d) + " " + units;
        },
        color = d3.scale.category20();

// append the svg canvas to the page
    var svg = ReactFauxDOM.createElement('svg');
    var someDiv = d3.select(svg)
        //        var svg = d3.select("#content").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform",
            "translate(" + margin.left + "," + margin.top + ")");

// Set the sankey diagram properties
    var sankey = d3.sankey()
        .nodeWidth(36)
        .nodePadding(40)
        .size([width, height]);

    var path = sankey.link();

// load the data
    d3.json("sankey-formatted.json", function (error, graph) {
      var nodeMap = {};
      graph.nodes.forEach(function (x) {
        nodeMap[x.name] = x;
      });
      graph.links = graph.links.map(function (x) {
        return {
          source: nodeMap[x.source],
          target: nodeMap[x.target],
          value: x.value
        };
      });

      sankey
          .nodes(graph.nodes)
          .links(graph.links)
          .layout(32);

// add in the links
      var link = someDiv.append("g").selectAll(".link")
          .data(graph.links)
          .enter().append("path")
          .attr("class", "link")
          .attr("d", path)
          .style("stroke-width", function (d) {
            return Math.max(1, d.dy);
          })
          .sort(function (a, b) {
            return b.dy - a.dy;
          });

// add the link titles
      link.append("title")
          .text(function (d) {
            return d.source.name + " → " +
                d.target.name + "\n" + format(d.value);
          });

// add in the nodes
      var node = someDiv.append("g").selectAll(".node")
          .data(graph.nodes)
          .enter().append("g")
          .attr("class", "node")
          .attr("transform", function (d) {
            return "translate(" + d.x + "," + d.y + ")";
          })
          .call(d3.behavior.drag()
              .origin(function (d) {
                return d;
              })
              .on("dragstart", function () {
                this.parentNode.appendChild(this);
              })
              .on("drag", dragmove));

// add the rectangles for the nodes
      node.append("rect")
          .attr("height", function (d) {
            return d.dy;
          })
          .attr("width", sankey.nodeWidth())
          .style("fill", function (d) {
            return d.color = color(d.name.replace(/ .*/, ""));
          })
          .style("stroke", function (d) {
            return d3.rgb(d.color).darker(2);
          })
          .append("title")
          .text(function (d) {
            return d.name + "\n" + format(d.value);
          });

// add in the title for the nodes
      node.append("text")
          .attr("x", -6)
          .attr("y", function (d) {
            return d.dy / 2;
          })
          .attr("dy", ".35em")
          .attr("text-anchor", "end")
          .attr("transform", null)
          .text(function (d) {
            return d.name;
          })
          .filter(function (d) {
            return d.x < width / 2;
          })
          .attr("x", 6 + sankey.nodeWidth())
          .attr("text-anchor", "start");

// the function for moving the nodes
      function dragmove(d) {
        d3.select(this).attr("transform",
            "translate(" + (
                d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
            )
            + "," + (
                d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
            ) + ")");
        sankey.relayout();
        link.attr("d", path);
      }
    });

    return (
        <div>{someDiv.node().toReact()}</div>
    );

  }
}


export default Sankey

There is no output in the browser. Any ideas as in what I am missing in here?

Implementation of mouseover function

I am trying to implement a tooltip on a line chart that will typically have around 2700 data points. I set it up by starting with the solution from #29. The code below has the state change where instead of a color change, I am updating the opacity from 0 to 1. Once there are lots of data points, it becomes horribly slow. Is there a better way to implement mouseover behavior so that there will not be a huge lag as the mouse moves across the chart?

svg.selectAll('.bar')
      .data(data)
      .enter().append('rect')
      .attr('class', 'annotation-bar')
      .attr('x', function(d) { return x(d.Time); })
      .attr('y', margin.top)
      .attr('height', height - margin.top)
      .attr('width', '10px')
      .attr('opacity', 0)
      .on('mouseover', function(d) {
          that.setState({
            xLabel: x(d.Time) + margin.left,
            yLabel: y(d.HeartRate) + margin.top,
            labelHR: d.HeartRate,
            labelTime: d.Time,
            opacity: '1.0'
          });
      })
      .on('mouseout', function(d) {
        that.setState({
          xLabel: 0,
          yLabel: 0,
          labelHR: '',
          labelTime: '',
          opacity: '0.0'
        });
      }
    );

Typescript and mixins

Hi,

I'm trying to play with D3 and react so I assume this is the place to be.

I have a typescripted project and I don't know how to make everything work.

I was able to make this example (https://github.com/Olical/lab/blob/gh-pages/js/react-faux-dom-state/main.js) work with some minor tweaks (ie: I extracted the variable ReactFauxDOM.createElement('svg') and called toReact() on it instead of svg.node().toReact()

But when I tried another simple animated example (http://bl.ocks.org/jose187/4733747) I ended with a non animated graph really ugly. So I started playing with componentDidMount but I wasn't able to add the svg to the already existing dom.

The source code of my class is here (https://github.com/MichaelBitard/react-blank-project/blob/try_animate/src/components/Topology/Topology.tsx), is there something obvious that I did wrong?

SVG not appearing?

I'm currently writing a a little lib for my job using D3 and React. So I decided to utilize your library to try to get it done efficiently between React and D3 Also, thanks for this library it is great!

Im trying to write a reusable BarGraph component. While I was working on this step by step I saw the bars working throughout. However as soon as I started adding axis to the component it no longer rendered the bars that model data. It's odd because I inspect the console and the rect are rendered to the DOM however there is nothing displayed aside from the axis data. I've been trying to get this working for 2 days now and am at a loss.

BarGraphComponent

class BarGraph extends React.Component {
  render () {
    const {data, width, height} = this.props;
    const innerW = width - 70,
          innerH = height - 50;

    var x = d3.scale.ordinal()
        .domain(data.map((d) => d.name))
        .rangeRoundBands([0, innerW], .1);

    var y = d3.scale.linear()
        .domain([0, d3.max(data, (d) => d.value)])
        .range([innerH, 0]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .orient('bottom');

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient('left');

    var chart = d3.select(ReactFauxDOM.createElement('svg'))
      .attr('width', width)
      .attr('height', height)
    .append('g')
      .attr('transform', `translate(${40}, ${20})`);

    chart.append('g')
      .attr('class', 'axis x')
      .attr('transform', `translate(0, ${innerH})`)
      .call(xAxis)

    chart.append('g')
      .attr('class', 'axis y')
      .call(yAxis);

    chart.selectAll('.bar')
      .data(data)
    .enter().append('rect')
      .attr('class', 'bar')
      .attr('x', (d) => x(d.name))
      .attr('y', (d) => y(d.value))
      .attr('height', (d) => innerH - y(d.value))
      .attr('width', x.rangeBand());

    return chart.node().toReact();
  }
}

BarGraph.proptypes = {
  width: React.PropTypes.number,
  height: React.PropTypes.number,
  data: React.PropTypes.array,
}

The code above only renders this:
screen shot 2016-06-14 at 6 34 32 pm

Elements rendered in the console
screen shot 2016-06-14 at 6 35 42 pm

It is based off of this tutorial from the creator of D3:
https://bost.ocks.org/mike/bar/3/

If the problem is obvious or you can tell what I'm doing wrong please let me know I've been having a hard time debugging this mostly because I'm a bit newer to d3.

Support nesting of components before toReact()

Right now if you need to create a line chart with an an X axis and y Axis, a line and a tooltip.
You end up writing these all into one giant render function all writing these elements to the faux-dom using the d3 library. Every re-render will re-render the whole thing, specially when all you want is move the tooltip around.

Now lets say you want to componentize these.
On the top level I want to create an svg with d3. Then add three sub-components xAxis, yAxis, Line and a Tooltip. I want the tooltip to render independently of the others because usually I want the re-render that on a mouse move.

Proposal
Add an addChild(ReactComponent) support on the faux-dom element.
Have the method be able to specify a custom key so that we can make rendering on one subtree independent of the other.

Thoughts?

a problem with on("click",function)

Dear @Olical
I am using react-faux-dom now to reproduce a bar chart.

chart.selectAll(".bar")
      .data(data)
      .enter()
        .append("rect")
        .attr("class", "bar")
        .attr("x", function(d) { return x(d.tradeDate.slice(5)); })
        .attr("width", x.rangeBand())
        .attr("y", function(d) { return y(d.turnoverVol); })
        .attr("height", function(d) { return height - y(d.turnoverVol); })
        .on("click", function(d, i) {console.log(d);console.log(i)});

when I click, "d" shows undefined, "i" shows correctly. I have tried console.log(d) in other attr, they all work well.
Am I wrong with something? How to use new event system? Thank you very much!

http://bl.ocks.org/mbostock/3885304

Documentation

I need to document the full extent of the API with examples. More importantly, I need to document adding features that are missing.

Add `install()` function to allow globals to be modified?

Hi @Olical, I was thinking about raising a pull-request for D3 to prevent it using the window or document globals, and instead using code like node.ownerDocument and node.ownerDocument.defaultView, but this just seems wrong since it's perfectly valid for D3 to do that, and it shouldn't have to start using awkward syntax for the sake of 'react-faux-dom'.

Instead, it seems like the correct thing to do would be to add an install() method to 'react-faux-dom' that updated these global objects so that they behave correctly. Apps could then invoke this method before using D3, or any libraries that use D3.

What do you think? Does this make sense, and if so should I try to create a pull-request for this?

Support style strings

Right now, as per normal React, you have to set the style with an object. I've created the style.{set,get,remove}Property methods, but it would be cool to parse style strings into objects if you set element.style = 'foo: bar;'.

Easily done with getters and setters. Because style is always a string in the real DOM, setting and getting it should encode and decode the string to and from an object.

changing fill color on 'mouseover' and 'mouseout'?

Hi there, thanks for creating this great package. I was hoping you could point out what I could be doing wrong?

My goal is to change the fill color of the bars on 'mouseover' and back to normal on 'mouseout' events. I am following this simple example http://bost.ocks.org/mike/bar/2/#automatic. The only changes I've made is adding the event listeners and creating an element with the ReactFauxDOM.

The chart is rendering and the events are being triggered, as it is logging to the console, but the color is not changing. I feel like there is probably a mistake in my implementation though I'm not sure? Is there something I'm missing?

Any advice would be greatly appreciated. Thanks and keep up the great work!

import React from 'react';
import ReactFauxDOM from 'react-faux-dom';
import d3 from 'd3';

class TestChart extends React.Component {
    render() {
        var data = [4, 8, 15, 16, 23, 42];

        var width = 420;
        var barHeight = 20;

        var x = d3.scale.linear()
            .domain([0, d3.max(data)])
            .range([0, width]);

        var chart = d3.select(ReactFauxDOM.createElement('svg'))
            .attr('width', width)
            .attr('height', barHeight * data.length);

        var bar = chart.selectAll('g')
            .data(data)
          .enter().append('g')
            .attr('transform', function(d, i) { return 'translate(0,' + i * barHeight + ')'; });

        bar.append('rect')
            .attr('width', x)
            .attr('height', barHeight - 1)
            .on('mouseover', function(d) {
                d3.select(this).style('fill', 'green');
                console.log('over');
            })
            .on('mouseout', function(d) {
                d3.select(this).style('fill', 'steelblue');
                console.log('out');
            });

        bar.append('text')
            .attr('x', function(d) { return x(d) - 3; })
            .attr('y', barHeight / 2)
            .attr('dy', '.35em')
            .text(function(d) { return d; });

        return chart.node().toReact();
    }
};

export default TestChart;

Create a contributors list

Not sure if this should be in the readme or another file. But a small script that generates a list of contributors would be cool upon release. Contributor list in each version in CHANGES.md? Sort by amount of commits or last to commit? Or maybe by size of change or amount of branches merged?

I've seen it done on qutebrowser where it was sorted by commits, but I commit a lot, so it may not be representative. Maybe I'll count PRs merged? :)

Thoughts? I feel like it'll be nice to credit people in some way. Contributions and discussions about this project make me happy, so I'd like to encourage it.

Make events work like the normal DOM

This involves the following changes which should help with things like #4.

  • Calling addEventListener multiple times should allow you to add multiple listeners.
  • Events such as mouse events should pass the native event through, not the synthetic React event.

On listeners...

Right now I have a dumb approach which removes the previous listener and replaces it because I'm just setting props.onClick, for example. This needs to basically delegate to some event system that allows you to add or remove multiple listeners. An abstraction on top of Reacts event system to make it behave like the DOM.

On event objects...

This should allow things like d3.mouse to work, right now it completely fails, you have to hit d3.event and work it out yourself.

weird string in style attribute

I just use the example from README.md.

foax-dom

var Graph = React.createClass({
    propTypes: {
        data: React.PropTypes.arrayOf(React.PropTypes.number)
    },
    render: function () {
        var chart = d3.select(ReactFauxDOM.createElement('svg'));
        chart
            .selectAll('g')
            .data(this.props.data)
            .enter().append('g')
            .classed('bar', true)
            .attr('width', function (d, i) {
                return d * 10
            })
            .text((d) => d);
        return chart.node().toReact();
    }
});

querySelectorAll is not a function

I get an error when I run this within a React component:

d3.select(‘.container’)
  .selectAll(‘div.dataPoint’)
  .data(incomingData)
  .enter()
  .append(‘div’)
  .attr(‘class’, ‘dataPoint’)
  .html(function(d) { return d.label })

The error:

bundle.js:7360 Uncaught TypeError: this.querySelectorAll is not a function

I'm using D3 v4.1.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.