I'm looking to create a stacked line graph similar to the following example:
https://dc-js.github.io/dc.js/
However, in addition I would like a field above the graph that displays the current value of the mouseover.
I.e. instead of having to pause for a second with the cursor on the graph, and then having a mouse over box come up, I would like the values to show outside the graph, similar to the way that they do in Google Finance (see how price and vol on top left of graph change as you mouseover). E.g.https://www.google.com/finance?q=apple&ei=MUiWVtnQIdaP0ASy-6Uo
I would really appreciate any info the community could share on what is the best way to approach this.
You can do this by adding your own mouseover/mouseout events to the dots in the chart. I've added a .display-qux span inside the chart div:
<div id="monthly-move-chart">
...
<span class="display-qux"></span>
</div>
but of course it could be somewhere else, this just makes it easy to select for this example.
Then add mouse events using the renderlet event, which is fired after every render and every redraw:
.on('renderlet', function(chart) {
chart.selectAll('circle.dot')
.on('mouseover.foo', function(d) {
chart.select('.display-qux').text(dateFormat(d.data.key) + ': ' + d.data.value);
})
.on('mouseout.foo', function(d) {
chart.select('.display-qux').text('');
});
});
The .foo is an event namespace, to avoid interfering with internal use of these events. You should probably use a word here that is relevant to what you're trying to do. Documentation on event namespaces is here.
Sample output:
The process is the same for adding events to the other charts, but for example, you would selectAll('rect.bar', ... for bar charts, etc.
Related
this is somewhat related to my previous post where I learned a bit more about actions.
I have been trying to figure out how to work with this nifty feature but I seem to be a bit stuck in the past few hours.
In my Component I create an SVG viewbox like so:
<svg id="pitch" viewBox={`0 0 ${width} ${height}`} use:foo>
</svg>
then drawPitch is this function:
function foo(node) {
// the node has been mounted in the DOM
let g = node.append('h1');
g.text("This is the text I'd like to render to check that it works");
return {
destroy() {
// the node has been removed from the DOM
}
};
}
From what I've understood in the docs, the use:foo will pass the calling node to foo, so I thought directly appending svg elements to it should work.
Do I need to update it somehow?
Here is a repl with reproducible code.
I get the following error:
Missing "./types/runtime/internal/keyed_each.js" export in "svelte" package
Thank you!
I would expect the code in foo to start with d3.select(node), and everything to work based off that. Otherwise the DOM tree generated by d3 will not be connected to your document at all. Alternatively the resulting element (selection.node()) has to be appended to node at some point.
The error sounds highly unrelated and probably would require more context.
Note: You cannot add HTML directly to SVGs, SVGs are for canvas-like vector graphics, not document layouts. If you want to insert text, use the <text> element.
It seems that the selection events are not being passed through custom cell renderers. My goal is I want to change the background color of every cell in my grid (based on the values), and also be able to handle selection events. I've modified the example in the docs here:
https://www.telerik.com/kendo-react-ui/components/grid/selection/
To include a background color on the Units on Order column. You'll notice that that column does not participate in selections. I created a stackblitz example here:
https://stackblitz.com/edit/react-o4ycqi?file=app/main.jsx
All I changed was I added a cellWithBackground function and assigned it to the column UnitsInStock. Here is that function
const cellWithBackGround = props => {
const examplePrice = true;
const style = {
backgroundColor: "rgb(243, 23, 0, 0.32)"
};
const field = props.field || '';
return <td style={style}>
{props.dataItem[field]}
</td>;
};
I did find an example that was close but it I couldn't get it to work with functional components. It just worked with classes which I don't use. So, please provide examples or references on that support Functional Components.
H Peter,
Thank you for sharing the code. By completely replacing the entire cell's infrastructure, it will no longer respond to anything from the Grid.
Specifically, I'm referring to this line in the function. It only returns a <td> with the field's name, it abandons the rest of the properties of the element.
//cell returned form the function
return <td style={style}>
{props.dataItem[field]}
</td>;
At that point, it's basically a dead cell. It will not respond to events, data actions, etc because it is missing all the parts that the Grid requires to interact with it.
Further Guidance
As I mentioned in my Twitter reply, you can get guidance for the Kendo Engineers to help you from here.
I think there's a better way to handle this by using styling instead of manually handling the DOM elements directly. At the very least, you need to return the complete infrastructure of the cell and they can assist with that.
I'm still relatively new to programming and I have a project I am working on. I am making a staff efficiency dashboard for a fictional pizza company. I want to find the quickest pizza making time and display the time and the staff members name to the user.
With the data charts it has been easy. Create a function, then use dc, e.g dc.barChart("#idOfDivInHtmlPage")
I suspect I might be trying to be too complicated, and that I've completely forgotten how to display any outputs of a js function to a html page.
I've been using d3.js, dc.js and crossfilter to represent most of the data visually in an interactive way.
Snippet of the .csv
Name,Rank,YearsService,Course,PizzaTime
Scott,Instore,3,BMC,96
Mark,Instore,4,Intro,94
Wendy,Instore,3,Intro,76
This is what I've tried so far:
var timeDim = ndx.dimension(function(d) {
return [d.PizzaTime, d.Name]
});
var minStaffPizzaTimeName = timeDim.bottom(1)[0].PizzaTime;
var maxStaffPizzaTimeName = timeDim.top(1)[0].PizzaTime;
}
then in the html
<p id="minStaffPizzaTimeName"></p>
<script type="text/javascript" src="static/js/graph.js">
document.write("minStaffPizzaTimeName");
</script>
You are surely on the right track, but in javascript you often have to consider the timing of when things will happen.
document.write() (or rather, anything at the top level of a script) will get executed while the page is getting loaded.
But I bet your data is loaded asynchronously (probably with d3.csv), so you won't have a crossfilter object until a bit later. You haven't shown these parts but that's the usual way to use crossfilter and dc.js.
So you will need to modify the page after it's loaded. D3 is great for this! (The straight javascript way to do this particular thing isn't much harder.)
You should be able to leave the <p> tag where it is, remove the extra <script> tag, and then, in the function which creates timeDim:
d3.select('#minStaffPizzaTimeName').text(minStaffPizzaTimeName);
This looks for the element with that ID and replaces its content with the value you have computed.
General problem solving tools
You can use the dev tools dom inspector to make sure that the p tag exists with id minStaffPizzaTimeName.
You can also use
console.log(minStaffPizzaTimeName)
to see if you are fetching the data correctly.
It's hard to tell without a running example but I think you will want to define your dimension using the PizzaTime only, and convert it from a string to a number:
var timeDim = ndx.dimension(function(d) {
return +d.PizzaTime;
});
Then timeDim.bottom(1)[0] should give you the row of your original data with the lowest value of PizzaTime. Adding .Name to that expression should retrieve the name field from the row object.
But you might have to poke around using console.log or the interactive debugger to find the exact expression that works. It's pretty much impossible to use dc.js or D3 without these tools, so a little investment in learning them will pay off big time.
Boom, finally figured it out.
function show_fastest_and_slowest_pizza_maker(ndx) {
var timeDim = ndx.dimension(dc.pluck("PizzaTime"));
var minPizzaTimeName = timeDim.bottom(1)[0].Name;
var maxPizzaTimeName = timeDim.top(1)[0].Name;
d3.select('#minPizzaTimeName')
.text(minPizzaTimeName);
d3.select('#maxPizzaTimeName')
.text(maxPizzaTimeName);
}
Thanks very much Gordon, you sent me down the right path!
Lets say I am creating a bar chart using Recharts, how would i create a custom component for each of the following Recharts components:
XAxis, YAxis, Tooltip, Legend, CartesianGrid, Cell, and Bar
The reason for this s because i am planning to create a chart with a lot of props and wish to separate all the default props and customization in their own individual component for the list component above.
I have tried just putting the CartesianGrid in a react component and the grid will not show
Any ideas?
It seems that you want to wrap the existing Recharts provided components in a custom component for better organization of your code.
This is currently not supported directly by Recharts, as they check for the type of element that you are rendering and if it does not match one of the allowed types it does not get rendered.
For ex:
<LineChart>
<Line></Line>
</LineChart>
would display a line correctly,
But
function MyLine(props) {
return <Line></Line>
}
<LineChart>
<MyLine />
<LineChart>
would not render the line.
This is because, recharts figures out that the MyLine component is not allowed and hence would not be displayed.
This is a big problem as it does not allow us to reuse or compose components.
But there are some workarounds, one of them being calling your custom component as a function directly:
<LineChart>
{
MyLine({})
}
</LineChart>
It also seems like there are no plans to provide such an api in future. All such issues on their github are already closed, without providing a solution.
https://github.com/recharts/recharts/issues/412
https://github.com/recharts/recharts/issues/41
https://github.com/recharts/recharts/issues/1470
I am trying to create a grid app with various sections and each section is being fetched to a specific listview however I have encountered a problem where you can only have one listview covering the entire page in order to properly horizontally scroll the objects inside the list which means there's no room for another one. This is the code I am using right now:
WinJS.xhr({ url: "http://search.twitter.com/search.json?q=%23windows8&rpp=100}).then(
function (response) {
var json = JSON.parse(response.responseText.toString());
var list = new WinJS.Binding.List(json.result);
gridView1.winControl.itemDataSource = list.dataSource;
//gridView1 is ID of listview
}
With the above code I can easily show grids of objects containing result array and then bind em to the list. However now I want multiple similar listviews for different URLs that are displayed like the one shown as default interface in WinJS grid app.
To be more clear, this is what I want - Twitter usernames in first section of grid by using Twitter API URL1 and then I want twitter search results in adjacent grid so I have to use another listview b using URL2.
How do I find a fix for this. Appreciate your help.
Yeah, coming up with what all of the disparate items from the different lists have in common and projecting your data up to a single grouped list is one option. You might not want to give up on what you were trying to do though. If you put multiple ListViews on a page wrapped in a flexbox, you shouldn't have any trouble with scrolling. If you look at my codeSHOW app at the ListView demo, you'll see that I have the rough equivalent. Windows is actually really smart about the way it handles the panning.
** EDIT **
Here's a rough example of what I'm talking about. Again, you can find a working example of this in the ListView demo of codeSHOW.
<!-- HTML snippet -->
<div class="hub">
<div>
<div id="list1" data-win-control="WinJS.UI.ListView"></div>
</div>
<div>
<div id="list2" data-win-control="WinJS.UI.ListView"></div>
</div>
<div>
<div id="list3" data-win-control="WinJS.UI.ListView"></div>
</div>
</div>
/* CSS snippet */
.hub {
display:-ms-flexbox; /* this will lay the lists out horizontally */
overflow-x:auto; /* set the flexbox to scroll its overflow */
}
/* select each of the sections */
.hub > div {
padding-right:80px; /* 80px of space between "sections" */
}
/* choose whatever sizes you want for your list views. You may want to make them wide
enough that they don't scroll because it can get a little awkward to have scrolling
within scrolling */
[data-win-control=WinJS.UI.ListView] {
width: 640px;
height: 480px;
}
You can solve this by aggregating the result set into a single data source.
You can either do this through splurging your data into a WinJS.Binding.List that's been set up with a grouping function, and attribute your data in such a way that you know how to group them. An example of the grouping of a WinJS.Binding.List can be found in the "Grid" Template that you find in Visual Studio when doing File/New/Project.
Or, you can build your own data VirtualizedDataSource - there is a great tutorial for this on MSDN here.