Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ For all available CLI options see the [Basic CLI docs](https://marketsquare.gith
- 📄 **Dashboard Pages** - Overview, Dashboard, and Compare pages for multi-level insights.
- 🎛️ **Customizable Layouts** - Drag-and-drop sections with adjustable size and order.
- 📊 **Graph Customization** - Toggle legends, axis titles, labels, and control animations.
- 🔎 **Global Filters** - Filter runs by name, tags, date, metadata, or quantity (applied per project). Every filter option shows how many runs it still matches, and options that match none are greyed out.
- 🔎 **Global Filters** - Filter runs by name, tags, date, metadata, or quantity (applied per project). Drag across the runs-over-time histogram to zoom into a period. Every filter option shows how many runs it still matches, and options that match none are greyed out.
- ⚖️ **Comparison Mode** - Compare up to 4 runs side by side with visual statistics.
- 🔗 **Automatic Log Linking** - Open Robot Framework logs directly from the dashboard.
- 🔁 **Rerun History** - Merged `--rerunfailed` outputs keep the status of every attempt per test.
Expand Down
24 changes: 16 additions & 8 deletions docs/filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,25 @@ Example: with two custom filters where `filter_1=A` only ever occurs together wi
- Typing in the search box inside the dropdown selects every matching version and unselects the rest (e.g. `1.` selects all `1.x` versions in one go).
- Click the **X** in the search box to clear it while keeping the current selection. Deleting characters one by one instead re-runs the matching on every keystroke.

#### 4. From Date / From Time
#### 4. Runs over Time (date histogram)

- Sets the earliest point in time a run must have started at to be included.
- Runs that started before this date and time are excluded.
- Defaults to the date and time of the oldest run in the data (with a small margin to account for seconds and daylight saving time).
A bar chart of how many runs started in each time bucket, sitting directly above the date range. It replaces typing dates with dragging: the bars show where the runs actually are, so you can pick the period straight off the chart.

#### 5. To Date / To Time
- **Bars** show the number of runs per bucket, stacked by run status: red for runs with failures, yellow for runs that only skipped, green for the rest. Vertical separators mark every bucket, so gaps in the data stay readable.
- **Drag across the chart** to select a range. The From/To fields below are filled in and the chart **zooms into the selection**, re-bucketing finer, so dragging again drills further down (a month at a bar per day, then a week at a bar per 6 hours, then a day at a bar per hour).
- **Click a single bar** to select just that bucket.
- **Reset Range** returns to the full span of the data. Widening the From/To fields by hand does the same.
- The bucket size is chosen from the visible range and is printed next to the chart as `1 bar = 1 day`. Hovering a bar shows its exact time range and, per run in it, how many tests passed, failed and were skipped — the same shape as the hover on the run graphs.
- The bars follow every other filter (runs, run tags, versions, metadata, custom filters, suite path) but not the date range itself — the date range is the part of the timeline you are looking at, which is why widening it brings the runs outside it straight back. The *Amount per project* filter is not applied to the bars either.

- Sets the latest point in time a run must have started at to be included.
- Runs that started after this date and time are excluded.
- Defaults to the date and time of the most recent run in the data (with a small margin).
#### 5. From / To Date and Time

The four fields below the chart are the range itself, and they stay editable by hand.

- **From Date / From Time** set the earliest point in time a run must have started at to be included; runs that started before it are excluded.
- **To Date / To Time** set the latest point in time; runs that started after it are excluded.
- They default to the oldest and most recent run in the data (with a small margin to account for seconds and daylight saving time), which is also what **Reset Range** restores.
- Typing in them moves the histogram above, exactly like dragging moves them.

#### 6. Metadata

Expand Down
90 changes: 90 additions & 0 deletions robotframework_dashboard/css/components/02-filters.css
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,93 @@ li.filter-option-unavailable > .filter-option-count {
.filter-profile-delete:hover {
opacity: 1;
}

.date-histogram-wrapper {
position: relative;
width: 100%;
height: 160px;
/* the drag selection is a pointer gesture, so the browser may not claim it for panning */
touch-action: none;
cursor: crosshair;
}

.date-histogram-selection {
position: absolute;
top: 0;
bottom: 0;
background-color: rgba(54, 162, 235, 0.2);
border-left: 1px solid rgba(54, 162, 235, 0.8);
border-right: 1px solid rgba(54, 162, 235, 0.8);
pointer-events: none;
}

.date-histogram-bucket,
.date-histogram-empty {
font-size: 0.8rem;
color: var(--color-text-muted);
}

.date-range-row {
gap: 0.75rem 1.25rem;
}

.date-range-field {
flex: 0 0 auto;
}

.date-range-label {
margin-right: 0.5rem;
min-width: 2.5rem;
}

.date-range-input {
width: 150px;
}

/* shaped after the Chart.js tooltip the other graphs use, but an element of its own: the details
per run are taller than the chart and carry a colour per row, neither of which the tooltip on
the canvas can do */
.date-histogram-tooltip {
position: absolute;
top: 8px;
z-index: 3;
max-width: 320px;
padding: 6px 8px;
border-radius: 6px;
background-color: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: 0.75rem;
line-height: 1.35;
pointer-events: none;
}

.date-histogram-tooltip-title {
font-weight: 700;
margin-bottom: 0.25rem;
}

.date-histogram-tooltip-run + .date-histogram-tooltip-run {
margin-top: 0.35rem;
}

.date-histogram-tooltip-run-name {
opacity: 0.85;
}

.date-histogram-tooltip-row {
display: flex;
align-items: center;
}

.date-histogram-tooltip-dot {
width: 9px;
height: 9px;
margin-right: 0.4rem;
border-radius: 2px;
flex-shrink: 0;
}

.date-histogram-tooltip-empty,
.date-histogram-tooltip-more {
opacity: 0.85;
}
13 changes: 13 additions & 0 deletions robotframework_dashboard/js/eventlisteners/filter_modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ import {
schedule_filter_option_availability_refresh,
set_filter_modal_open,
} from "../filter/availability.js";
import {
render_date_histogram,
schedule_date_histogram_refresh,
setup_date_histogram,
} from "../filter/date_histogram.js";
import { confirm_action } from "./confirm_modal.js";
import { setup_merge_profiles_modal } from "./merge_profiles.js";

Expand Down Expand Up @@ -65,10 +70,16 @@ function setup_filter_modal() {
document.getElementById("filtersModal").addEventListener("show.bs.modal", function () {
set_filter_modal_open(true);
});
// Chart.js cannot size a canvas inside a hidden element, so the histogram waits for the modal
// to be on screen instead of drawing on show.bs.modal
document.getElementById("filtersModal").addEventListener("shown.bs.modal", function () {
render_date_histogram();
});
// eventlistener to reset the filters
document.getElementById("resetFilters").addEventListener("click", function () {
clear_all_filters();
clear_active_profile();
schedule_date_histogram_refresh();
add_alert("Filters have been set to default values!", "success")
update_profile_select_display();
});
Expand Down Expand Up @@ -147,6 +158,7 @@ function setup_filter_modal() {
setup_project_versions_in_select_filter_buttons();
setup_suite_path_navigator("All");
setup_custom_filters_in_select_filter_buttons();
setup_date_histogram();
// snapshot the default/initial filter state so profile checkboxes can reflect changes
capture_default_filters();
// filter profiles setup
Expand Down Expand Up @@ -207,6 +219,7 @@ function setup_filter_modal() {
const profile = profiles[name];
if (profile) {
apply_filter_profile(profile, name);
schedule_date_histogram_refresh();
add_alert(`Filter profile "${name}" applied`, "success");
update_profile_select_display();
populate_filter_profile_select();
Expand Down
14 changes: 12 additions & 2 deletions robotframework_dashboard/js/filter/availability.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ function apply_filters_except(runList, selections, facet, dimName = null, suiteL
if (facet === "customFilters" && dim === dimName) { continue; }
result = apply_custom_filter_dimension(result, dim, dimSelection.values, dimSelection.mode);
}
// the date range and the suite path have no option lists to count, so they always apply
if (selections.dateRange) {
// the suite path has no option list to count, so it always applies. The date range applies
// too, except for the date histogram, which draws the runs the window is chosen from.
if (facet !== "dates" && selections.dateRange) {
result = apply_date_filter(result, selections.dateRange.from, selections.dateRange.to);
}
if (selections.suitePath && selections.suitePath !== "All") {
Expand Down Expand Up @@ -158,6 +159,14 @@ function compute_filter_option_availability(runList, selections, suiteList = nul
return availability;
}

// the runs the date histogram draws: every run level filter except the date range itself,
// so the bars still cover the runs outside the window and widening it brings them back. The
// amount filter is left out for the same reason it is left out of the option counts.
function get_runs_for_date_histogram() {
const selections = normalize_filter_selections(capture_current_filters());
return apply_filters_except(get_filter_base_runs(), selections, "dates");
}

// add or update the "(X)" count of one filter option row. The count is a sibling of the
// label, not a child: the row is the flex container, so only a direct child of the row can
// be pushed into its own right-aligned column.
Expand Down Expand Up @@ -252,6 +261,7 @@ function schedule_filter_option_availability_refresh() {

export {
compute_filter_option_availability,
get_runs_for_date_histogram,
normalize_filter_selections,
refresh_filter_option_availability,
schedule_filter_option_availability_refresh,
Expand Down
Loading