Refactor dashboard widget picker and fix gridstack layout bugs#129
Merged
roncodes merged 7 commits intoMay 20, 2026
Merged
Conversation
Picker (Dashboard::WidgetPanel):
- Rewrite as a compact, sticky-header overlay with All / Recommended / On
Dashboard tabs, search across name + description + category, and category
grouping driven by the registry Widget.category field.
- Add Dashboard::WidgetCard primitive with fixed-height (88px) cards, default
icon fallback (puzzle-piece), inline Default and Added×N badges aligned
beneath the title, and a hover-revealed plus button.
- Zero out the inner-wrapper padding via class="no-padding" + a
dashboard-widget-panel-body scope so the sticky header sits flush.
Dashboard service / Dashboard model:
- Stop using the widget registry slug (e.g. fleet-ops-kpi-earnings-widget)
as the Ember Data dashboard-widget record id. Strip it on createRecord and
stash it under options.widget_key so persisted records can still resolve
back to the registry. Fixes "The id … has already been used with another
'dashboard-widget' record" assertion when adding a fleetops widget.
- Collapse the redundant peekAll + peekRecord lookup in
_createDefaultDashboard, replace the double-next() reset race with a
synchronous unloadAll pair.
System dashboard:
- Add isSystemDashboard getter on DashboardComponent; force @isEdit to false
on Dashboard::Create and replace the action-menu Edit/Add/Delete items
with a "Create a dashboard to customize widgets" hint when the active
dashboard is the virtual system record.
GridStack layout:
- Wrap the GridStack subtree in {{#each (array @dashboard.id) key="@identity"}}
so switching dashboards destroys + remounts the .grid-stack element. This
is the only reliable way to clear gridstack's engine state AND the inline
min-height/height styles it stamps on the container, which previously left
a big empty band at the top after switching between dashboards of
different sizes.
- compactGrid() is still invoked after widget removal so single deletions
close the hole left behind.
Tests:
- Replace placeholder widget-panel test with real integration coverage for
category grouping, On Dashboard badge counts, tab filtering, and search
across name + description + category.
- Unit test asserts _createDefaultDashboardWidgets can be invoked twice
without triggering the identity-map regression, that each record gets a
fresh client UUID, and that the slug lands in options.widget_key.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "clickable card" pattern needs descriptive children (icon, title, description, badges) so the user can identify which widget they're about to activate. ember-template-lint flags any role="button" with semantic descendants, but the alternative — stripping content — would defeat the picker's purpose. The card remains tabindex-focusable and click-activated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
deleteDashboard calls `this.loadDashboards.perform()` with no args. The task
signature destructures the first parameter, so the call hit a TypeError
("Cannot read properties of undefined (reading 'defaultDashboardId')") and
broke the post-delete reload. Default the destructured parameter to an
empty object so the defaults kick in instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`assert.doesNotThrow` is a Node `assert` API, not a QUnit one. The test was
silently flagged at runtime ("assert.doesNotThrow is not a function") and
counted as a failure. Replaced with a try/catch + `assert.strictEqual`
pattern so the test asserts the same invariant (no identity-map collision
on second materialization) using only standard QUnit methods.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Dashboard::WidgetCardprimitive with fixed-height cards, default icon fallback, badges aligned beneath the title.Dashboard.addWidgetno longer use the widget registry slug as the Ember Datadashboard-widgetrecord id; the slug is stashed underoptions.widget_keyinstead. Fixes the "The idfleet-ops-kpi-earnings-widgethas already been used with another 'dashboard-widget' record" assertion.isSystemDashboardgetter gates edit/add/delete actions on the virtual default dashboard so they don't 404 against the backend.{{#each (array @dashboard.id) key="@identity"}}so switching dashboards remounts the.grid-stackDOM cleanly. Resolves the empty band that previously appeared above the new dashboard's widgets when switching between dashboards of different sizes.gridstack.compact()so the surrounding widgets slide up to fill the hole.Test plan
On dashboard ×2badge appears in the picker.ember test— new integration tests (tests/integration/components/dashboard/widget-panel-test.js) and unit tests (tests/unit/services/dashboard-test.js) pass.