Repository: plotly/dash

Issue:
[BUG] Dropdown:  Selected options not showing when the `value` contains a comma 
As [reported on the forum by @ marcus](https://community.plotly.com/t/dcc-dropdown-bug-suspected-please-confirm-or-correct-me/60585):

Problem is in this spot: 'value': "50 , 10"
Specifically comma in `value` string makes that selection is not being displayed. When coma is removed or replaced (dot creates no issue) it works fine, but when coma is within the string, the selections are not displayed in the input box and are not removed from the `options`

 This only occurs in Multi-Value dropdowns.  This is new in Dash V2.1.0  It works as expected in V2.0.0




```
from dash import Dash, dcc, html, Input, Output

app = Dash(__name__)

app.layout = html.Div(
    [
        dcc.Dropdown(
            id="dropdown",
            className="inputbox-long",
            options=[
                {"label": "New York City", "value": "50, 10"},
                {"label": "Montreal", "value": "MTL" },
                {"label": "San Francisco", "value": "SF"},
            ],
            placeholder="Select one or more",
            multi=True,
        ),
        html.Div(id="output"),
    ]
)


@app.callback(
    Output("output", "children"), Input("dropdown", "value"),
)
def update(value):    
    return value


if __name__ == "__main__":
    app.run_server(debug=True)
```
![dropdown_bug](https://user-images.githubusercontent.com/72614349/152015862-8680cc37-dc44-4ebd-b6c5-32975ee08e98.gif)



Discussion:
Yikes, yeah this bug is breaking my covid dashboard when I upgraded to 2.2 (so still there not just 2.1) from 2.0. I have a Dropdown with County,State pairs: .e.g. `Harris, Texas`. It's all broken now.

This is a pretty bad breaking bug, can we get someone assigned to this or otherwise are there any updates? I have reverted to 2.0 for now.


Resolving patch:
diff --git a/components/dash-html-components/dash_html_components_base/__init__.py b/components/dash-html-components/dash_html_components_base/__init__.py
--- a/components/dash-html-components/dash_html_components_base/__init__.py
+++ b/components/dash-html-components/dash_html_components_base/__init__.py
@@ -33,27 +33,27 @@
 
 _js_dist = [
     {
-        "relative_package_path": 'html/{}.min.js'.format(_this_module),
+        "relative_package_path": "html/{}.min.js".format(_this_module),
         "external_url": (
             "https://unpkg.com/dash-html-components@{}"
             "/dash_html_components/dash_html_components.min.js"
         ).format(__version__),
-        "namespace": "dash"
+        "namespace": "dash",
     },
     {
-        'relative_package_path': 'html/{}.min.js.map'.format(_this_module),
-        'external_url': (
-            'https://unpkg.com/dash-html-components@{}'
-            '/dash_html_components/dash_html_components.min.js.map'
+        "relative_package_path": "html/{}.min.js.map".format(_this_module),
+        "external_url": (
+            "https://unpkg.com/dash-html-components@{}"
+            "/dash_html_components/dash_html_components.min.js.map"
         ).format(__version__),
-        'namespace': 'dash',
-        'dynamic': True
-    }
+        "namespace": "dash",
+        "dynamic": True,
+    },
 ]
 
 _css_dist = []
 
 
 for _component in __all__:
-    setattr(locals()[_component], '_js_dist', _js_dist)
-    setattr(locals()[_component], '_css_dist', _css_dist)
+    setattr(locals()[_component], "_js_dist", _js_dist)
+    setattr(locals()[_component], "_css_dist", _css_dist)
diff --git a/components/dash-html-components/setup.py b/components/dash-html-components/setup.py
--- a/components/dash-html-components/setup.py
+++ b/components/dash-html-components/setup.py
@@ -2,22 +2,22 @@
 import json
 from setuptools import setup
 
-with open('package.json') as f:
+with open("package.json") as f:
     package = json.load(f)
 
 package_name = str(package["name"].replace(" ", "_").replace("-", "_"))
 
 setup(
-    name='dash_html_components',
+    name="dash_html_components",
     version=package["version"],
-    author=package['author'],
-    author_email='chris@plotly.com',
+    author=package["author"],
+    author_email="chris@plotly.com",
     packages=[package_name],
-    url='https://github.com/plotly/dash-html-components',
+    url="https://github.com/plotly/dash-html-components",
     include_package_data=True,
-    license=package['license'],
-    description=package['description'] if 'description' in package else package_name,
-    long_description=io.open('README.md', encoding='utf-8').read(),
-    long_description_content_type='text/markdown',
-    install_requires=[]
+    license=package["license"],
+    description=package["description"] if "description" in package else package_name,
+    long_description=io.open("README.md", encoding="utf-8").read(),
+    long_description_content_type="text/markdown",
+    install_requires=[],
 )


Test patch:
diff --git a/components/dash-core-components/tests/integration/dropdown/test_dynamic_options.py b/components/dash-core-components/tests/integration/dropdown/test_dynamic_options.py
--- a/components/dash-core-components/tests/integration/dropdown/test_dynamic_options.py
+++ b/components/dash-core-components/tests/integration/dropdown/test_dynamic_options.py
@@ -52,24 +52,18 @@ def update_options(search_value):
     assert dash_dcc.get_logs() == []
 
 
-def test_dddo002_array_value(dash_dcc):
-    dropdown_options = [
-        {"label": "New York City", "value": "New,York,City"},
-        {"label": "Montreal", "value": "Montreal"},
-        {"label": "San Francisco", "value": "San,Francisco"},
-    ]
-
+def test_dddo002_array_comma_value(dash_dcc):
     app = Dash(__name__)
-    arrayValue = ["San", "Francisco"]
 
     dropdown = dcc.Dropdown(
-        options=dropdown_options,
-        value=arrayValue,
+        options=["New York, NY", "Montreal, QC", "San Francisco, CA"],
+        value=["San Francisco, CA"],
+        multi=True,
     )
-    app.layout = html.Div([dropdown])
+    app.layout = html.Div(dropdown)
 
     dash_dcc.start_server(app)
 
-    dash_dcc.wait_for_text_to_equal("#react-select-2--value-item", "San Francisco")
+    dash_dcc.wait_for_text_to_equal("#react-select-2--value-0", "San Francisco, CA\n ")
 
     assert dash_dcc.get_logs() == []
diff --git a/components/dash-core-components/tests/integration/dropdown/test_remove_option.py b/components/dash-core-components/tests/integration/dropdown/test_remove_option.py
new file mode 100644
--- /dev/null
+++ b/components/dash-core-components/tests/integration/dropdown/test_remove_option.py
@@ -0,0 +1,84 @@
+import json
+
+from dash import Dash, html, dcc, Output, Input
+from dash.exceptions import PreventUpdate
+
+
+sample_dropdown_options = [
+    {"label": "New York City", "value": "NYC"},
+    {"label": "Montreal", "value": "MTL"},
+    {"label": "San Francisco", "value": "SF"},
+]
+
+
+def test_ddro001_remove_option_single(dash_dcc):
+    dropdown_options = sample_dropdown_options
+
+    app = Dash(__name__)
+    value = "SF"
+
+    app.layout = html.Div(
+        [
+            dcc.Dropdown(
+                options=dropdown_options,
+                value=value,
+                id="dropdown",
+            ),
+            html.Button("Remove option", id="remove"),
+            html.Div(id="value-output"),
+        ]
+    )
+
+    @app.callback(Output("dropdown", "options"), [Input("remove", "n_clicks")])
+    def on_click(n_clicks):
+        if not n_clicks:
+            raise PreventUpdate
+        return sample_dropdown_options[:-1]
+
+    @app.callback(Output("value-output", "children"), [Input("dropdown", "value")])
+    def on_change(val):
+        if not val:
+            raise PreventUpdate
+        return val or "None"
+
+    dash_dcc.start_server(app)
+    btn = dash_dcc.wait_for_element("#remove")
+    btn.click()
+
+    dash_dcc.wait_for_text_to_equal("#value-output", "None")
+
+
+def test_ddro002_remove_option_multi(dash_dcc):
+    dropdown_options = sample_dropdown_options
+
+    app = Dash(__name__)
+    value = ["MTL", "SF"]
+
+    app.layout = html.Div(
+        [
+            dcc.Dropdown(
+                options=dropdown_options,
+                value=value,
+                multi=True,
+                id="dropdown",
+            ),
+            html.Button("Remove option", id="remove"),
+            html.Div(id="value-output"),
+        ]
+    )
+
+    @app.callback(Output("dropdown", "options"), [Input("remove", "n_clicks")])
+    def on_click(n_clicks):
+        if not n_clicks:
+            raise PreventUpdate
+        return sample_dropdown_options[:-1]
+
+    @app.callback(Output("value-output", "children"), [Input("dropdown", "value")])
+    def on_change(val):
+        return json.dumps(val)
+
+    dash_dcc.start_server(app)
+    btn = dash_dcc.wait_for_element("#remove")
+    btn.click()
+
+    dash_dcc.wait_for_text_to_equal("#value-output", '["MTL"]')
diff --git a/components/dash-html-components/tests/test_dash_html_components.py b/components/dash-html-components/tests/test_dash_html_components.py
--- a/components/dash-html-components/tests/test_dash_html_components.py
+++ b/components/dash-html-components/tests/test_dash_html_components.py
@@ -23,7 +23,7 @@ def test_imports():
 def test_sample_items():
     layout = html.Div(
         html.Div(html.Img(src="https://plotly.com/~chris/1638.png")),
-        style={"color": "red"}
+        style={"color": "red"},
     )
 
     expected = (
diff --git a/components/dash-html-components/tests/utils.py b/components/dash-html-components/tests/utils.py
--- a/components/dash-html-components/tests/utils.py
+++ b/components/dash-html-components/tests/utils.py
@@ -4,18 +4,14 @@
 def assert_clean_console(TestClass):
     def assert_no_console_errors(TestClass):
         TestClass.assertEqual(
-            TestClass.driver.execute_script(
-                'return window.tests.console.error.length'
-            ),
-            0
+            TestClass.driver.execute_script("return window.tests.console.error.length"),
+            0,
         )
 
     def assert_no_console_warnings(TestClass):
         TestClass.assertEqual(
-            TestClass.driver.execute_script(
-                'return window.tests.console.warn.length'
-            ),
-            0
+            TestClass.driver.execute_script("return window.tests.console.warn.length"),
+            0,
         )
 
     assert_no_console_warnings(TestClass)