react-vega icon indicating copy to clipboard operation
react-vega copied to clipboard

"width": "container" property not resizing graph properly

Open rafaelbenavent opened this issue 4 years ago • 22 comments

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.

rafaelbenavent avatar Jan 17 '20 23:01 rafaelbenavent

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 avatar Jan 17 '20 23:01 domoritz

@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.

rafaelbenavent avatar Jan 17 '20 23:01 rafaelbenavent

React vega uses vega-lite via vega-embed. Only vega-lite 4 has support for responsive charts.

domoritz avatar Jan 18 '20 22:01 domoritz

So basically react vega uses vega-lite < 4?

rafaelbenavent avatar Jan 19 '20 00:01 rafaelbenavent

No https://github.com/vega/react-vega/blob/master/packages/react-vega/package.json#L27

domoritz avatar Jan 20 '20 03:01 domoritz

@domoritz I checked my node_modules folder and it seems my version of vega-lite is 4.0.2.

rafaelbenavent avatar Jan 20 '20 03:01 rafaelbenavent

@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 avatar Feb 05 '20 06:02 kristw

@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.

rafaelbenavent avatar Feb 05 '20 08:02 rafaelbenavent

🚫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 avatar Feb 05 '20 18:02 backnotprop

@backnotprop Have you tried replacing react-vega-lite, which is deprecated, with react-vega ?

kristw avatar Feb 05 '20 20:02 kristw

@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 avatar Feb 06 '20 04:02 backnotprop

@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.

rafaelbenavent avatar Feb 06 '20 04:02 rafaelbenavent

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?

longlin58 avatar Jun 30 '20 20:06 longlin58

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.

awm33 avatar Jul 06 '20 15:07 awm33

Related issue in Vega-Embed: https://github.com/vega/vega-embed/pull/490

domoritz avatar Jul 06 '20 17:07 domoritz

Update: switching to render to "svg" and adding the following CSS fixed this for me:

.vega-embed > svg {
  width: 100%;
  height: 100%;
}

awm33 avatar Jul 11 '20 19:07 awm33

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.

Falgianot avatar Aug 03 '20 13:08 Falgianot

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 :/

Ramblurr avatar Mar 10 '21 09:03 Ramblurr

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.

screen-capture

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?

jasonsturges avatar May 07 '21 20:05 jasonsturges

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: image

replacing height in the spec with 600: image

fittyCent avatar Sep 13 '22 21:09 fittyCent

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.

fittyCent avatar Sep 13 '22 22:09 fittyCent

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!

lehnerchristian avatar Nov 25 '23 21:11 lehnerchristian