Repository: streamlit/streamlit

Issue:
Setting dataframe width doesn't have effect
### Summary

Providing `st.dataframe` a width doesn't seem to have the desired effect.

### Steps to reproduce

Code snippet:

```
st.dataframe(df, width=1200)
```
<img width="1167" alt="Screen Shot 2022-07-04 at 9 42 00 PM" src="https://user-images.githubusercontent.com/89804861/177250878-ae893618-c6fb-4208-8d63-3a97e6bb804f.png">

That's the output of the table, regardless of whether I give the `width` argument or not.

**Expected behavior:**

The table width is set to the integer number of pixels and the column widths are dispersed evenly within that width.

**Actual behavior:**

The table doesn't change size.

### Is this a regression?

Don't think so.

### Debug info

- Streamlit version: 1.10
- Python version: 3.9.13
- OS version: OS X
- Browser version: Chrome latest

### Additional information

I believe this is due to the columns themselves not having width and just taking enough width to fit the values. But the header values (column names) seem to get ignored when making that calculation.

I tried to play around with the CSS but due to Canvas was unable to do things easily in inspection mode in the browser.

As an aside, would really like a full-width, responsive dataframe option (potentially as default).


Discussion:
@alexscott-ff Thanks for reporting this issue.
> I believe this is due to the columns themselves not having a width and just taking enough width to fit the values. 

That's indeed the reason why it currently fails to set the width. Unfortunately, there is no good workaround at the moment to fix this with `st.dataframe`. We will soon investigate some time to improve the automatic column sizing, which I assume will also fix this issue. 
Sounds good, thanks for the quick response @LukasMasuch !
Has there been any updates on this? This makes Dataframes unusable right?
Has there been any updates on this? I had same problems.
any updates? this df behavior makes my eye tick!
I have the same issue. I can use `st.table` which looks fine but of course I lose the interactivity.

I think if it could just take the header width into account when sizing the columns that might help, though I'd still eventually want the width parameter to have an effect.
+1 for this, the current implementation is unusable, I have to use `st._legacy_dataframe` for all dataframes instead. 

Resolving patch:
diff --git a/e2e/scripts/st_arrow_dataframe_dimensions.py b/e2e/scripts/st_arrow_dataframe_dimensions.py
--- a/e2e/scripts/st_arrow_dataframe_dimensions.py
+++ b/e2e/scripts/st_arrow_dataframe_dimensions.py
@@ -27,3 +27,13 @@
 st._arrow_dataframe(df, width=250)
 st._arrow_dataframe(df, height=150)
 st._arrow_dataframe(df, 5000, 5000)
+st._arrow_dataframe(df, use_container_width=True)
+
+small_df = pd.DataFrame(np.random.randn(100, 3))
+st._arrow_dataframe(small_df, width=500)
+st._arrow_dataframe(small_df, use_container_width=True)
+st._arrow_dataframe(small_df, width=200, use_container_width=True)
+st._arrow_dataframe(small_df, width=200, use_container_width=False)
+
+one_col_df = pd.DataFrame(np.random.randn(100, 1))
+st._arrow_dataframe(one_col_df, use_container_width=True)
diff --git a/lib/streamlit/elements/arrow.py b/lib/streamlit/elements/arrow.py
--- a/lib/streamlit/elements/arrow.py
+++ b/lib/streamlit/elements/arrow.py
@@ -36,6 +36,8 @@ def _arrow_dataframe(
         data: Data = None,
         width: Optional[int] = None,
         height: Optional[int] = None,
+        *,
+        use_container_width: bool = False,
     ) -> "DeltaGenerator":
         """Display a dataframe as an interactive table.
 
@@ -46,13 +48,20 @@ def _arrow_dataframe(
 
             If 'data' is a pandas.Styler, it will be used to style its
             underlying DataFrame.
+
         width : int or None
-            Desired width of the UI element expressed in pixels. If None, a
-            default width based on the page width is used.
+            Desired width of the dataframe expressed in pixels. If None, the width
+            will be automatically calculated based on the column content.
+
         height : int or None
-            Desired height of the UI element expressed in pixels. If None, a
+            Desired height of the dataframe element expressed in pixels. If None, a
             default height is used.
 
+        use_container_width : bool
+            If True, set the dataframe width to the width of the parent container.
+            This takes precedence over the width argument.
+            This argument can only be supplied by keyword.
+
         Examples
         --------
         >>> df = pd.DataFrame(
@@ -80,13 +89,13 @@ def _arrow_dataframe(
         default_uuid = str(hash(delta_path))
 
         proto = ArrowProto()
+        proto.use_container_width = use_container_width
+        if width:
+            proto.width = width
+        if height:
+            proto.height = height
         marshall(proto, data, default_uuid)
-        return self.dg._enqueue(
-            delta_type="arrow_data_frame",
-            element_proto=proto,
-            element_width=width,
-            element_height=height,
-        )
+        return self.dg._enqueue("arrow_data_frame", proto)
 
     def _arrow_table(self, data: Data = None) -> "DeltaGenerator":
         """Display a static table.
diff --git a/lib/streamlit/elements/dataframe_selector.py b/lib/streamlit/elements/dataframe_selector.py
--- a/lib/streamlit/elements/dataframe_selector.py
+++ b/lib/streamlit/elements/dataframe_selector.py
@@ -39,6 +39,8 @@ def dataframe(
         data: "Data" = None,
         width: Optional[int] = None,
         height: Optional[int] = None,
+        *,
+        use_container_width: bool = False,
     ) -> "DeltaGenerator":
         """Display a dataframe as an interactive table.
 
@@ -56,13 +58,20 @@ def dataframe(
             (i.e. with `config.dataFrameSerialization = "legacy"`).
             To use pyarrow tables, please enable pyarrow by changing the config setting,
             `config.dataFrameSerialization = "arrow"`.
+
         width : int or None
-            Desired width of the UI element expressed in pixels. If None, a
-            default width based on the page width is used.
+            Desired width of the dataframe expressed in pixels. If None, the width
+            will be automatically calculated based on the column content.
+
         height : int or None
-            Desired height of the UI element expressed in pixels. If None, a
+            Desired height of the dataframe expressed in pixels. If None, a
             default height is used.
 
+        use_container_width : bool
+            If True, set the dataframe width to the width of the parent container.
+            This takes precedence over the width argument.
+            This argument can only be supplied by keyword.
+
         Examples
         --------
         >>> df = pd.DataFrame(
@@ -92,7 +101,9 @@ def dataframe(
 
         """
         if _use_arrow():
-            return self.dg._arrow_dataframe(data, width, height)
+            return self.dg._arrow_dataframe(
+                data, width, height, use_container_width=use_container_width
+            )
         else:
             return self.dg._legacy_dataframe(data, width, height)
 


Test patch:
diff --git a/frontend/src/components/widgets/DataFrame/DataFrame.test.tsx b/frontend/src/components/widgets/DataFrame/DataFrame.test.tsx
--- a/frontend/src/components/widgets/DataFrame/DataFrame.test.tsx
+++ b/frontend/src/components/widgets/DataFrame/DataFrame.test.tsx
@@ -26,6 +26,7 @@ import { renderHook, act } from "@testing-library/react-hooks"
 import { TEN_BY_TEN } from "src/lib/mocks/arrow"
 import { mount } from "src/lib/test_util"
 import { Quiver } from "src/lib/Quiver"
+import { Arrow as ArrowProto } from "src/autogen/proto"
 
 import DataFrame, {
   DataFrameProps,
@@ -34,10 +35,18 @@ import DataFrame, {
 } from "./DataFrame"
 import { StyledResizableContainer } from "./styled-components"
 
-const getProps = (data: Quiver): DataFrameProps => ({
-  element: data,
-  width: 400,
-  height: 400,
+const getProps = (
+  data: Quiver,
+  useContainerWidth = false
+): DataFrameProps => ({
+  element: ArrowProto.create({
+    data: new Uint8Array(),
+    useContainerWidth,
+    width: 400,
+    height: 400,
+  }),
+  data,
+  width: 700,
 })
 
 const { ResizeObserver } = window
@@ -74,6 +83,17 @@ describe("DataFrame widget", () => {
     )
   })
 
+  it("grid container should use full width when useContainerWidth is used", () => {
+    const wrapper = mount(
+      <DataFrame {...getProps(new Quiver({ data: TEN_BY_TEN }), true)} />
+    )
+    const dataFrameContainer = wrapper
+      .find(StyledResizableContainer)
+      .props() as any
+    expect(dataFrameContainer.width).toBe(700)
+    expect(dataFrameContainer.height).toBe(400)
+  })
+
   it("grid container should render with specific size", () => {
     const wrapper = mount(<DataFrame {...props} />)
     const dataFrameContainer = wrapper
@@ -85,7 +105,7 @@ describe("DataFrame widget", () => {
 
   it("Test column resizing function.", () => {
     const { result } = renderHook(() =>
-      useDataLoader(new Quiver({ data: TEN_BY_TEN }))
+      useDataLoader(props.element, props.data)
     )
 
     // Resize first column to size of 123:
@@ -111,11 +131,11 @@ describe("DataFrame widget", () => {
   })
 
   it("should correctly sort the table descending order", () => {
-    const tableColumns = getColumns(new Quiver({ data: TEN_BY_TEN }))
+    const tableColumns = getColumns(props.element, props.data)
 
     // Add descending sort for first column
     const { result } = renderHook(() =>
-      useDataLoader(new Quiver({ data: TEN_BY_TEN }), {
+      useDataLoader(props.element, props.data, {
         column: tableColumns[0],
         mode: "smart",
         direction: "desc",
@@ -138,11 +158,11 @@ describe("DataFrame widget", () => {
   })
 
   it("should correctly sort the table ascending order", () => {
-    const tableColumns = getColumns(new Quiver({ data: TEN_BY_TEN }))
+    const tableColumns = getColumns(props.element, props.data)
 
     // Add ascending sort for first column
     const { result } = renderHook(() =>
-      useDataLoader(new Quiver({ data: TEN_BY_TEN }), {
+      useDataLoader(props.element, props.data, {
         column: tableColumns[0],
         mode: "smart",
         direction: "asc",
diff --git a/lib/tests/streamlit/arrow_dataframe_dimensions_test.py b/lib/tests/streamlit/arrow_dataframe_dimensions_test.py
--- a/lib/tests/streamlit/arrow_dataframe_dimensions_test.py
+++ b/lib/tests/streamlit/arrow_dataframe_dimensions_test.py
@@ -45,9 +45,9 @@ def _do_test(self, fn, expectedWidth, expectedHeight):
         df = pd.DataFrame({"A": [1, 2, 3, 4, 5]})
 
         fn(st._arrow_dataframe, df)
-        metadata = self._get_metadata()
-        self.assertEqual(metadata.element_dimension_spec.width, expectedWidth)
-        self.assertEqual(metadata.element_dimension_spec.height, expectedHeight)
+        arrow_data_frame = self.get_delta_from_queue().new_element.arrow_data_frame
+        self.assertEqual(arrow_data_frame.width, expectedWidth)
+        self.assertEqual(arrow_data_frame.height, expectedHeight)
 
     def _get_metadata(self):
         """Returns the metadata for the most recent element in the
diff --git a/lib/tests/streamlit/dataframe_selector_test.py b/lib/tests/streamlit/dataframe_selector_test.py
--- a/lib/tests/streamlit/dataframe_selector_test.py
+++ b/lib/tests/streamlit/dataframe_selector_test.py
@@ -47,7 +47,9 @@ def test_legacy_dataframe(self, arrow_dataframe, legacy_dataframe):
     def test_arrow_dataframe(self, arrow_dataframe, legacy_dataframe):
         streamlit.dataframe(DATAFRAME, 100, 200)
         legacy_dataframe.assert_not_called()
-        arrow_dataframe.assert_called_once_with(DATAFRAME, 100, 200)
+        arrow_dataframe.assert_called_once_with(
+            DATAFRAME, 100, 200, use_container_width=False
+        )
 
     @patch.object(DeltaGenerator, "_legacy_table")
     @patch.object(DeltaGenerator, "_arrow_table")
diff --git a/lib/tests/streamlit/help_test.py b/lib/tests/streamlit/help_test.py
--- a/lib/tests/streamlit/help_test.py
+++ b/lib/tests/streamlit/help_test.py
@@ -84,14 +84,14 @@ def test_unwrapped_deltagenerator_func(self):
             # Python < 3.9 represents the signature slightly differently
             self.assertEqual(
                 "(data: 'Data' = None, width: Union[int, NoneType] = None, "
-                "height: Union[int, NoneType] = None) -> 'DeltaGenerator'",
+                "height: Union[int, NoneType] = None, *, use_container_width: bool = False) -> 'DeltaGenerator'",
                 ds.signature,
             )
         else:
 
             self.assertEqual(
                 "(data: 'Data' = None, width: Optional[int] = None, "
-                "height: Optional[int] = None) -> 'DeltaGenerator'",
+                "height: Optional[int] = None, *, use_container_width: bool = False) -> 'DeltaGenerator'",
                 ds.signature,
             )