refactor: split the largest source files, fix modal scroll lock and popup placement (#353, #354) - #355
Merged
Conversation
filter.js (1922), eventlisteners.js (1564), server.py (908) and components.css (854) each did several unrelated jobs, so every change touching them meant reading the whole file. - server.py -> server.py (app, auth, HTML routes) + server_models.py + server_routes_outputs.py + server_routes_logs.py. The route modules are register_*_routes(server, authenticate) functions; server.py re-exports the models so existing imports keep working. - js/eventlisteners.js -> js/eventlisteners/, one module per modal or listener group. The merge-profiles wiring moved out of setup_filter_modal into setup_merge_profiles_modal(). - js/filter.js -> js/filter/: pipeline, section_selects, modal_options, suite_path, availability, controls, profiles, profile_merge. - css/components.css -> css/components/. CSS is concatenated in path order, which is the cascade order, so the subdirectory keeps the files between colors.css and dark.css and the numbered prefixes keep their order within it. Also fixes three imports that only ever resolved because the bundler flattens every module into one scope: filteredAmount came from the wrong module, six setup_*_in_select functions were used without being imported, and set_filter_show_current_version was imported from a module that does not export it. Behaviour is unchanged: the bundled <script> is identical to the one built from main apart from the extracted function and its call, and the bundled <style> is identical apart from the section marker comments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…354) The dashboard's scrollbar sits on <html> (.html-scroll), but Bootstrap locks the page by setting overflow:hidden on <body>, so the lock never applied and the page scrolled behind every open modal. Lock <html> when body carries .modal-open, with scrollbar-gutter so the layout does not jump sideways when the scrollbar goes away. createTooltip() corrected only horizontal overflow, so long information popups low in the filter modal ran past the bottom of the window. Flip them above their icon when they would overflow, pinned to the top edge when the popup is taller than the window. Both get a robot test, each checked to fail before the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #353
Fixes #354
Problem
#353 — four files had grown large enough that any change touching them meant reading the whole file:
js/filter.js(1922 lines),js/eventlisteners.js(1564),server.py(908) andcss/components.css(854). Each was doing several unrelated jobs, so the size was not inherent — every one had clean internal seams.#354 — two visual defects around modals. The page scrolled behind an open modal instead of staying put, and long information popups near the bottom of the filter modal were cut off by the bottom of the window.
Root cause
overflow: hiddenon<body>, but the dashboard's scrollbar is on<html>(class="html-scroll"in the template,.html-scroll { overflow-y: scroll }inbase.css).<html>is the scroll container, so the body-level lock had nothing to lock.createTooltip()injs/information.jsplaces the popup below its icon, then measures it and shifts it left or right to stay inside the window. There was no equivalent vertical correction.Fix
Splits (#353)
server.pyserver.py(211) +server_models.py(289) +server_routes_outputs.py(275) +server_routes_logs.py(193)js/eventlisteners.jsjs/eventlisteners/— 8 modules, 25–386 linesjs/filter.jsjs/filter/— 8 modules, 107–472 linescss/components.csscss/components/— 8 files, 30–247 linesserver.py— the first 295 lines were OpenAPI example dicts and Pydantic models with no logic, and the routes were one 520-line_setup_routes. The route modules are plainregister_*_routes(server, authenticate)functions taking theApiServerinstance;server.pyre-exports the models sofrom robotframework_dashboard.server import ResponseMessagekeeps working.js/eventlisteners.js— one module per modal or listener group:filter_modal,merge_profiles,settings_modal,confirm_modal,section_filters,graph_view_buttons,collapsables,overview_listeners. The merge-profiles dialogue wiring moved out ofsetup_filter_modalintosetup_merge_profiles_modal().js/filter.js— split along its existing concerns:pipeline,section_selects,modal_options,suite_path,availability,controls,profiles,profile_merge.css/components.css—_inline_css_files()concatenates in path order, and that order is the cascade. A subdirectory keeps the files in the exact slotcomponents.cssheld betweencolors.cssanddark.css, and the numbered prefixes keep their order within it. The first file carries a comment saying renaming one reorders the stylesheet.Three imports that only ever resolved because the bundler flattens every module into one scope are fixed along the way:
filteredAmountwas imported fromvariables/globals.jsinstead ofvariables/data.js, sixsetup_*_in_selectfunctions were used without being imported, andset_filter_show_current_versionwas imported fromgraph_creation/overview.js, which does not export it. Two are deliberately left as bundle globals, because importing them would create a source-level cycle back into the modules that import filter:update_filters_button_indicatorandclear_project_filter.arguments.py, the modals intemplates/dashboard.htmland the large test files were considered and deliberately left alone.Fixes (#354)
css/base.css—html:has(body.modal-open) { overflow: hidden; scrollbar-gutter: stable }. The gutter keeps the scrollbar width reserved, otherwise the layout jumps sideways every time a modal opens.:has()is already used elsewhere in the stylesheet. The admin page is unaffected: its<html>has nohtml-scroll, so Bootstrap's own lock works there.js/information.js— after the existing measurement, flip the popup above its icon when it would overflow the bottom, pinned to the top edge when the popup is taller than the window. The sides were already handled; the top only becomes reachable after a flip, which the clamp covers.CLAUDE.mdand thecoding-standards,js-bundling,js-features,filtering-and-settings,dashboard-graphs,server-apiandtestingskills carry the new file maps, so the splits do not just move the search cost around.Tests
406 passed, 100% coverage on all four server modules337 passed119 tests, 119 passed, 0 failed, single pass, no rerunsTwo regression tests, each verified to fail on
mainand pass here:03_dashboard.robot— The Page Behind An Open Modal Does Not Scroll: opens the database modal, sends a wheel event, assertswindow.scrollYis unchanged. The database modal is short enough not to scroll itself, so the wheel reaches the page whenever the lock is missing. Onmain:15 passed, 1 failed(page scrolls 0 → 900).06_filters.robot— Information Popups In The Filter Modal Stay Inside The Window: hovers every information icon in the filter modal and asserts each popup is fully inside the viewport. Shrinks the window to 1600x800 first, since the suite's 2000px-high viewport is too tall for any popup to overflow. Onmain:29 passed, 1 failed(filterAmountInformation616–905 andfilterSuitePathsInformation660–881, both past 800).The refactor is behaviour-preserving, checked rather than assumed:
<script>is line-for-line identical to the one built frommain, except the extractedsetup_merge_profiles_modal()wrapper, its call, and one corrected comment.<style>is identical except the/* === filename === */section markers, so the cascade is unchanged.Notes
No user-visible behaviour changes from the refactor, so no
docs/updates; the CHANGELOG is written at release time.example/robot_dashboard.htmlandexample/robot_results.dbare release-time artifacts and are deliberately not regenerated here, although the CSS fix will show up in them at the next release.One trap worth recording for local runs: a stale gitignored
build/directory makes setuptools ship both the oldfilter.jsand the newfilter/in the wheel, which puts twoconst dashboardPagesin the bundle and kills every browser test with aSyntaxError. Runrm -rf buildbeforepip install .after files move. Related: single-suite robot runs skip the__init__.robotteardown, sorobotdashboard_shared.htmlsurvives and is reused by the next run.🤖 Generated with Claude Code