Skip to content

Commit

Permalink
Clean accordion with new upload (#29)
Browse files Browse the repository at this point in the history
* clean the accordion when a new file is uploaded
  • Loading branch information
gcroci2 authored Aug 14, 2024
1 parent d63ffe7 commit 21766b9
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 118 deletions.
176 changes: 114 additions & 62 deletions app/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,33 +78,85 @@ def upload_data(status: du.UploadStatus) -> tuple[str, str | None]:
Output("gm-tab", "disabled"),
Output("gm-accordion-control", "disabled"),
Output("mg-tab", "disabled"),
Output("blocks-id", "data", allow_duplicate=True),
Output("blocks-container", "children", allow_duplicate=True),
],
[Input("file-store", "data")],
prevent_initial_call=True,
)
def disable_tabs(file_name: str | None) -> tuple[bool, bool, bool]:
"""Disable the tabs when no file is uploaded.
def disable_tabs_and_reset_blocks(
file_name: str | None,
) -> tuple[bool, bool, bool, list[str], list[dict[str, Any]]]:
"""Manage tab states and reset blocks based on file upload status.
This function controls the accessibility of the following UI elements:
1. The "Genomics -> Metabolomics" tab
2. The "Genomics filter" accordion control within the GM tab
3. The "Metabolomics -> Genomics" tab
Args:
file_name: The name of the uploaded file, or None if no file is uploaded.
Returns:
A tuple containing:
- Boolean values for disabling gm-tab, gm-accordion-control, and mg-tab.
- A list with a single block ID.
- A list with a single block component.
"""
if file_name is None:
# Disable the tabs, don't change blocks
return True, True, True, [], []

When no file is uploaded, all these elements are disabled to prevent user interaction.
Once a file is successfully uploaded, these elements become enabled, allowing the user
to interact with the application's features.
# Enable the tabs and reset blocks
initial_block_id = [str(uuid.uuid4())]
new_blocks = [create_initial_block(initial_block_id[0])]

return False, False, False, initial_block_id, new_blocks


def create_initial_block(block_id: str) -> dmc.Grid:
"""Create the initial block component with the given ID.
Args:
file_name: The name of the uploaded file.
block_id: A unique identifier for the block.
Returns:
A tuple of boolean values indicating whether each tab should be disabled.
A dictionary representing a dmc.Grid component with nested elements.
"""
if file_name is None:
# Disable the tabs
return True, True, True
# Enable the tabs
return False, False, False
return dmc.Grid(
id={"type": "gm-block", "index": block_id},
children=[
dmc.GridCol(
dbc.Button(
[html.I(className="fas fa-plus")],
id={"type": "gm-add-button", "index": block_id},
className="btn-primary",
),
span=1,
),
dmc.GridCol(
dcc.Dropdown(
options=GM_DROPDOWN_MENU_OPTIONS,
value="GCF_ID",
id={"type": "gm-dropdown-menu", "index": block_id},
clearable=False,
),
span=6,
),
dmc.GridCol(
[
dmc.TextInput(
id={"type": "gm-dropdown-ids-text-input", "index": block_id},
placeholder="1, 2, 3, ...",
className="custom-textinput",
),
dcc.Dropdown(
id={"type": "gm-dropdown-bgc-class-dropdown", "index": block_id},
options=GM_DROPDOWN_BGC_CLASS_OPTIONS,
multi=True,
style={"display": "none"},
),
],
span=5,
),
],
gutter="md",
)


@app.callback(
Expand Down Expand Up @@ -186,9 +238,7 @@ def add_block(n_clicks: list[int], blocks_id: list[str]) -> list[str]:
Input("blocks-id", "data"),
State("blocks-container", "children"),
)
def display_blocks(
blocks_id: list[str], existing_blocks: list[dict[str, Any]]
) -> list[dict[str, Any]]:
def display_blocks(blocks_id: list[str], existing_blocks: list[dmc.Grid]) -> list[dmc.Grid]:
"""Display the blocks for the input block IDs.
Args:
Expand All @@ -198,54 +248,56 @@ def display_blocks(
Returns:
Updated list of block components.
"""
new_block_id = blocks_id[-1]

new_block = dmc.Grid(
id={"type": "gm-block", "index": new_block_id},
children=[
dmc.GridCol(
dbc.Button(
[html.I(className="fas fa-plus")],
id={"type": "gm-add-button", "index": new_block_id},
className="btn-primary",
),
span=1,
),
dmc.GridCol(
dcc.Dropdown(
options=GM_DROPDOWN_MENU_OPTIONS,
value="GCF_ID",
id={"type": "gm-dropdown-menu", "index": new_block_id},
clearable=False,
),
span=6,
),
dmc.GridCol(
[
dmc.TextInput(
id={"type": "gm-dropdown-ids-text-input", "index": new_block_id},
placeholder="1, 2, 3, ...",
className="custom-textinput",
if len(blocks_id) > 1:
new_block_id = blocks_id[-1]

new_block = dmc.Grid(
id={"type": "gm-block", "index": new_block_id},
children=[
dmc.GridCol(
dbc.Button(
[html.I(className="fas fa-plus")],
id={"type": "gm-add-button", "index": new_block_id},
className="btn-primary",
),
span=1,
),
dmc.GridCol(
dcc.Dropdown(
id={"type": "gm-dropdown-bgc-class-dropdown", "index": new_block_id},
options=GM_DROPDOWN_BGC_CLASS_OPTIONS,
multi=True,
style={"display": "none"},
options=GM_DROPDOWN_MENU_OPTIONS,
value="GCF_ID",
id={"type": "gm-dropdown-menu", "index": new_block_id},
clearable=False,
),
],
span=5,
),
],
gutter="md",
)
span=6,
),
dmc.GridCol(
[
dmc.TextInput(
id={"type": "gm-dropdown-ids-text-input", "index": new_block_id},
placeholder="1, 2, 3, ...",
className="custom-textinput",
),
dcc.Dropdown(
id={"type": "gm-dropdown-bgc-class-dropdown", "index": new_block_id},
options=GM_DROPDOWN_BGC_CLASS_OPTIONS,
multi=True,
style={"display": "none"},
),
],
span=5,
),
],
gutter="md",
)

# Hide the add button on the previous last block
existing_blocks[-1]["props"]["children"][0]["props"]["children"]["props"]["style"] = {
"display": "none"
}
# Hide the add button on the previous last block
existing_blocks[-1]["props"]["children"][0]["props"]["children"]["props"]["style"] = {
"display": "none"
}

return existing_blocks + [new_block]
return existing_blocks + [new_block]
return existing_blocks


@app.callback(
Expand Down
53 changes: 2 additions & 51 deletions app/layouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
import dash_uploader as du
from config import GM_DROPDOWN_BGC_CLASS_OPTIONS
from config import GM_DROPDOWN_MENU_OPTIONS
from dash import dcc
from dash import html

Expand Down Expand Up @@ -77,57 +75,10 @@
initial_block_id = str(uuid.uuid4())
gm_input_group = html.Div(
[
dcc.Store(id="blocks-id", data=[initial_block_id]), # Start with one block
dcc.Store(id="blocks-id", data=[]), # Start with one block
html.Div(
id="blocks-container",
children=[
dmc.Grid(
id={"type": "gm-block", "index": initial_block_id}, # Start with one block
children=[
dmc.GridCol(
dbc.Button(
[html.I(className="fas fa-plus")],
id={"type": "gm-add-button", "index": initial_block_id},
className="btn-primary",
),
span=1,
),
dmc.GridCol(
dcc.Dropdown(
options=GM_DROPDOWN_MENU_OPTIONS,
value="GCF_ID",
id={"type": "gm-dropdown-menu", "index": initial_block_id},
clearable=False,
),
span=6,
),
dmc.GridCol(
[
dmc.TextInput(
id={
"type": "gm-dropdown-ids-text-input",
"index": initial_block_id,
},
placeholder="1, 2, 3, ...",
className="custom-textinput",
),
dcc.Dropdown(
id={
"type": "gm-dropdown-bgc-class-dropdown",
"index": initial_block_id,
},
options=GM_DROPDOWN_BGC_CLASS_OPTIONS,
placeholder="Select one or more BGC classes",
multi=True,
style={"display": "none"},
),
],
span=5,
),
],
gutter="md",
)
],
children=[],
),
]
)
Expand Down
16 changes: 11 additions & 5 deletions tests/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from dash_uploader import UploadStatus
from app.callbacks import add_block
from app.callbacks import disable_tabs
from app.callbacks import disable_tabs_and_reset_blocks
from app.callbacks import upload_data
from . import DATA_DIR

Expand All @@ -25,14 +25,20 @@ def test_upload_data():

def test_disable_tabs():
# Test with None as input
result = disable_tabs(None)
result = disable_tabs_and_reset_blocks(None)
assert result[0] is True # GM tab should be disabled
assert result[1] is True # MG tab should be disabled
assert result[1] is True # GM accordion should be disabled
assert result[2] is True # MG tab should be disabled
assert result[3] == [] # No blocks should be displayed
assert result[4] == [] # No blocks should be displayed

# Test with a string as input
result = disable_tabs(MOCK_FILE_PATH)
result = disable_tabs_and_reset_blocks(MOCK_FILE_PATH)
assert result[0] is False # GM tab should be enabled
assert result[1] is False # MG tab should be enabled
assert result[1] is False # GM accordion should be enabled
assert result[2] is False # MG tab should be enabled
assert len(result[3]) == 1 # One block should be displayed
assert len(result[4]) == 1 # One block should be displayed


@pytest.fixture
Expand Down

0 comments on commit 21766b9

Please sign in to comment.