Skip to content

Commit

Permalink
Enable turning off marks when step is define in slider components (#115)
Browse files Browse the repository at this point in the history
Co-authored-by: huong-li-nguyen <[email protected]>
  • Loading branch information
nadijagraca and huong-li-nguyen authored Oct 18, 2023
1 parent bc835cf commit 4868535
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 44 deletions.
Binary file modified .github/images/tech_logos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!--
A new scriv changelog fragment.
Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Removed
- A bullet item for the Removed category.
-->
<!--
### Added
- A bullet item for the Added category.
-->
<!--
### Changed
- A bullet item for the Changed category.
-->
<!--
### Deprecated
- A bullet item for the Deprecated category.
-->

### Fixed

- Enable turning off `marks` when `step` is defined in `Slider` and `RangeSlider` ([#115](https://github.com/mckinsey/vizro/pull/115))

<!--
### Security
- A bullet item for the Security category.
-->
2 changes: 1 addition & 1 deletion vizro-core/examples/default/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ def create_country_analysis():
],
controls=[
vm.Filter(column="country", selector=vm.Dropdown(value="India", multi=False, title="Select country")),
vm.Filter(column="year", selector=vm.RangeSlider(title="Select timeframe")),
vm.Filter(column="year", selector=vm.RangeSlider(title="Select timeframe", step=1, marks=None)),
],
)
return page_country
Expand Down
6 changes: 4 additions & 2 deletions vizro-core/schemas/0.1.5.dev0.json
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@
},
"RangeSlider": {
"title": "RangeSlider",
"description": "Numeric multi-selector `RangeSlider`.\n\nCan be provided to [`Filter`][vizro.models.Filter] or\n[`Parameter`][vizro.models.Parameter]. Based on the underlying\n[`dcc.RangeSlider`](https://dash.plotly.com/dash-core-components/rangeslider).\n\nArgs:\n type (Literal[\"range_slider\"]): Defaults to `\"range_slider\"`.\n min (Optional[float]): Start value for slider. Defaults to `None`.\n max (Optional[float]): End value for slider. Defaults to `None`.\n step (Optional[float]): Step-size for marks on slider. Defaults to `None`.\n marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `None`.\n value (Optional[List[float]]): Default start and end value for slider. Must be 2 items. Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
"description": "Numeric multi-selector `RangeSlider`.\n\nCan be provided to [`Filter`][vizro.models.Filter] or\n[`Parameter`][vizro.models.Parameter]. Based on the underlying\n[`dcc.RangeSlider`](https://dash.plotly.com/dash-core-components/rangeslider).\n\nArgs:\n type (Literal[\"range_slider\"]): Defaults to `\"range_slider\"`.\n min (Optional[float]): Start value for slider. Defaults to `None`.\n max (Optional[float]): End value for slider. Defaults to `None`.\n step (Optional[float]): Step-size for marks on slider. Defaults to `None`.\n marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `{}`.\n value (Optional[List[float]]): Default start and end value for slider. Must be 2 items. Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
"type": "object",
"properties": {
"id": {
Expand Down Expand Up @@ -533,6 +533,7 @@
"marks": {
"title": "Marks",
"description": "Marks to be displayed on slider.",
"default": {},
"type": "object",
"additionalProperties": {
"type": "string"
Expand Down Expand Up @@ -566,7 +567,7 @@
},
"Slider": {
"title": "Slider",
"description": "Numeric single-selector `Slider`.\n\nCan be provided to [`Filter`][vizro.models.Filter] or\n[`Parameter`][vizro.models.Parameter]. Based on the underlying\n[`dcc.Slider`](https://dash.plotly.com/dash-core-components/slider).\n\nArgs:\n type (Literal[\"range_slider\"]): Defaults to `\"range_slider\"`.\n min (Optional[float]): Start value for slider. Defaults to `None`.\n max (Optional[float]): End value for slider. Defaults to `None`.\n step (Optional[float]): Step-size for marks on slider. Defaults to `None`.\n marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `None`.\n value (Optional[float]): Default value for slider. Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
"description": "Numeric single-selector `Slider`.\n\nCan be provided to [`Filter`][vizro.models.Filter] or\n[`Parameter`][vizro.models.Parameter]. Based on the underlying\n[`dcc.Slider`](https://dash.plotly.com/dash-core-components/slider).\n\nArgs:\n type (Literal[\"range_slider\"]): Defaults to `\"range_slider\"`.\n min (Optional[float]): Start value for slider. Defaults to `None`.\n max (Optional[float]): End value for slider. Defaults to `None`.\n step (Optional[float]): Step-size for marks on slider. Defaults to `None`.\n marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `{}`.\n value (Optional[float]): Default value for slider. Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
"type": "object",
"properties": {
"id": {
Expand Down Expand Up @@ -598,6 +599,7 @@
"marks": {
"title": "Marks",
"description": "Marks to be displayed on slider.",
"default": {},
"type": "object",
"additionalProperties": {
"type": "string"
Expand Down
6 changes: 4 additions & 2 deletions vizro-core/src/vizro/models/_components/form/_form_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,7 @@ def validate_step(cls, step, values):
return step


def set_default_marks(cls, v, values):
return v if values.get("step") is None else {}
def set_default_marks(cls, marks, values):
if not marks and values.get("step") is None:
marks = None
return marks
6 changes: 4 additions & 2 deletions vizro-core/src/vizro/models/_components/form/range_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class RangeSlider(VizroBaseModel):
min (Optional[float]): Start value for slider. Defaults to `None`.
max (Optional[float]): End value for slider. Defaults to `None`.
step (Optional[float]): Step-size for marks on slider. Defaults to `None`.
marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `None`.
marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `{}`.
value (Optional[List[float]]): Default start and end value for slider. Must be 2 items. Defaults to `None`.
title (Optional[str]): Title to be displayed. Defaults to `None`.
actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.
Expand All @@ -36,7 +36,7 @@ class RangeSlider(VizroBaseModel):
min: Optional[float] = Field(None, description="Start value for slider.")
max: Optional[float] = Field(None, description="End value for slider.")
step: Optional[float] = Field(None, description="Step-size for marks on slider.")
marks: Optional[Dict[float, str]] = Field(None, description="Marks to be displayed on slider.")
marks: Optional[Dict[float, str]] = Field({}, description="Marks to be displayed on slider.")
value: Optional[List[float]] = Field(
None, description="Default start and end value for slider", min_items=2, max_items=2
)
Expand Down Expand Up @@ -105,6 +105,7 @@ def build(self):
placeholder="start",
min=self.min,
max=self.max,
step=self.step,
value=value[0],
size="24px",
persistence=True,
Expand All @@ -118,6 +119,7 @@ def build(self):
placeholder="end",
min=self.min,
max=self.max,
step=self.step,
value=value[1],
persistence=True,
className="slider_input_field_right"
Expand Down
5 changes: 3 additions & 2 deletions vizro-core/src/vizro/models/_components/form/slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Slider(VizroBaseModel):
min (Optional[float]): Start value for slider. Defaults to `None`.
max (Optional[float]): End value for slider. Defaults to `None`.
step (Optional[float]): Step-size for marks on slider. Defaults to `None`.
marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `None`.
marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `{}`.
value (Optional[float]): Default value for slider. Defaults to `None`.
title (Optional[str]): Title to be displayed. Defaults to `None`.
actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.
Expand All @@ -36,7 +36,7 @@ class Slider(VizroBaseModel):
min: Optional[float] = Field(None, description="Start value for slider.")
max: Optional[float] = Field(None, description="End value for slider.")
step: Optional[float] = Field(None, description="Step-size for marks on slider.")
marks: Optional[Dict[float, str]] = Field(None, description="Marks to be displayed on slider.")
marks: Optional[Dict[float, str]] = Field({}, description="Marks to be displayed on slider.")
value: Optional[float] = Field(None, description="Default value for slider.")
title: Optional[str] = Field(None, description="Title to be displayed.")
actions: List[Action] = []
Expand Down Expand Up @@ -98,6 +98,7 @@ def build(self):
placeholder="end",
min=self.min,
max=self.max,
step=self.step,
value=self.value or self.min,
persistence=True,
className="slider_input_field_right" if self.step else "slider_input_field_no_space_right",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def expected_range_slider_default():
placeholder="start",
className="slider_input_field_no_space_left",
size="24px",
step=None,
persistence=True,
min=None,
max=None,
Expand All @@ -53,6 +54,7 @@ def expected_range_slider_default():
placeholder="end",
className="slider_input_field_no_space_right",
persistence=True,
step=None,
min=None,
max=None,
value=None,
Expand Down Expand Up @@ -89,8 +91,8 @@ def expected_range_slider_with_optional():
id="range_slider_with_all",
min=0,
max=10,
step=1,
marks={},
step=2,
marks={1.0: "1", 5.0: "5", 10.0: "10"},
className="range_slider_control",
value=[0, 10],
persistence=True,
Expand All @@ -102,6 +104,7 @@ def expected_range_slider_with_optional():
type="number",
placeholder="start",
min=0,
step=2,
max=10,
className="slider_input_field_left",
value=0,
Expand All @@ -114,6 +117,7 @@ def expected_range_slider_with_optional():
placeholder="end",
min=0,
max=10,
step=2,
className="slider_input_field_right",
value=10,
persistence=True,
Expand Down Expand Up @@ -149,13 +153,19 @@ def test_create_range_slider_mandatory_only(self):

def test_create_range_slider_mandatory_and_optional(self):
range_slider = vm.RangeSlider(
min=0, max=10, step=1, marks={}, value=[1, 9], title="Test title", id="range_slider_id"
min=0,
max=10,
step=1,
marks={1: "1", 5: "5", 10: "10"},
value=[1, 9],
title="Test title",
id="range_slider_id",
)

assert range_slider.min == 0
assert range_slider.max == 10
assert range_slider.step == 1
assert range_slider.marks == {}
assert range_slider.marks == {1: "1", 5: "5", 10: "10"}
assert range_slider.value == [1, 9]
assert range_slider.title == "Test title"
assert range_slider.id == "range_slider_id"
Expand Down Expand Up @@ -226,20 +236,6 @@ def test_validate_step_invalid(self):
):
vm.RangeSlider(min=0, max=10, step=11)

@pytest.mark.parametrize(
"marks, step, expected",
[
({2: "2", 4: "4", 6: "6"}, 1, {}),
({2: "2", 4: "4", 6: "6"}, None, {2: "2", 4: "4", 6: "6"}),
({}, 1, {}),
],
)
def test_step_precedence_over_marks(self, marks, step, expected):
slider = vm.RangeSlider(min=0, max=10, marks=marks, step=step)

assert slider.marks == expected
assert slider.step == step

@pytest.mark.parametrize(
"marks, expected",
[
Expand All @@ -266,6 +262,19 @@ def test_set_default_marks(self, step, expected):
slider = vm.RangeSlider(min=0, max=10, step=step)
assert slider.marks == expected

@pytest.mark.parametrize(
"step, marks, expected",
[
(1, None, None),
(None, {1: "1", 2: "2"}, {1: "1", 2: "2"}),
(1, {1: "1", 2: "2"}, {1: "1", 2: "2"}),
(None, {}, None),
],
)
def test_set_step_and_marks(self, step, marks, expected):
slider = vm.RangeSlider(min=0, max=10, step=step, marks=marks)
assert slider.marks == expected

@pytest.mark.parametrize(
"title",
[
Expand Down Expand Up @@ -301,7 +310,15 @@ def test_range_slider_build_default(self, expected_range_slider_default):
assert result == expected

def test_range_slider_build_with_optional(self, expected_range_slider_with_optional):
range_slider = vm.RangeSlider(min=0, max=10, step=1, value=[0, 10], id="range_slider_with_all", title="Title")
range_slider = vm.RangeSlider(
min=0,
max=10,
step=2,
value=[0, 10],
id="range_slider_with_all",
title="Title",
marks={1: "1", 5: "5", 10: "10"},
)
component = range_slider.build()

result = json.loads(json.dumps(component, cls=plotly.utils.PlotlyJSONEncoder))
Expand Down
32 changes: 16 additions & 16 deletions vizro-core/tests/unit/vizro/models/_components/form/test_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def expected_slider():
type="number",
placeholder="end",
min=0,
step=1,
max=10,
value=5,
persistence=True,
Expand All @@ -63,9 +64,9 @@ def test_create_slider_mandatory(self):

assert hasattr(slider, "id")
assert slider.type == "slider"
assert slider.step is None
assert slider.min is None
assert slider.max is None
assert slider.step is None
assert slider.marks is None
assert slider.value is None
assert slider.title is None
Expand Down Expand Up @@ -139,24 +140,10 @@ def test_validate_step_invalid(self):
vm.Slider(min=0, max=10, step=11)

def test_valid_marks_with_step(self):
slider = vm.Slider(min=0, max=10, step=1)
slider = vm.Slider(min=0, max=10, step=2)

assert slider.marks == {}

@pytest.mark.parametrize(
"marks, step, expected",
[
({2: "2", 4: "4", 6: "6"}, 1, {}),
({2: "2", 4: "4", 6: "6"}, None, {2: "2", 4: "4", 6: "6"}),
({}, 1, {}),
],
)
def test_step_precedence_over_marks(self, marks, step, expected):
slider = vm.Slider(min=0, max=10, marks=marks, step=step)

assert slider.marks == expected
assert slider.step == step

@pytest.mark.parametrize(
"marks, expected",
[
Expand All @@ -183,6 +170,19 @@ def test_set_default_marks(self, step, expected):
slider = vm.Slider(min=0, max=10, step=step)
assert slider.marks == expected

@pytest.mark.parametrize(
"step, marks, expected",
[
(1, None, None),
(None, {1: "1", 2: "2"}, {1: "1", 2: "2"}),
(2, {1: "1", 2: "2"}, {1: "1", 2: "2"}),
(None, {}, None),
],
)
def test_set_step_and_marks(self, step, marks, expected):
slider = vm.Slider(min=0, max=10, step=step, marks=marks)
assert slider.marks == expected

@pytest.mark.parametrize(
"title",
[
Expand Down

0 comments on commit 4868535

Please sign in to comment.