From 2b42029d55966567d01cfe049565c10763268984 Mon Sep 17 00:00:00 2001 From: Chuck McCallum Date: Thu, 21 Nov 2024 18:42:12 -0500 Subject: [PATCH] Hide weight if only one column selected (#170) * factor out weight input * message shows if only one column * add a test * hide the weight control * update app_test to match --- WHAT-WE-LEARNED.md | 4 +++ dp_wizard/app/analysis_panel.py | 1 + dp_wizard/app/components/column_module.py | 44 ++++++++++++++--------- dp_wizard/app/components/outputs.py | 5 +++ tests/test_app.py | 6 +++- 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/WHAT-WE-LEARNED.md b/WHAT-WE-LEARNED.md index 812b320..c21dfa2 100644 --- a/WHAT-WE-LEARNED.md +++ b/WHAT-WE-LEARNED.md @@ -2,6 +2,10 @@ Even if it seems obvious in retrospect, what have we learned about Python Shiny in this project? +## Some basic html attributes are not supported + +I can mark a button as disabled, but it doesn't seem that a `ui.input_select` can be disabled. + ## No warning if ID mismatch / type mismatch Unless I'm missing something, there doesn't seem to be any warning when there isn't a matching function name in the server for an ID in the UI. Either from typos, or fumbling some more complicated display logic, there have been times where this could have been helpful. diff --git a/dp_wizard/app/analysis_panel.py b/dp_wizard/app/analysis_panel.py index b1ca2da..0f13085 100644 --- a/dp_wizard/app/analysis_panel.py +++ b/dp_wizard/app/analysis_panel.py @@ -113,6 +113,7 @@ def columns_ui(): bin_counts=bin_counts, weights=weights, is_demo=is_demo, + is_single_column=len(column_ids) == 1, ) confidence_percent = f"{int(confidence * 100)}%" note_md = f""" diff --git a/dp_wizard/app/components/column_module.py b/dp_wizard/app/components/column_module.py index 37ece2f..89927bc 100644 --- a/dp_wizard/app/components/column_module.py +++ b/dp_wizard/app/components/column_module.py @@ -5,11 +5,11 @@ from dp_wizard.utils.dp_helper import make_accuracy_histogram from dp_wizard.utils.shared import plot_histogram from dp_wizard.utils.code_generators import make_column_config_block -from dp_wizard.app.components.outputs import output_code_sample, demo_tooltip +from dp_wizard.app.components.outputs import output_code_sample, demo_tooltip, hide_if default_weight = "2" - +label_width = "10em" # Just wide enough so the text isn't trucated. col_widths = { # Controls stay roughly a constant width; # Graph expands to fill space. @@ -21,29 +21,21 @@ @module.ui def column_ui(): # pragma: no cover - width = "10em" # Just wide enough so the text isn't trucated. return ui.layout_columns( [ # The initial values on these inputs # should be overridden by the reactive.effect. ui.input_numeric( - "lower", ["Lower", ui.output_ui("bounds_tooltip_ui")], 0, width=width + "lower", + ["Lower", ui.output_ui("bounds_tooltip_ui")], + 0, + width=label_width, ), - ui.input_numeric("upper", "Upper", 0, width=width), + ui.input_numeric("upper", "Upper", 0, width=label_width), ui.input_numeric( - "bins", ["Bins", ui.output_ui("bins_tooltip_ui")], 0, width=width - ), - ui.input_select( - "weight", - ["Weight", ui.output_ui("weight_tooltip_ui")], - choices={ - "1": "Less accurate", - default_weight: "Default", - "4": "More accurate", - }, - selected=default_weight, - width=width, + "bins", ["Bins", ui.output_ui("bins_tooltip_ui")], 0, width=label_width ), + ui.output_ui("optional_weight_ui"), ], [ ui.output_plot("column_plot", height="300px"), @@ -67,6 +59,7 @@ def column_server( bin_counts: reactive.Value[dict[str, int]], weights: reactive.Value[dict[str, str]], is_demo: bool, + is_single_column: bool, ): # pragma: no cover @reactive.effect def _set_all_inputs(): @@ -123,6 +116,23 @@ def bins_tooltip_ui(): """, ) + @render.ui + def optional_weight_ui(): + return hide_if( + is_single_column, + ui.input_select( + "weight", + ["Weight", ui.output_ui("weight_tooltip_ui")], + choices={ + "1": "Less accurate", + default_weight: "Default", + "4": "More accurate", + }, + selected=default_weight, + width=label_width, + ), + ) + @render.ui def weight_tooltip_ui(): return demo_tooltip( diff --git a/dp_wizard/app/components/outputs.py b/dp_wizard/app/components/outputs.py index 96d9d9f..cb0f4b3 100644 --- a/dp_wizard/app/components/outputs.py +++ b/dp_wizard/app/components/outputs.py @@ -17,3 +17,8 @@ def demo_tooltip(is_demo: bool, text: str): # pragma: no cover text, placement="right", ) + + +def hide_if(condition: bool, el): # pragma: no cover + display = "none" if condition else "block" + return ui.div(el, style=f"display: {display};") diff --git a/tests/test_app.py b/tests/test_app.py index 3e9e6bd..b8fe8b9 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -88,6 +88,7 @@ def expect_no_error(): # Set column details: page.get_by_label("grade").check() expect_visible(simulation) + expect_not_visible("Weight") # Check that default is set correctly: assert page.get_by_label("Upper").input_value() == "10" # Reset, and confirm: @@ -100,8 +101,11 @@ def expect_no_error(): page.get_by_label("grade").check() expect_visible(simulation) assert page.get_by_label("Upper").input_value() == new_value + # Add a second column: + page.get_by_label("blank").check() + expect_visible("Weight") # TODO: Setting more inputs without checking for updates - # cause recalculations to pile up, and these cause timeouts on CI: + # causes recalculations to pile up, and these cause timeouts on CI: # It is still rerendering the graph after hitting "Download results". # https://github.com/opendp/dp-wizard/issues/116 expect_no_error()