Repository: plotly/dash

Issue:
Remove `webdriver-manager` from `dash[testing]` requirements
This was added in #1801 but wasn't really clear why it was needed, and it reaches out to the internet during its installation (even if installed from a local PyPI mirror) which causes problems for some users.
[BUG] Dash DataTable style_header text_alignment breaks between Dash v2.0.0 and v2.1.0
Production environment is under a customer container hosting environment that hosts docker container in a large corporate environment.  Docker container is setup with rhel 7 (Redhat) with an Apache 2.4 web server.  Python 3.6 is the version of python using pip as the installation for packages.

-  replace the result of `pip list | grep dash` below
```
dash                       2.1.0    
dash-bootstrap-components  1.0.2    
dash-core-components       2.0.0    
dash-html-components       2.0.0    
dash-renderer              1.9.1    
dash-table                 5.0.0
```

-  if frontend related, tell us your Browser, Version and OS

    - OS: MacOS Monterey version 12.2
    - Browser Chrome
    - Version 98.0.4758.80

**Describe the bug**

When using 'text-align': 'center' in the style_header directive within the dash_table.DataTable, under Dash 2.1.0, the headings for the table are not centered, rather they are right aligned, regardless of the setting of the 'text-align' value.  In fact, directly editing the text-align within Chrome's developer tool will not change the alignment.  Changing other attributes (font, color, etc... will work).  

**Expected behavior**

I would expect that when 'text-align': 'center' is specified for the style_header directive within dash_table.DataTable that the headings for the columns specified would be centered above the column.

**Screenshots**

![image](https://user-images.githubusercontent.com/3968562/152881625-b91b2ad5-317c-4ae6-ba90-3e289fcea525.png)

**Additional Information**
I've been able to workaround this issue by reverting to Dash 2.0.0 in my production build, but I have not been able to build a similar working environment, neither in pip nor conda.  My production version is working.  I only reverted to Dash 2.0.0 since it was a very recent release, and then the behavior of the dash_table.DataTable worked correctly.  It was the only change I made to the Docker build.  Now, regardless of whether I use Dash 2.0.0 or Dash 2.1.0, I'm seeing the persistent right alignment of headers in my DataTables.  

I do not know if it will help, but here is an example of code that I saw work under Dash 2.0.0, and then fail once I upgraded to Dash 2.1.0:
```
import pandas as pd
from dash import dcc
# import dash_core_components as dcc
from dash import html
# import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import plotly.express as px
import time
import random

app = dash.Dash(__name__)

def get_rand_data():
    mylist=[random.randint(1,6) for _ in range(6)]
    return mylist

def build_data(mylist):
    df = pd.DataFrame({
        "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
        "Amount": mylist,
        "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
    })
    dcc.Store(id='new_data', data=df.to_json())
    return df

def draw_graph(df):
    fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
    return fig

def get_table():
    data_table =  dash_table.DataTable(
        columns=[
            {"name": ["", "Year"], "id": "year"},
            {"name": ["City", "Montreal"], "id": "montreal"},
            {"name": ["City", "Toronto"], "id": "toronto"},
            {"name": ["City", "Ottawa"], "id": "ottawa"},
            {"name": ["City", "Vancouver"], "id": "vancouver"},
            {"name": ["Climate", "Temperature"], "id": "temp"},
            {"name": ["Climate", "Humidity"], "id": "humidity"},
        ],
        data=[
            {
                "year": i,
                "montreal": i * 10,
                "toronto": i * 100,
                "ottawa": i * -1,
                "vancouver": i * -10,
                "temp": i * -100,
                "humidity": i * 5,
            }
            for i in range(10)
        ],
        style_header={
                'text-align': 'center',
            },
        merge_duplicate_headers=True,
    )
    return data_table


mylist=get_rand_data()
df = build_data(mylist)
fig = draw_graph(df)
data_table = get_table()

refresh_button = dbc.Button('Refresh Data', color="info", className="me-1", id='refresh_button_lmd')

app.layout = html.Div(children=[
    html.H1(children='Hello Dash'),
    refresh_button,
    html.Div(children=[
        data_table
    ]),
    dcc.Store(id='new_data'),
    dcc.Loading(
        id='loading-data',
        children=[
            html.Div(children=[
                dcc.Graph(
                    id='example-graph',
                    figure=fig
                )
            ]
            )
        ],
        type='circle',
    ),
])


@app.callback(Output("example-graph", "figure"),
              Input("new_data", "data"))
def on_data(data):
    df = pd.read_json(data)
    time.sleep(5)
    fig = draw_graph(df)
    return fig


@app.callback(Output('new_data', 'data'),
              Input('refresh_button_lmd', 'n_clicks'))
def new_data(n_clicks):
    if n_clicks is None:
        print("Override Startup")
        mylist = get_rand_data()
        df = build_data(mylist)
        data = df.to_json()
    else:
        print(f'Button was clicked, this is {n_clicks} times.')
        mylist = get_rand_data()
        df = build_data(mylist)
        data=df.to_json()
    return data

if __name__ == '__main__':
    app.run_server(debug=True)
```

I suspect that perhaps an upgrade supporting the move to Dash 2.1.0 might be the issue, and that now that I've moved my base install, I do not know what library is causing this.  Any help would be appreciated.  I would like to remove the constraint of staying at Dash 2.0.0 as I saw faster response times with Dash 2.1.0.  Thanks!


Discussion:

@geneblandjr a short description in the issue title would be useful (not just "[BUG]")!
Note that the bug can be seen in the Documentation itself: https://dash.plotly.com/datatable/style. Even in the section that talks about conditional formatting that sets the expectation that headers and columns will align in the same way

Resolving patch:
diff --git a/dash/development/update_components.py b/dash/development/update_components.py
--- a/dash/development/update_components.py
+++ b/dash/development/update_components.py
@@ -42,20 +42,17 @@ def booststrap_components(components_source):
         status = proc.poll()
 
     if err:
-        print(err.decode(), file=sys.stderr)
+        print(("🛑 " if status else "") + err.decode(), file=sys.stderr)
 
-    if status == 0:
+    if status or not out:
         print(
-            "🟢 Finished installing npm dependencies for the following component packages: {} (status={}) 🟢".format(
-                source_glob, status
-            ),
+            "🚨 Failed installing npm dependencies for component packages: {source_glob} (status={status}) 🚨",
             file=sys.stderr,
         )
-    if not out:
+        sys.exit(1)
+    else:
         print(
-            "Failed installing npm dependencies for the following component packages {} (status={})".format(
-                source_glob, status
-            ),
+            f"🟢 Finished installing npm dependencies for component packages: {source_glob} 🟢",
             file=sys.stderr,
         )
 
@@ -82,13 +79,11 @@ def build_components(components_source):
         status = proc.poll()
 
     if err:
-        print(err.decode(), file=sys.stderr)
+        print(("🛑 " if status else "") + err.decode(), file=sys.stderr)
 
-    if not out:
+    if status or not out:
         print(
-            "🟢 Finished updating the following component packages {} (status={}) 🟢".format(
-                source_glob, status
-            ),
+            f"🚨 Finished updating component packages: {source_glob} (status={status}) 🚨",
             file=sys.stderr,
         )
         sys.exit(1)
@@ -117,21 +112,18 @@ def build_components(components_source):
 
         if not os.path.exists(build_directory):
             print(
-                "Could not locate build artifacts. Check that the npm build process completed successfully for the given package: {}".format(
-                    package
-                )
+                "🚨 Could not locate build artifacts."
+                + " Check that the npm build process completed"
+                + f" successfully for package: {package} 🚨"
             )
+            sys.exit(1)
         else:
-            print("🚚 Moving build artifacts from " + build_directory + " to Dash 🚚")
+            print(f"🚚 Moving build artifacts from {build_directory} to Dash 🚚")
             shutil.rmtree(dest_path)
             shutil.copytree(build_directory, dest_path)
             with open(os.path.join(dest_path, ".gitkeep"), "w"):
                 pass
-            print(
-                "🟢 Finished moving build artifacts from "
-                + build_directory
-                + " to Dash 🟢"
-            )
+            print(f"🟢 Finished moving build artifacts from {build_directory} to Dash 🟢")
 
 
 def cli():
@@ -143,7 +135,9 @@ def cli():
     )
     parser.add_argument(
         "components_source",
-        help="A glob string that matches the Dash component libraries to be updated (eg.'dash-table' // 'dash-core-components|dash-html-components' // 'all'). The default argument is 'all'.",
+        help="A glob string that matches the Dash component libraries to be updated"
+        " (eg.'dash-table' // 'dash-core-components|dash-html-components' // 'all')."
+        " The default argument is 'all'.",
         default="all",
     )
 
@@ -153,4 +147,5 @@ def cli():
     build_components(args.components_source)
 
 
-cli()
+if __name__ == "__main__":
+    cli()


Test patch:
diff --git a/components/dash-table/tests/selenium/test_column.py b/components/dash-table/tests/selenium/test_column.py
--- a/components/dash-table/tests/selenium/test_column.py
+++ b/components/dash-table/tests/selenium/test_column.py
@@ -20,6 +20,9 @@ def get_app(props=dict()):
     baseProps["filter_action"] = "native"
     baseProps["merge_duplicate_headers"] = True
 
+    # first col is normally only 60px, make it wider since we're adding
+    # all these actions and need to interact with them
+    baseProps["style_cell_conditional"][0].update(width=120, maxWidth=120, minWidth=120)
     baseProps.update(props)
 
     app.layout = DataTable(**baseProps)
diff --git a/components/dash-table/tests/visual/percy-storybook/Header.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Header.percy.tsx
--- a/components/dash-table/tests/visual/percy-storybook/Header.percy.tsx
+++ b/components/dash-table/tests/visual/percy-storybook/Header.percy.tsx
@@ -5,30 +5,78 @@ import DataTable from 'dash-table/dash/DataTable';
 
 const setProps = () => {};
 
-storiesOf('DashTable/Headers', module).add('multi header', () => (
-    <DataTable
-        setProps={setProps}
-        id='multi-header'
-        data={R.map(
-            i => ({
-                year: i,
-                montreal: i * 10,
-                toronto: i * 100,
-                ottawa: i * -1,
-                vancouver: i * -10,
-                temp: i * -100,
-                humidity: i * 0.1
-            }),
-            R.range(0, 100)
-        )}
-        columns={[
-            {name: ['Year', ''], id: 'year'},
-            {name: ['City', 'Montreal'], id: 'montreal'},
-            {name: ['City', 'Toronto'], id: 'toronto'},
-            {name: ['City', 'Ottawa'], id: 'ottawa'},
-            {name: ['City', 'Vancouver'], id: 'vancouver'},
-            {name: ['Climate', 'Temperature'], id: 'temp'},
-            {name: ['Climate', 'Humidity'], id: 'humidity'}
-        ]}
-    />
-));
+const data = [
+    {
+        a: 'A',
+        b: 'BBBBB',
+        c: 'CCCCCCCCCC',
+        d: 'DDDDDDDDDDDDDDD',
+        e: 'EEEEEEEEEEEEEEEEEEEE',
+        f: 'FFFFFFFFFFFFFFFFFFFFFFFFF'
+    }
+];
+const cols = ['a', 'b', 'c', 'd', 'e', 'f'].map(i => ({
+    name: `Column ${i.toUpperCase()}`,
+    id: i,
+    selectable: true
+}));
+const colsEditable = cols.map(col => ({
+    editable: true,
+    renamable: true,
+    deletable: true,
+    ...col
+}));
+const common = {style_table: {width: 'auto', marginBottom: '10px'}};
+const allActions = {
+    sort_action: 'native',
+    column_selectable: 'single',
+    editable: true
+};
+
+const alignments: [React.ReactNode?] = [];
+
+[{}, {sort_action: 'native'}, allActions].forEach(actionProps => {
+    ['left', 'center', 'right'].forEach(alignment => {
+        alignments.push(
+            <DataTable
+                data={data}
+                columns={actionProps === allActions ? colsEditable : cols}
+                style_header={{textAlign: alignment}}
+                {...actionProps}
+                {...common}
+            />
+        );
+    });
+});
+
+storiesOf('DashTable/Headers', module)
+    .add('multi header', () => (
+        <DataTable
+            setProps={setProps}
+            id='multi-header'
+            data={R.map(
+                i => ({
+                    year: i,
+                    montreal: i * 10,
+                    toronto: i * 100,
+                    ottawa: i * -1,
+                    vancouver: i * -10,
+                    temp: i * -100,
+                    humidity: i * 0.1
+                }),
+                R.range(0, 100)
+            )}
+            columns={[
+                {name: ['Year', ''], id: 'year'},
+                {name: ['City', 'Montreal'], id: 'montreal'},
+                {name: ['City', 'Toronto'], id: 'toronto'},
+                {name: ['City', 'Ottawa'], id: 'ottawa'},
+                {name: ['City', 'Vancouver'], id: 'vancouver'},
+                {name: ['Climate', 'Temperature'], id: 'temp'},
+                {name: ['Climate', 'Humidity'], id: 'humidity'}
+            ]}
+        />
+    ))
+    .add('alignment and actions', () => (
+        <div style={{width: '100px'}}>{alignments}</div>
+    ));
diff --git a/components/dash-table/tests/visual/percy-storybook/TriadValidation.percy.tsx b/components/dash-table/tests/visual/percy-storybook/TriadValidation.percy.tsx
--- a/components/dash-table/tests/visual/percy-storybook/TriadValidation.percy.tsx
+++ b/components/dash-table/tests/visual/percy-storybook/TriadValidation.percy.tsx
@@ -8,22 +8,33 @@ const actions = [TableAction.Native, TableAction.Custom];
 
 const setProps = () => {};
 
-let stories = storiesOf('DashTable/Props Validation', module);
+const elements: [React.ReactNode?] = [];
 
 actions.forEach(filter => {
     actions.forEach(sort => {
         actions.forEach(page => {
-            stories = stories.add(
-                `filter=${filter}, sorting=${sort}, pagination=${page}`,
-                () => (
-                    <DataTable
-                        filter_action={filter}
-                        sort_action={sort}
-                        page_action={page}
-                        setProps={setProps}
-                    />
-                )
+            elements.push(
+                <div
+                    style={{marginTop: '10px'}}
+                >{`filter=${filter}, sorting=${sort}, pagination=${page}`}</div>
+            );
+            elements.push(
+                <DataTable
+                    columns={[
+                        {name: 'A', id: 'a'},
+                        {name: 'B', id: 'b'}
+                    ]}
+                    data={[{a: 1, b: 2}]}
+                    filter_action={filter}
+                    sort_action={sort}
+                    page_action={page}
+                    setProps={setProps}
+                />
             );
         });
     });
 });
+
+storiesOf('DashTable/Props Validation', module).add('all variants', () => (
+    <div style={{width: '500px'}}>{elements}</div>
+));
diff --git a/components/dash-table/tests/visual/percy-storybook/Types.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Types.percy.tsx
--- a/components/dash-table/tests/visual/percy-storybook/Types.percy.tsx
+++ b/components/dash-table/tests/visual/percy-storybook/Types.percy.tsx
@@ -14,8 +14,8 @@ const columns: {name: string[]; id: string; presentation?: string}[] = [
 
 const columns_dd = columns.map(i => ({...i, presentation: 'dropdown'}));
 
-storiesOf('DashTable/Types', module)
-    .add('types input', () => (
+storiesOf('DashTable/Types', module).add('types input & dropdown', () => (
+    <div>
         <DataTable
             setProps={setProps}
             id='types input'
@@ -51,8 +51,6 @@ storiesOf('DashTable/Types', module)
             ]}
             columns={columns}
         />
-    ))
-    .add('types dropdown', () => (
         <DataTable
             setProps={setProps}
             id='types dropdown'
@@ -121,4 +119,5 @@ storiesOf('DashTable/Types', module)
                 }
             }}
         />
-    ));
+    </div>
+));
diff --git a/components/dash-table/tests/visual/percy-storybook/Width.all.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Width.all.percy.tsx
--- a/components/dash-table/tests/visual/percy-storybook/Width.all.percy.tsx
+++ b/components/dash-table/tests/visual/percy-storybook/Width.all.percy.tsx
@@ -19,7 +19,53 @@ const data = (() => {
     );
 })();
 
-const baseProps = {
+const basePropsDflts = {
+    setProps,
+    fill_width: false,
+    id: 'table',
+    data
+};
+
+const propsDflts = Object.assign({}, basePropsDflts, {
+    columns: columns.map(id => ({id: id, name: id.toUpperCase()}))
+});
+
+const basePropsWidth = {
+    setProps,
+    fill_width: false,
+    id: 'table',
+    data
+};
+
+const propsWidth = Object.assign({}, basePropsWidth, {
+    columns: columns.map(id => ({id: id, name: id.toUpperCase(), width: 20}))
+});
+
+const basePropsMax = {
+    setProps,
+    fill_width: false,
+    id: 'table',
+    data,
+    style_data_conditional: [{max_width: 10}]
+};
+
+const propsMax = Object.assign({}, basePropsMax, {
+    columns: columns.map(id => ({id: id, name: id.toUpperCase()}))
+});
+
+const basePropsMin = {
+    setProps,
+    fill_width: false,
+    id: 'table',
+    data
+};
+
+const propsMin = Object.assign({}, basePropsMin, {
+    columns: columns.map(id => ({id: id, name: id.toUpperCase()})),
+    style_data_conditional: [{min_width: 100}]
+});
+
+const basePropsAll = {
     setProps,
     id: 'table',
     data,
@@ -29,22 +75,80 @@ const baseProps = {
     ]
 };
 
-const props = Object.assign({}, baseProps, {
+const propsAll = Object.assign({}, basePropsAll, {
     columns: columns.map(id => ({id: id, name: id.toUpperCase()}))
 });
 
-storiesOf('DashTable/Width width, minWidth, maxWidth', module)
-    .add('without frozen columns or rows', () => <DataTable {...props} />)
-    .add('with frozen rows', () => (
-        <DataTable {...props} fixed_rows={{headers: true}} />
-    ))
-    .add('with frozen columns', () => (
-        <DataTable {...props} fixed_columns={{headers: true}} />
-    ))
-    .add('with frozen rows and frozen columns', () => (
-        <DataTable
-            {...props}
-            fixed_columns={{headers: true}}
-            fixed_rows={{headers: true}}
-        />
-    ));
+const basePropsPct = {
+    setProps,
+    id: 'table',
+    data
+};
+
+const propsPct = Object.assign({}, basePropsPct, {
+    columns: columns.map(id => ({id: id, name: id.toUpperCase()})),
+    style_cell: {
+        width: '33%'
+    },
+    style_table: {
+        width: '100%',
+        min_width: '100%',
+        max_width: '100%'
+    },
+    css: [
+        {
+            selector: '.dash-fixed-column',
+            rule: 'width: 33%;'
+        }
+    ]
+});
+
+const makeVariants =
+    (
+        title: string,
+        props:
+            | ({
+                  setProps: () => void;
+                  id: string;
+                  data: any[];
+                  fill_width: boolean;
+                  style_data_conditional: {
+                      width: string;
+                      min_width: string;
+                      max_width: string;
+                  }[];
+              } & {columns: {id: string; name: string}[]})
+            | (JSX.IntrinsicAttributes &
+                  JSX.IntrinsicClassAttributes<DataTable> &
+                  Readonly<any> &
+                  Readonly<{children?: React.ReactNode}>)
+    ) =>
+    () =>
+        (
+            <div>
+                <div style={{fontWeight: 'bold'}}>{title}</div>
+                <div>without frozen columns or rows</div>
+                <DataTable {...props} />
+                <div>with frozen rows</div>
+                <DataTable {...props} fixed_rows={{headers: true}} />
+                <div>with frozen columns</div>
+                <DataTable {...props} fixed_columns={{headers: true}} />
+                <div>with frozen rows and frozen columns</div>
+                <DataTable
+                    {...props}
+                    fixed_columns={{headers: true}}
+                    fixed_rows={{headers: true}}
+                />
+            </div>
+        );
+
+storiesOf('DashTable/Width -', module)
+    .add('defaults', makeVariants('defaults', propsDflts))
+    .add('width only', makeVariants('width only', propsWidth))
+    .add('maxWidth only', makeVariants('maxWidth only', propsMax))
+    .add('minWidth only', makeVariants('minWidth only', propsMin))
+    .add(
+        'width, minWidth, maxWidth',
+        makeVariants('width, minWidth, maxWidth', propsAll)
+    )
+    .add('percentage', makeVariants('percentage', propsPct));
diff --git a/components/dash-table/tests/visual/percy-storybook/Width.defaults.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Width.defaults.percy.tsx
deleted file mode 100644
--- a/components/dash-table/tests/visual/percy-storybook/Width.defaults.percy.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import * as R from 'ramda';
-import React from 'react';
-import {storiesOf} from '@storybook/react';
-import random from 'core/math/random';
-import DataTable from 'dash-table/dash/DataTable';
-
-const setProps = () => {};
-
-const columns = ['a', 'b', 'c'];
-
-const data = (() => {
-    const r = random(1);
-
-    return R.range(0, 5).map(() =>
-        ['a', 'b', 'c'].reduce((obj: any, key) => {
-            obj[key] = Math.floor(r() * 1000);
-            return obj;
-        }, {})
-    );
-})();
-
-const baseProps = {
-    setProps,
-    fill_width: false,
-    id: 'table',
-    data
-};
-
-const props = Object.assign({}, baseProps, {
-    columns: columns.map(id => ({id: id, name: id.toUpperCase()}))
-});
-
-storiesOf('DashTable/Width defaults', module)
-    .add('without frozen columns or rows', () => <DataTable {...props} />)
-    .add('with frozen rows', () => (
-        <DataTable {...props} fixed_rows={{headers: true}} />
-    ))
-    .add('with frozen columns', () => (
-        <DataTable {...props} fixed_columns={{headers: true}} />
-    ))
-    .add('with frozen rows and frozen columns', () => (
-        <DataTable
-            {...props}
-            fixed_columns={{headers: true}}
-            fixed_rows={{headers: true}}
-        />
-    ));
diff --git a/components/dash-table/tests/visual/percy-storybook/Width.empty.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Width.empty.percy.tsx
--- a/components/dash-table/tests/visual/percy-storybook/Width.empty.percy.tsx
+++ b/components/dash-table/tests/visual/percy-storybook/Width.empty.percy.tsx
@@ -34,33 +34,33 @@ const props = Object.assign({}, baseProps, {
     columns: columns.map(id => ({id: id, name: id.toUpperCase()}))
 });
 
-storiesOf('DashTable/Empty', module)
-    .add('with column filters -- no query', () => <DataTable {...props} />)
-    .add('with column filters -- invalid query', () => (
+storiesOf('DashTable/Empty', module).add('all variants', () => (
+    <div>
+        <div>with column filters -- no query</div>
+        <DataTable {...props} />
+        <div>with column filters -- invalid query</div>
         <DataTable
             {...R.merge(props, {
                 filter_query: '{a} !'
             })}
         />
-    ))
-    .add('with column filters -- single query', () => (
+        <div>with column filters -- single query</div>
         <DataTable
             {...R.merge(props, {
                 filter_query: '{a} ge 0'
             })}
         />
-    ))
-    .add('with column filters -- multi query', () => (
+        <div>with column filters -- multi query</div>
         <DataTable
             {...R.merge(props, {
                 filter_query: '{a} ge 0 && {b} ge 0'
             })}
         />
-    ))
-    .add('with column filters -- multi query, no data', () => (
+        <div>with column filters -- multi query, no data</div>
         <DataTable
             {...R.merge(props, {
                 filter_query: '{a} gt 1000 && {b} gt 1000'
             })}
         />
-    ));
+    </div>
+));
diff --git a/components/dash-table/tests/visual/percy-storybook/Width.max.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Width.max.percy.tsx
deleted file mode 100644
--- a/components/dash-table/tests/visual/percy-storybook/Width.max.percy.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import * as R from 'ramda';
-import React from 'react';
-import {storiesOf} from '@storybook/react';
-import random from 'core/math/random';
-import DataTable from 'dash-table/dash/DataTable';
-
-const setProps = () => {};
-
-const columns = ['a', 'b', 'c'];
-
-const data = (() => {
-    const r = random(1);
-
-    return R.range(0, 5).map(() =>
-        ['a', 'b', 'c'].reduce((obj: any, key) => {
-            obj[key] = Math.floor(r() * 1000);
-            return obj;
-        }, {})
-    );
-})();
-
-const baseProps = {
-    setProps,
-    fill_width: false,
-    id: 'table',
-    data,
-    style_data_conditional: [{max_width: 10}]
-};
-
-const props = Object.assign({}, baseProps, {
-    columns: columns.map(id => ({id: id, name: id.toUpperCase()}))
-});
-
-storiesOf('DashTable/Width maxWidth only', module)
-    .add('without frozen columns or rows', () => <DataTable {...props} />)
-    .add('with frozen rows', () => (
-        <DataTable {...props} fixed_rows={{headers: true}} />
-    ))
-    .add('with frozen columns', () => (
-        <DataTable {...props} fixed_columns={{headers: true}} />
-    ))
-    .add('with frozen rows and frozen columns', () => (
-        <DataTable
-            {...props}
-            fixed_columns={{headers: true}}
-            fixed_rows={{headers: true}}
-        />
-    ));
diff --git a/components/dash-table/tests/visual/percy-storybook/Width.min.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Width.min.percy.tsx
deleted file mode 100644
--- a/components/dash-table/tests/visual/percy-storybook/Width.min.percy.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import * as R from 'ramda';
-import React from 'react';
-import {storiesOf} from '@storybook/react';
-import random from 'core/math/random';
-import DataTable from 'dash-table/dash/DataTable';
-
-const setProps = () => {};
-
-const columns = ['a', 'b', 'c'];
-
-const data = (() => {
-    const r = random(1);
-
-    return R.range(0, 5).map(() =>
-        ['a', 'b', 'c'].reduce((obj: any, key) => {
-            obj[key] = Math.floor(r() * 1000);
-            return obj;
-        }, {})
-    );
-})();
-
-const baseProps = {
-    setProps,
-    fill_width: false,
-    id: 'table',
-    data
-};
-
-const props = Object.assign({}, baseProps, {
-    columns: columns.map(id => ({id: id, name: id.toUpperCase()})),
-    style_data_conditional: [{min_width: 100}]
-});
-
-storiesOf('DashTable/Width minWidth only', module)
-    .add('without frozen columns or rows', () => <DataTable {...props} />)
-    .add('with frozen rows', () => (
-        <DataTable {...props} fixed_rows={{headers: true}} />
-    ))
-    .add('with frozen columns', () => (
-        <DataTable {...props} fixed_columns={{headers: true}} />
-    ))
-    .add('with frozen rows and frozen columns', () => (
-        <DataTable
-            {...props}
-            fixed_columns={{headers: true}}
-            fixed_rows={{headers: true}}
-        />
-    ));
diff --git a/components/dash-table/tests/visual/percy-storybook/Width.percentages.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Width.percentages.percy.tsx
deleted file mode 100644
--- a/components/dash-table/tests/visual/percy-storybook/Width.percentages.percy.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import * as R from 'ramda';
-import React from 'react';
-import {storiesOf} from '@storybook/react';
-import random from 'core/math/random';
-import DataTable from 'dash-table/dash/DataTable';
-
-const setProps = () => {};
-
-const columns = ['a', 'b', 'c'];
-
-const data = (() => {
-    const r = random(1);
-
-    return R.range(0, 5).map(() =>
-        ['a', 'b', 'c'].reduce((obj: any, key) => {
-            obj[key] = Math.floor(r() * 1000);
-            return obj;
-        }, {})
-    );
-})();
-
-const baseProps = {
-    setProps,
-    id: 'table',
-    data
-};
-
-const props = Object.assign({}, baseProps, {
-    columns: columns.map(id => ({id: id, name: id.toUpperCase()})),
-    style_cell: {
-        width: '33%'
-    },
-    style_table: {
-        width: '100%',
-        min_width: '100%',
-        max_width: '100%'
-    },
-    css: [
-        {
-            selector: '.dash-fixed-column',
-            rule: 'width: 33%;'
-        }
-    ]
-});
-
-storiesOf('DashTable/Width percentages', module)
-    .add('without frozen columns or rows', () => <DataTable {...props} />)
-    .add('with frozen rows', () => (
-        <DataTable {...props} fixed_rows={{headers: true}} />
-    ))
-    .add('with frozen columns', () => (
-        <DataTable {...props} fixed_columns={{headers: true}} />
-    ))
-    .add('with frozen rows and frozen columns', () => (
-        <DataTable
-            {...props}
-            fixed_columns={{headers: true}}
-            fixed_rows={{headers: true}}
-        />
-    ));
diff --git a/components/dash-table/tests/visual/percy-storybook/Width.width.percy.tsx b/components/dash-table/tests/visual/percy-storybook/Width.width.percy.tsx
deleted file mode 100644
--- a/components/dash-table/tests/visual/percy-storybook/Width.width.percy.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import * as R from 'ramda';
-import React from 'react';
-import {storiesOf} from '@storybook/react';
-import random from 'core/math/random';
-import DataTable from 'dash-table/dash/DataTable';
-
-const setProps = () => {};
-
-const columns = ['a', 'b', 'c'];
-
-const data = (() => {
-    const r = random(1);
-
-    return R.range(0, 5).map(() =>
-        ['a', 'b', 'c'].reduce((obj: any, key) => {
-            obj[key] = Math.floor(r() * 1000);
-            return obj;
-        }, {})
-    );
-})();
-
-const baseProps = {
-    setProps,
-    fill_width: false,
-    id: 'table',
-    data
-};
-
-const props = Object.assign({}, baseProps, {
-    columns: columns.map(id => ({id: id, name: id.toUpperCase(), width: 20}))
-});
-
-storiesOf('DashTable/Width width only', module)
-    .add('without frozen columns or rows', () => <DataTable {...props} />)
-    .add('with frozen rows', () => (
-        <DataTable {...props} fixed_rows={{headers: true}} />
-    ))
-    .add('with frozen columns', () => (
-        <DataTable {...props} fixed_columns={{headers: true}} />
-    ))
-    .add('with frozen rows and frozen columns', () => (
-        <DataTable
-            {...props}
-            fixed_columns={{headers: true}}
-            fixed_rows={{headers: true}}
-        />
-    ));
diff --git a/dash/testing/browser.py b/dash/testing/browser.py
--- a/dash/testing/browser.py
+++ b/dash/testing/browser.py
@@ -20,10 +20,6 @@
     MoveTargetOutOfBoundsException,
 )
 
-from webdriver_manager.chrome import ChromeDriverManager
-from webdriver_manager.utils import ChromeType
-from webdriver_manager.firefox import GeckoDriverManager
-
 from dash.testing.wait import text_to_equal, style_to_equal, contains_text, until
 from dash.testing.dash_page import DashPageMixin
 from dash.testing.errors import DashAppLoadingError, BrowserError, TestingTimeoutError
@@ -477,11 +473,7 @@ def _get_chrome(self):
                 desired_capabilities=capabilities,
             )
             if self._remote
-            else webdriver.Chrome(
-                ChromeDriverManager(chrome_type=ChromeType.GOOGLE).install(),
-                options=options,
-                desired_capabilities=capabilities,
-            )
+            else webdriver.Chrome(options=options, desired_capabilities=capabilities)
         )
 
         # https://bugs.chromium.org/p/chromium/issues/detail?id=696481
@@ -524,10 +516,7 @@ def _get_firefox(self):
             )
             if self._remote
             else webdriver.Firefox(
-                executable_path=GeckoDriverManager().install(),
-                firefox_profile=fp,
-                options=options,
-                capabilities=capabilities,
+                firefox_profile=fp, options=options, capabilities=capabilities
             )
         )