react-vega
react-vega copied to clipboard
"width": "container" property not resizing graph properly
Steps to reproduce:
- Create a spec with the property: "width": "container".
- Place the VegaLite container inside a with width 100%.
- The graph will not be resized to fit the whole container. If on Windows, clicking the maximize/Restore down button (the one with overlapping squares by the close window icon) will cause the graph to resize slowly.
Responsiveness works in https://vega.github.io/editor/#/examples/vega-lite/bar_size_responsive. Can you clarify what is not working? Which versions of Vega and Vega-Lite are you using?
@domoritz That is vega-lite, not react-vega. I created a minimum reproducible project in vega-lite and react-vega and vega-lite works. react-vega doesn't.
React vega uses vega-lite via vega-embed. Only vega-lite 4 has support for responsive charts.
So basically react vega uses vega-lite < 4?
No https://github.com/vega/react-vega/blob/master/packages/react-vega/package.json#L27
@domoritz I checked my node_modules folder and it seems my version of vega-lite is 4.0.2.
@rafaelbenavent
I created a minimum reproducible project in vega-lite and react-vega and vega-lite works. react-vega doesn't.
Would you mind sharing so we can debug?
@kristw Sure. I uploaded it here: https://file.io/sT0UoU.
Basically there are 3 files:
App.js:
import { BrowserRouter as Router } from "react-router-dom";
import { Vega } from "react-vega";
import spec from "./utils/vega-spec";
import "./app.css";
function App() {
return (
<Router>
<div className="container">
<Vega spec={spec} />
</div>
</Router>
);
}
export default App;
vega-spec.js:
const spec = {
$schema: "https://vega.github.io/schema/vega-lite/v4.json",
title: "Last Recorded PM10",
description: "A simple bar chart with embedded data.",
data: {
name: "table",
values: {
pM2_5: 2,
pM10: 3,
id: "70B3D549989BC2F9",
name: "aq_04",
description: "",
latitude: 0,
longitude: 0,
timestamp: "2018-11-08T10:53:35.640808",
enabled: true,
type: "air"
}
},
width: "container",
autosize: "fit",
mark: "bar",
autosize: "pad",
encoding: {
x: {
field: "name",
type: "ordinal",
title: "Name"
},
y: {
field: "pM10",
type: "quantitative",
title: "PM10"
},
color: {
field: "name",
type: "nominal",
title: "Name"
}
},
signals: [
{
name: "width",
update: "(containerSize()[0])",
on: [
{
events: { source: "window", type: "resize" },
update: "containerSize()[0]"
}
]
},
{
name: "height",
update: "(containerSize()[1])",
on: [
{
events: { source: "window", type: "resize" },
update: "containerSize()[1]"
}
]
}
]
};
export default spec;
app.css:
*,
*::after,
*::before {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
You can see it doesn't stretch properly, especially if you click the maximise/restore down button of the window.
🚫Having trouble with this in react-vega-lite
with the example config/setup
✅Update: sorry, I only needed to apply the styles to the VegaLite component itself (className is vega-container)
.vega-container,
.vega-container::after,
.vega-container::before {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
}
.vega-container {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
@backnotprop Have you tried replacing react-vega-lite
, which is deprecated, with react-vega
?
@kristw sorry that was a poor report... I was using react-vega and I solved it by applying the styles to the VegaLite component
@backnotprop were you able to get a graph to be responsive and stretch to fit its container? If so, would you mind sharing some code with me to have a look at. I've been struggling with this.
having the same problem where width is set to "container" but will not resize with the div container. anyone has a solution or an example that i can follow?
I'm also experiencing this. I'm using the Vega
component, with vega-lite spec and the mode prop set to "vega-lite". The vega-lite spec has both width
and height
set to "container". This works on the initial load, but when it's container is resized, it does not resize.
I found a workaround, but it's not performant. In the parent React component, I created a ref to the Vega
component and run clearView()
and createView()
on resize.
this.vega.current.vegaEmbed.current.clearView();
this.vega.current.vegaEmbed.current.createView();
I've also tried running view.resize()
, but that doesn't work.
this.vega.current.vegaEmbed.current.modifyView(view => {
view.resize().run();
});
Taking a look at the VegaEmbed
component's code, it looks like it inspects the width
and height
propes and properties of the spec to see if they changed in order to re-render https://github.com/vega/react-vega/blob/f2a4d64cf7e1ba55f3e768c81d95237788434e1b/packages/react-vega/src/VegaEmbed.tsx#L44-L77
Overall, I think there's a lot of use cases when using Vega in a web app that having fit to container exist as a prop that even works on vega spec would be amazing.
Related issue in Vega-Embed: https://github.com/vega/vega-embed/pull/490
Update: switching to render
to "svg" and adding the following CSS fixed this for me:
.vega-embed > svg {
width: 100%;
height: 100%;
}
Update: switching to
render
to "svg" and adding the following CSS fixed this for me:.vega-embed > svg { width: 100%; height: 100%; }
What file did you change the render to svg and where did you add this css? Having trouble figuring out this issue still.
I got responsiveness working by adding the following css. No need to change the render option.
.vega-embed {
width: 100%;
height: 100%;
}
Using versions:
"react-vega": "^7.4.2",
"vega": "^5.19.1",
"vega-lite": "^5.0.0"
Edit: Correction, it wonly works properly when width
is set to "container"
. Setting both width and height to container results in a canvas with 0
height. The only way to "fix" it is to give the vega-embed
a fixed pixel height (rather than something like 100%
) which defeats the purpose :/
What's the status here? Tracking some issues regarding this across Vega, Vega Lite, and Vega Embed projects.
Is there some global CSS missing?
In React, if I put this component within a full width container the chart canvas is 0 width when using width: "container"
unless I include css for vega-embed. Also tried a variety of autosize
options.
Vega Chart is inside a full-width container:
<Box sx={{ width: "100%" }}>
<BarChart data={data} />
</Box>
Seems like this only works with css referenced in earlier posts:
.vega-embed {
width: 100%;
}
While I see this working in the example: https://vega.github.io/vega-lite/examples/bar_size_responsive.html
It isn't full width in the Editor, which I believe is also in a full-width chart container: https://vega.github.io/editor/#/examples/vega-lite/bar_size_responsive
Is there still an issue here?
It doesn't work if you pass it a Vega style spec (at least from what I'm seeing):
spec:
const testSpecVega = {
title: {
text: 'TEST',
fontSize: 20
},
width: 'container',
height: 600,
data: [
{
name: 'table'
}
],
scales: [
{
name: 'xscale',
type: 'band',
domain: { data: 'table', field: 'a' },
range: 'width',
padding: 0.2,
},
{
name: 'yscale',
domain: { data: 'table', field: 'b' },
nice: true,
range: 'height'
},
],
axes: [
{
orient: 'bottom',
scale: 'xscale',
title: 'a title',
},
{
orient: 'left',
scale: 'yscale',
title: 'b title',
grid: true,
},
],
marks: [
{
type: 'rect',
from: { data: 'table' },
encode: {
enter: {
"fill": { "value": "steelblue" },
x: { scale: 'xscale', field: 'a' },
width: { scale: 'xscale', band: 1 },
y: { scale: 'yscale', field: 'b' },
y2: { scale: 'yscale', value: 0 },
}
}
}
],
}
react component
<Vega spec={testSpecVega} data={{
table: [
{ a: 'A', b: 3 },
{ a: 'B', b: 2 },
{ a: 'C', b: 5 },
{ a: 'D', b: 7 },
{ a: 'E', b: 8 },
{ a: 'F', b: 2 },
]
}} />
css:
.vega-embed {
width: 100%;
}
result:
replacing height in the spec with 600:
Did some researching and yeah the 'container'
value for width is only a vega-lite thing. In order to get the chart to be full width of container, do this:
const testSpec = {
width: 'container',
height: 200,
actions: false,
mark: 'bar',
text: '#fff',
encoding: {
x: { field: 'a', type: 'ordinal' },
y: { field: 'b', type: 'quantitative' },
},
data: { name: 'table' }
}
const testSpecVega = {
title: {
text: 'TEST',
fontSize: 20
},
signals: [
{
name: "width",
init: "containerSize()[0]",
on: [{ events: "window:resize", update: "containerSize()[0]" }]
},
],
height: 600,
data: [
{
name: 'table'
}
],
scales: [
{
name: 'xscale',
type: 'band',
domain: { data: 'table', field: 'a' },
range: 'width',
padding: 0.2,
},
{
name: 'yscale',
domain: { data: 'table', field: 'b' },
nice: true,
range: 'height'
},
],
axes: [
{
orient: 'bottom',
scale: 'xscale',
title: 'a title',
},
{
orient: 'left',
scale: 'yscale',
title: 'b title',
grid: true,
},
],
marks: [
{
type: 'rect',
from: { data: 'table' },
encode: {
update: {
"fill": { "value": "steelblue" },
x: { scale: 'xscale', field: 'a' },
width: { scale: 'xscale', band: 1 },
y: { scale: 'yscale', field: 'b' },
y2: { scale: 'yscale', value: 0 },
}
}
}
],
}
Notice the width
value is a signal that responds to window resizing and the description of the encoding is placed in encode.update
rather than encode.enter
.
Hi! I also had this problem for the longest time. In my case I have vega-lite visualizations in resizeable parent components and the visualizations should re-render with the correct size when the parent resizes. It turns out that vega or vega-lite listen to resize
events, therefore I am firing resize events when the parent resizes via
window.dispatchEvent(new Event('resize'));
Here's a working example: https://stackblitz.com/edit/stackblitz-starters-p1vuxb?file=src%2FApp.tsx
Hope this helps!