feat(vue-mri): filter the exploration list by author, status and date (#3150) - #3263
Open
khairul-syazwan wants to merge 6 commits into
Open
Conversation
khairul-syazwan
marked this pull request as ready for review
September 7, 2026 02:10
khairul-syazwan
requested review from
LSriragavan,
SantanM,
brandantck,
hengxian-jiang,
jerome-ng,
maggie-li-yd,
p-hoffmann and
suwarnoong
as code owners
September 7, 2026 02:10
D2eDateField is a single date input with a leading calendar icon, for #3150's filter panel, which uses six of them in three From/To pairs. There is no range picker in the design. It composes VTextField, VMenu and VDatePicker rather than using the labs VDateInput. VDateInput exists in the pinned Vuetify 3.12.0, but vuetify's export map only exposes labs as vuetify/labs/<Name>, and the app aliases vuetify/components and vuetify/directives and nothing else. A bare labs import from libs/d2e-ui therefore resolves by plain node lookup and breaks the ATLAS isolated install, which puts vuetify under the app rather than above the library. The model value is an ISO YYYY-MM-DD string or null, in and out. The two conversion helpers are a plain .ts file so the test does not mount anything, and they read a Date's local parts instead of calling toISOString(), which returns the previous day east of UTC. D2eMenu gains a default slot so an activator can anchor arbitrary content. With no default slot the items path is unchanged.
Closes the Filters button the exploration toolbar left space for. It opens a 420px panel below it, anchored to its right edge, holding an author multi-select, a materialization-status multi-select and three From/To date pairs. Filtering is client-side; the list endpoint takes no filter parameters and the whole list is already in memory. The order is filter, then search, then sort. The author option list reads the unfiltered list, or selecting one author would remove every other option and the filter could not be widened again. Materialization status is derived from the presence of a cohort definition, the same test getBookmarkType() makes. It yields two options; stale is not derivable today and belongs to #3117. The CREATED pair renders disabled with a note. A bookmark carries only dateModified, so there is nothing to compare against; the predicate honours the range anyway, so the filter starts working once the backend surfaces a creation timestamp. Filtering dateModified and labelling it created would give wrong results that look right. de and zh strings need native review.
The other range tests use lastMaterialized, which reads a date field directly. lastUpdated goes through lastUpdatedMs(), which returns milliseconds and falls back across three fields, so that path had no coverage.
D2eDateField stopped inheriting attrs. Its root is a VMenu, which does not stop inheritance either, so a fallthrough attr travelled on to VOverlay and was merged onto the teleported .v-overlay div: the story's width sized the popup instead of the field, and a data-testid existed on two nodes once the picker had opened. forwardAttrs already puts every attr on the field. D2eDateField gained an opt-in clearable, and the panel turns it on. VDatePickerMonth in single mode always assigns the clicked day and never deselects, so a picked date could not be dropped short of Clear all selections, which also wipes the author and status filters. EMPTY_FILTERS is frozen through its nested ranges, not only at the top level. The comment promised a loud failure; a shallow freeze left EMPTY_FILTERS.created writable, which is the exact object the bug it describes corrupts. Enter on Clear all selections now clears. D2eMenu sets close-on-content-click false, and Vuetify's VMenu keydown handler then calls preventDefault() on Enter, cancelling the browser's Enter-activates-a-button default.
…d typeahead Three defects reported from manual verification. The Author filter is now searchable, so a growing author list can be narrowed by typing. D2eSelect gained a searchable prop that renders a VAutocomplete in place of a VSelect; everything else about the component is unchanged, and the prop is off by default because a fixed two-item list is faster to click than to type. Field text was not vertically centred. Two separate causes. In D2eSelect the <input> never inherited a font, so the UA's 13.33px Arial made the element 17px tall while its placeholder painted at 16px, and the two disagreed about the baseline; the input now takes the field's font and the input row fills the box with no vertical padding. In D2eDateField the field's own <input> took its intrinsic 34px and sat flush against the top edge of the 40px box, leaving 6px below it. Both now measure zero drift against the field's centre. The outline notch drew a line through the floating 'Data source' label. D2eSelect restated Vuetify's notch borders and gave BOTH ::before and ::after a top border. Only ::before takes Vuetify's opacity: 0 when the label floats, so the reworked ::after survived and struck the label through. Vuetify's own rules already read --v-field-border-width, so the restatement is removed rather than corrected. D2eDateField carried a copy of the same rule and is cleaned up with it.
VSelect no longer sits 9px above centre. Vuetify takes a VSelect's <input> out of flex flow (position: absolute with align-self: flex-start), so align-items: center on the row cannot reach it and it hangs from the top of the content box; removing the vertical padding moved that anchor from 16px to 0 and made it worse, not better. An absolutely positioned flex child takes its static position from align-self, so that is what now centres it. Measured zero drift on the select, the autocomplete and the date field. The LAST UPDATED range filtered on a date the card never shows. lastUpdatedMs() falls through to cohortDefinition.createdOn so that sorting has a total order, but the card's Last updated row stops at the bookmark and Atlas fields. A materialised record with no bookmark reads '-' there and was still matched on its materialisation instant, which also made LAST UPDATED and LAST MATERIALIZED the same filter for those records. The filter now reads what the card reads. Enter works in the date fields. A VMenu with close-on-content-click false treats Enter on its content as move-to-next-focusable-then-close, so Enter on the last field closed the whole panel, and its preventDefault meant Enter never opened a picker. D2eDateField now handles the key itself and keeps it from the enclosing menu. The CREATED note claimed no creation date exists. Atlas cohort definitions do carry one. Reworded in all three locales to say the range is disabled because it cannot apply to every card. menuProps.contentClass replaced Vuetify's own content class rather than merging with it, because mergeProps special-cases only class, style and on*. Vuetify's class is carried through by hand. This PR is the first time a D2eSelect dropdown ever opens in the app. D2eDateField merges the activator props instead of spreading them, so a consumer's own click handler cannot silently replace the one that opens the picker. Date tests now cover a UTC timestamp, which is the format the store actually produces.
khairul-syazwan
force-pushed
the
khairul-syazwan/d2e-exploration-page
branch
from
September 7, 2026 05:53
d6b9372 to
b849157
Compare
khairul-syazwan
force-pushed
the
khairul-syazwan/d2e-exploration-filters
branch
from
September 7, 2026 05:53
875dcbb to
efb8988
Compare
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.
Closes #3150.
Fifth in the Data Exploration redesign stack. Based on
khairul-syazwan/d2e-exploration-page(#3262), notdevelop— review that one first.What this adds
A Filters button in the exploration toolbar, right of the search field, opening a 420px panel anchored to its right edge. The panel filters the list by author, materialisation status, and three
From/Todate ranges.Filtering is client-side: the list endpoint accepts no filter parameters and the whole list is already in memory. The order is filter, then search, then sort.
D2eDateField— a single date input, ISOYYYY-MM-DDin and outlibs/d2e-uiD2eMenudefault slot, so an activator can anchor arbitrary contentlibs/d2e-uiD2eSelectsearchableprop, rendering aVAutocompletelibs/d2e-uiapps/vue-mri-ui-libExplorationFiltersPanel.vue, the Filters button, i18napps/vue-mri-ui-libDecisions a diff cannot show
The
CREATEDrange is disabled on purpose. ABookmarkcarries onlydateModified;CohortDefinition.createdOnis the materialisation instant, and the AtlascreatedOncovers Atlas records only. FilteringdateModifiedand labelling it "created" would give wrong results that look right. The control renders with a note saying why, andmatchesFiltershonours the range already, so it starts working with no logic change once a creation timestamp is surfaced. The audit columns exist on theuser_artifactrow butformatUserArtifactDatadoes not expose them — no ticket for that yet.Two materialisation statuses, not three. Derived from the presence of a cohort definition, which is the same test
getBookmarkType()makes. Stale is not derivable from any field today and belongs to #3117.LAST UPDATEDdoes not reuselastUpdatedMs(). That helper falls through tocohortDefinition.createdOnso sorting has a total order, but the card's own "Last updated" row stops at the bookmark and Atlas fields. A materialised record with neither reads-on the card, so matching it on its materialisation instant would drop or keep it for a reason nothing on screen explains — and would makeLAST UPDATEDandLAST MATERIALIZEDthe same filter for those records. The filter reads what the card reads; sorting keeps the three-field chain.D2eDateFieldcomposesVTextField+VMenu+VDatePickerrather than usingVDateInput.VDateInputdoes exist in the pinned Vuetify 3.12.0. Vuetify exposes labs only asvuetify/labs/<Name>, and this repo's vite alias list rewritesvuetify/componentsandvuetify/directivesand nothing else, so a bare labs import fromlibs/d2e-uiresolves by plain node lookup and breaks the ATLAS isolated install (npm install --workspaces=falseputs vuetify below the library, not above it). Adding a fourth alias to three vite configs for one labs component that can change between minors was the worse trade.The author option list reads the unfiltered list. Deriving it from the visible cards would remove every other option as soon as one author is selected, and the filter could never be widened again.
Two bugs fixed in shared components
The outline notch struck through the floating
Data sourcelabel.D2eSelectrestated Vuetify's notch borders and gave both::beforeand::aftera top border. Vuetify draws the top rule on::beforeand the bottom rule on::after, and only::beforetakesopacity: 0once the label floats — so the reworked::aftersurvived and drew a line across the label. The restatement is deleted; Vuetify's own rules already read--v-field-border-width.Field text was not vertically centred, from two separate causes.
D2eSelect's<input>never inherited a font, so the UA's 13.33px Arial made it 17px tall while its placeholder painted at 16px. And Vuetify takes aVSelect's input out of flex flow (position: absolutewithalign-self: flex-start), soalign-items: centercould not reach it.D2eDateField's input took its intrinsic 34px and sat flush against the top of the 40px box. All three fields now measure zero drift against the field centre.A trap worth knowing
A teleported Vuetify overlay lands in
.v-overlay-container, a direct child of<body>, outside.mri-app-vue-container. Every rule scoped to that container stops applying — includingstyle.scss's compensation for$reset: false, which is the only thing removing the browser's default2px insetborder from an<input>. Without it the border returns and draws a box around every placeholder. The panel carries its own unscoped block to replace it, the wayExplorationsPage.vuealready does for tooltips. This applies to any Vuetify overlay, so the dialogs in #3259 are worth a look.Validation evidence — agent reported
Run in the working checkout, not in CI. No end-to-end suite was run; do not read this as full E2E coverage.
libs/d2e-ui:bun run tokens:check,bun run test(50 tests),bun run build,bun run verify:dist— all pass.apps/vue-mri-ui-lib:npx vitest run— 947 pass, 1 fail. The failure isapp-segmented-button, a known local-environment artefact unrelated to this change; it passes in CI.TZ=Pacific/Auckland,America/Los_Angeles,UTCandAsia/Singapore. The date comparison is on the local calendar day and never usestoISOString(), which shifts the day east of UTC.vite build,--config vite.config.atlas.ts,--config vite.config.atlas-app.ts.plugins/ui/node_moduleshoists eslint 7.32, which cannot read the app's flateslint.config.js.Browser checks, hot-deployed and driven with Playwright against a local runtime, 19 assertions:
Not run yet, so that is the real complement.LAST UPDATEDToof 2025-07-01 empties the grid; Clear all selections restores it.admin Author narrows to the one match;zzzshows no data.Fromdisables the ten earlier days in theTopicker.Measured geometry against the design frame: panel 420 wide with its right edge on the button's, selects 388x40, date fields 186x40.
Reviewer notes
CREATEDpair, which the frame does not draw.3 Sept 2026. Locale-aware formatting is deferred to design; the fix belongs in the library component, which must not import the app'sDateUtils.