From 316fafa675c8a4fd3fa1c95088e34e9267dc6200 Mon Sep 17 00:00:00 2001 From: MinaSaad1 Date: Wed, 1 Apr 2026 21:02:24 +0200 Subject: [PATCH] feat: add advancedSlicerVisual (tile/image slicer) visual type (v3.6.0) Registers advancedSlicerVisual with aliases advanced_slicer, adv_slicer, and tile_slicer. Includes template with Values queryState, data roles, role aliases, default size (280x280), renderer color/icon, and tests. --- src/pbi_cli/core/pbir_models.py | 4 ++++ src/pbi_cli/core/visual_backend.py | 3 +++ src/pbi_cli/preview/renderer.py | 2 ++ .../visuals/advancedSlicerVisual.json | 24 +++++++++++++++++++ tests/test_visual_backend.py | 24 +++++++++++++++++++ 5 files changed, 57 insertions(+) create mode 100644 src/pbi_cli/templates/visuals/advancedSlicerVisual.json diff --git a/src/pbi_cli/core/pbir_models.py b/src/pbi_cli/core/pbir_models.py index 0820471..70cee71 100644 --- a/src/pbi_cli/core/pbir_models.py +++ b/src/pbi_cli/core/pbir_models.py @@ -75,6 +75,7 @@ SUPPORTED_VISUAL_TYPES: frozenset[str] = frozenset({ "shape", "textbox", "pageNavigator", + "advancedSlicerVisual", }) # Mapping from user-friendly names to PBIR visualType identifiers @@ -132,6 +133,9 @@ VISUAL_TYPE_ALIASES: dict[str, str] = { "page_navigator": "pageNavigator", "page_nav": "pageNavigator", "navigator": "pageNavigator", + "advanced_slicer": "advancedSlicerVisual", + "adv_slicer": "advancedSlicerVisual", + "tile_slicer": "advancedSlicerVisual", } diff --git a/src/pbi_cli/core/visual_backend.py b/src/pbi_cli/core/visual_backend.py index c066cc4..2292ceb 100644 --- a/src/pbi_cli/core/visual_backend.py +++ b/src/pbi_cli/core/visual_backend.py @@ -81,6 +81,7 @@ VISUAL_DATA_ROLES: dict[str, list[str]] = { "shape": [], "textbox": [], "pageNavigator": [], + "advancedSlicerVisual": ["Values"], } # Roles that should default to Measure references (not Column) @@ -138,6 +139,7 @@ ROLE_ALIASES: dict[str, dict[str, str]] = { "shape": {}, "textbox": {}, "pageNavigator": {}, + "advancedSlicerVisual": {"value": "Values", "field": "Values"}, } @@ -224,6 +226,7 @@ DEFAULT_SIZES: dict[str, tuple[float, float]] = { "shape": (300, 200), "textbox": (300, 100), "pageNavigator": (120, 400), + "advancedSlicerVisual": (280, 280), } diff --git a/src/pbi_cli/preview/renderer.py b/src/pbi_cli/preview/renderer.py index de556f4..7dfcad0 100644 --- a/src/pbi_cli/preview/renderer.py +++ b/src/pbi_cli/preview/renderer.py @@ -84,6 +84,7 @@ _VISUAL_COLORS: dict[str, str] = { "shape": "#7F7F7F", "textbox": "#404040", "pageNavigator": "#00B0F0", + "advancedSlicerVisual": "#FFC000", } _VISUAL_ICONS: dict[str, str] = { @@ -122,6 +123,7 @@ _VISUAL_ICONS: dict[str, str] = { "shape": "▲", "textbox": "◻", "pageNavigator": "►", + "advancedSlicerVisual": "☰", } diff --git a/src/pbi_cli/templates/visuals/advancedSlicerVisual.json b/src/pbi_cli/templates/visuals/advancedSlicerVisual.json new file mode 100644 index 0000000..924a009 --- /dev/null +++ b/src/pbi_cli/templates/visuals/advancedSlicerVisual.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/fabric/item/report/definition/visualContainer/2.7.0/schema.json", + "name": "__VISUAL_NAME__", + "position": { + "x": __X__, + "y": __Y__, + "z": __Z__, + "height": __HEIGHT__, + "width": __WIDTH__, + "tabOrder": __TAB_ORDER__ + }, + "visual": { + "visualType": "advancedSlicerVisual", + "query": { + "queryState": { + "Values": { + "projections": [] + } + } + }, + "objects": {}, + "drillFilterOtherVisuals": true + } +} diff --git a/tests/test_visual_backend.py b/tests/test_visual_backend.py index 3bddb6a..4a24286 100644 --- a/tests/test_visual_backend.py +++ b/tests/test_visual_backend.py @@ -905,3 +905,27 @@ def test_textbox_no_how_created(report_with_page: Path) -> None: ) data = json.loads(vfile.read_text()) assert "howCreated" not in data + + +# --------------------------------------------------------------------------- +# v3.6.0 -- advancedSlicerVisual +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize("alias", [ + "advancedSlicerVisual", "advanced_slicer", "adv_slicer", "tile_slicer", +]) +def test_advanced_slicer_aliases(report_with_page: Path, alias: str) -> None: + r = visual_add(report_with_page, "test_page", alias, x=0, y=0) + assert r["visual_type"] == "advancedSlicerVisual" + + +def test_advanced_slicer_has_values_querystate(report_with_page: Path) -> None: + r = visual_add(report_with_page, "test_page", "advancedSlicerVisual", x=0, y=0) + vfile = ( + report_with_page / "pages" / "test_page" / "visuals" / r["name"] / "visual.json" + ) + data = json.loads(vfile.read_text()) + assert "query" in data["visual"] + assert "Values" in data["visual"]["query"]["queryState"] + assert isinstance(data["visual"]["query"]["queryState"]["Values"]["projections"], list)