Skip to content

Commit

Permalink
Merge pull request #301 from AnnMarieW/fix-291
Browse files Browse the repository at this point in the history
fix for #291
  • Loading branch information
snehilvj authored Sep 7, 2024
2 parents 3d19642 + 41ad0c7 commit fb2b7b6
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 8 deletions.
138 changes: 138 additions & 0 deletions pr-examples/pr-291.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import dash_mantine_components as dmc
import dash
from dash import dcc, Input, Output, callback


dash._dash_renderer._set_react_version("18.2.0")
app = dash.Dash(__name__, prevent_initial_callbacks=True)

all_types = {L: [f"{L}{i}" for i in range(5)] for L in ["A", "B", "C", "D"]}

all_types_label_value = {
L: [{"label": f"Label {L}{i}", "value": f"{L}{i}"} for i in range(5)]
for L in ["A", "B", "C", "D"]
}


all_types_list = [f"{L}{i}" for L in ["A", "B", "C", "D"] for i in range(5)]


app.layout = dmc.MantineProvider(
dmc.Container(
[
dcc.Markdown(
"""
In Issue #291 the values were not updated correctly when data is updated in a callback.
This PR verifies that #291 is fixed for all valid formats of the `data` prop
The `data` in dropdowns can be either:
1. an array of strings - use when label and value are same.
2. an array of dicts with label and value properties.
3. an array of dict with group and items as keys where items are one of the previous two types.
"""
),
dmc.Text("STEP 1", my=10),
dmc.MultiSelect(
w="47%",
searchable=True,
hidePickedOptions=True,
clearable=True,
label="Select Category",
id="n_search_in_category",
data=["A", "B", "C", "D"],
# value=[], Checking for value is none closes PR
),
dmc.Text("STEP 2 Verify that all the dropdowns below update the values correctly regardless of the format of the `data`.", my=20),
dmc.MultiSelect(
w="47%",
searchable=True,
hidePickedOptions=True,
clearable=True,
label="1. Data is array of strings",
id="n_search_in_types",
# value=[],
mb=60,
),
dmc.MultiSelect(
w="47%",
searchable=True,
hidePickedOptions=True,
clearable=True,
label="2. Data is dict with labels and values",
id="n_search_in_types_dict",
# value=[],
mb=60,
),
dmc.MultiSelect(
w="47%",
searchable=True,
hidePickedOptions=True,
clearable=True,
label="3a. Data is dict with group and items keys, where items is array of strings",
id="n_search_in_types_groups",
# value=[],
mb=60,
),
dmc.MultiSelect(
w="47%",
searchable=True,
hidePickedOptions=True,
clearable=True,
label="3b. Data is dict with group and items keys, where items is a dict with labels and values",
id="n_search_in_types_groups_3b",
# value=[],
mb=60,
),
], mb=200
)
)


# 1. Data is array of strings
@callback(
Output("n_search_in_types", "data"),
Input("n_search_in_category", "value"),
)
def add_types_in_search(category_id):
if category_id is None:
return dash.no_update
return [i for i in all_types_list if i[0] in category_id]


# 2. Data is dict with labels and values
@callback(
Output("n_search_in_types_dict", "data"),
Input("n_search_in_category", "value"),
)
def add_types_in_search(category_id):
if category_id is None:
return dash.no_update
return [item for L in category_id for item in all_types_label_value[L]]


# 3a. Data is dict with group and items keys, where items are an array
@callback(
Output("n_search_in_types_groups", "data"), Input("n_search_in_category", "value")
)
def add_types_in_search(category_id):
if category_id is None:
return dash.no_update
return [{"group": L, "items": all_types[L]} for L in category_id]


# 3b. Data is dict with group and items keys, where items is a dict with label and value
@callback(
Output("n_search_in_types_groups_3b", "data"),
Input("n_search_in_category", "value"),
)
def add_types_in_search(category_id):
if category_id is None:
return dash.no_update
return [{"group": L, "items": all_types_label_value[L]} for L in category_id]


if __name__ == "__main__":
app.run(debug=True)
33 changes: 25 additions & 8 deletions src/ts/utils/combobox.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
export const filterSelected = (options, values) => {
if (options.length === 0 || values.length === 0) return [];
if (!options || !values || options.length === 0 || values.length === 0) return [];

if (typeof options[0] === "string") {
return values.filter((value) => options.includes(value));
} else if (typeof options[0] === "object") {
const optionValues = options.map((option) => option.value);
return values.filter((value) => optionValues.includes(value));
}
const extractValues = (optionList) => {
let extractedValues = [];

return [];
for (const option of optionList) {
if (typeof option === "string") {
// Case 1: option is a string
extractedValues.push(option);
} else if ('value' in option && 'label' in option) {
// Case 2: option is an object with label and value
extractedValues.push(option.value);
} else if ('group' in option && 'items' in option) {
// Case 3: option is a group with nested items, recursively extract values
extractedValues = extractedValues.concat(extractValues(option.items));
}
}

return extractedValues;
};

// Extract all valid option values
const optionValues = extractValues(options);

// Return filtered values based on extracted option values
return values.filter((value) => optionValues.includes(value));
};


export const isInOption = (options, value) => {
if (options.length === 0) return false;

Expand Down

0 comments on commit fb2b7b6

Please sign in to comment.