From dfeda94e06424544c321be2703f119ff6a84e161 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Tue, 16 Dec 2025 13:43:16 +0100 Subject: [PATCH] Add report percentages to dashboard and details view (#5923) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update report percentages on dashboard and details view * Add percentages to Countries, Regions, and Cities reports * Add percentages to Channels, Sources, and UTM reports * Add percentages to top pages, entry pages, and exit pages reports * Update tests to include percentages * Change dashboard copy from title case to sentence case * Update details modal style * Make animations snappier * Introduce max height to modal and make inner content scrollable * Improve modal mobile design - Enable horizontal scroll for details modal on mobile - Add responsive spacing and positioning to modal * Added mobile tap behavior to external link in list report * Show tooltips only when in comparison mode or when the number is abbreviated * remove previously added showTooltip prop - This isn't needed anymore since we now handle the tooltip logic in the MetricValue component * Show long format upon hovering detailed view metrics * Added mobile tapping behaviour to detailed view * Added percentages to all detailed views * Add mobile swipe-to-close behavior for modal * Adjust sensitivity of modal drag to close * Use hammerjs for swipe-to-close modal behaviour * Prevent dragging if gesture starts inside table * Show 2 decimal places for percentages < 0.1% across dashboard * Adjust dark mode styles * Add hover effect to external link icon * Update tests to expect two-decimal percentages * Undo hammer install and revert to old modal styling * Remove CR and % columns from goals and custom props reports on dashboard, and show on hover in detailed view * Remove unused constants * Undo conversion rate on hover behaviour - Unlike percentages, CR should show permanently. * Show percentages permanently in custom props detailed view * Adjust width of conversion metrics column * Updated metric-value test * Update top-bar test * Added changelog entry * Fix test expectations for percentages with imported data - Update tests to expect correct percentages (≤100%) when imported data is included. These tests will fail until the percentage calculation bug is fixed, documenting the expected behavior. * Add imported_visitors to tests to ensure correct total_visitors calculation * Correct imported_visitors count in test --- CHANGELOG.md | 2 + assets/css/app.css | 13 +- assets/css/modal.css | 27 -- .../js/dashboard/components/search-input.tsx | 2 +- .../js/dashboard/components/sort-button.tsx | 13 +- assets/js/dashboard/components/table.tsx | 59 ++- assets/js/dashboard/components/tabs.tsx | 2 +- .../segments/searchable-segments-section.tsx | 2 +- assets/js/dashboard/nav-menu/top-bar.test.tsx | 2 +- assets/js/dashboard/stats/bar.js | 2 +- .../dashboard/stats/behaviours/conversions.js | 2 +- .../stats/behaviours/goal-conversions.js | 1 - assets/js/dashboard/stats/behaviours/index.js | 4 +- assets/js/dashboard/stats/behaviours/props.js | 3 +- assets/js/dashboard/stats/devices/index.js | 28 +- assets/js/dashboard/stats/graph/graph-util.js | 20 +- assets/js/dashboard/stats/locations/index.js | 14 +- assets/js/dashboard/stats/locations/map.tsx | 2 +- .../stats/modals/breakdown-modal.tsx | 63 +++- .../stats/modals/breakdown-table.tsx | 64 ++-- .../js/dashboard/stats/modals/conversions.js | 2 +- .../modals/devices/browser-versions-modal.js | 2 +- .../stats/modals/devices/choose-metrics.js | 4 +- .../operating-system-versions-modal.js | 2 +- .../modals/devices/operating-systems-modal.js | 2 +- .../stats/modals/devices/screen-sizes.js | 2 +- .../js/dashboard/stats/modals/entry-pages.js | 8 +- .../js/dashboard/stats/modals/exit-pages.js | 7 +- .../js/dashboard/stats/modals/filter-modal.js | 33 +- .../dashboard/stats/modals/locations-modal.js | 8 +- assets/js/dashboard/stats/modals/modal.js | 33 +- assets/js/dashboard/stats/modals/pages.js | 4 +- assets/js/dashboard/stats/modals/props.js | 3 +- .../stats/modals/referrer-drilldown.js | 2 +- assets/js/dashboard/stats/modals/sources.js | 26 +- assets/js/dashboard/stats/pages/index.js | 30 +- .../stats/reports/change-arrow.test.tsx | 2 +- .../dashboard/stats/reports/change-arrow.tsx | 12 +- assets/js/dashboard/stats/reports/list.tsx | 177 ++++++--- .../stats/reports/metric-value.test.tsx | 41 +-- .../dashboard/stats/reports/metric-value.tsx | 107 ++++-- assets/js/dashboard/stats/reports/metrics.js | 39 +- .../dashboard/stats/sources/search-terms.tsx | 2 +- .../js/dashboard/stats/sources/source-list.js | 28 +- assets/js/dashboard/util/filters.js | 20 +- assets/js/dashboard/util/number-formatter.ts | 6 +- assets/js/dashboard/util/tooltip.tsx | 17 +- .../live/funnel_settings/form.ex | 2 +- lib/plausible/stats/sql/special_metrics.ex | 2 +- .../controllers/api/stats_controller.ex | 48 ++- lib/plausible_web/live/components/modal.ex | 2 +- .../query_comparisons_test.exs | 10 +- .../external_stats_controller/query_test.exs | 4 +- .../api/stats_controller/browsers_test.exs | 18 +- .../api/stats_controller/cities_test.exs | 40 +- .../api/stats_controller/countries_test.exs | 4 +- .../custom_prop_breakdown_test.exs | 16 +- .../api/stats_controller/imported_test.exs | 76 ++-- .../operating_systems_test.exs | 12 +- .../api/stats_controller/pages_test.exs | 347 ++++++++++++------ .../api/stats_controller/regions_test.exs | 24 +- .../stats_controller/screen_sizes_test.exs | 16 +- .../api/stats_controller/sources_test.exs | 271 +++++++++----- .../controllers/stats_controller_test.exs | 8 +- 64 files changed, 1170 insertions(+), 674 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a35de82a2..eee1c4a618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. ### Added +- A visitor percentage breakdown is now shown on all reports, both on the dashboard and in the detailed breakdown + ### Removed ### Changed diff --git a/assets/css/app.css b/assets/css/app.css index a3460f3b63..71879d553e 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -90,6 +90,7 @@ --color-gray-950: var(--color-zinc-950); /* Custom gray shades from config (override some zinc values) */ + --color-gray-75: rgb(247 247 248); --color-gray-150: rgb(236 236 238); --color-gray-750: rgb(50 50 54); --color-gray-825: rgb(35 35 38); @@ -294,16 +295,12 @@ blockquote { display: inline; } -.table-striped tbody tr:nth-child(odd) { - background-color: var(--color-gray-100); +.table-striped tbody tr:nth-child(odd) td { + background-color: var(--color-gray-75); } -.dark .table-striped tbody tr:nth-child(odd) { - background-color: var(--color-gray-800); -} - -.dark .table-striped tbody tr:nth-child(even) { - background-color: var(--color-gray-900); +.dark .table-striped tbody tr:nth-child(odd) td { + background-color: var(--color-gray-850); } .fade-enter { diff --git a/assets/css/modal.css b/assets/css/modal.css index 500ffab9a0..365c0f5a30 100644 --- a/assets/css/modal.css +++ b/assets/css/modal.css @@ -32,33 +32,6 @@ overflow: auto; } -.modal__container { - background-color: #fff; - padding: 1rem 2rem; - border-radius: 4px; - margin: 50px auto; - box-sizing: border-box; - min-height: 509px; - transition: height 200ms ease-in; -} - -.modal__close { - position: fixed; - color: #b8c2cc; - font-size: 48px; - font-weight: bold; - top: 12px; - right: 24px; -} - -.modal__close::before { - content: '\2715'; -} - -.modal__content { - margin-bottom: 2rem; -} - @keyframes mm-fade-in { from { opacity: 0; diff --git a/assets/js/dashboard/components/search-input.tsx b/assets/js/dashboard/components/search-input.tsx index 68dc848ada..a9a739ade1 100644 --- a/assets/js/dashboard/components/search-input.tsx +++ b/assets/js/dashboard/components/search-input.tsx @@ -66,7 +66,7 @@ export const SearchInput = ({ type="text" placeholder={isFocused ? placeholderFocused : placeholderUnfocused} className={classNames( - 'dark:text-gray-100 block border-gray-300 dark:border-gray-750 rounded-md dark:bg-gray-750 w-48 dark:placeholder:text-gray-400 focus:outline-none focus:ring-3 focus:ring-indigo-500/20 dark:focus:ring-indigo-500/25 focus:border-indigo-500', + 'text-sm dark:text-gray-100 block border-gray-300 dark:border-gray-750 rounded-md dark:bg-gray-750 max-w-64 w-full dark:placeholder:text-gray-400 focus:outline-none focus:ring-3 focus:ring-indigo-500/20 dark:focus:ring-indigo-500/25 focus:border-indigo-500', className )} onChange={debouncedOnSearchInputChange} diff --git a/assets/js/dashboard/components/sort-button.tsx b/assets/js/dashboard/components/sort-button.tsx index eaf5561933..3ca98b298c 100644 --- a/assets/js/dashboard/components/sort-button.tsx +++ b/assets/js/dashboard/components/sort-button.tsx @@ -15,14 +15,18 @@ export const SortButton = ({ return (