Fluent-next: extend the component tier to every component that owns v… - #34977
Open
EugeniyKiyashko wants to merge 5 commits into
Conversation
…ariables Wave F published --dx-* for 50 of 84 components; the other 34 (scheduler, chat, fileManager, diagram, pivotGrid, cardView, list, gantt, form, popup, calendar, treeView, tabs, ...) were checked by neither the strict grammar nor the tier, so customization through the tier did not work for them at all. migrated 50 -> 78, tier 798 -> 2049 names in 76 folders. The six components left out own no variables (autocomplete, dropDownBox, multiView, rangeSlider, responsiveBox, speechToText). cardView stopped being an exemption: its 111 BEM names moved onto the grammar and exemptFolders is now empty; the shared $cardview-* parameters of base keep their spelling, so the legacy themes are untouched. Three defects the gates caught: -var(--dx-...)/2 from a mixin that forwards its parameters into unguarded math, a cross-scope duplicate on .dx-radio-value-container, and math.div(var(--dx-...), 2) printing a division without calc(). The wiring codmod grew a WIRING_UNSAFE list so a later run cannot silently undo those reverts. Per-selector resolve diff: 0 lost, 0 changed values in both modes (+1802 tier declarations). generic/material/fluent byte-identical. jest 81/81, stylelint 0, naming:check 0, calc budget 349 -> 348.
dmlvr
approved these changes
Aug 28, 2026
…nder outside their root
CI on the previous commit was red: 180 screenshots in 10 jobs. All of one class —
the tier resolves only under the root it is declared on, so a rule that paints an
element JS renders elsewhere lost the whole declaration. The resolve diff cannot
see this (values are unchanged) and neither can the cross-scope gate (no
duplicate); only the cascade shows it.
Four roots were missing:
- pagination: dx-pager and dx-pagination are MUTUALLY EXCLUSIVE
(pagination/content.tsx, getClasses) — a grid's pager runs in
grid-compatibility mode and never carries dx-pagination, which emptied the
tier in every grid and moved every screenshot with a pager;
- pivotGrid: the field chooser is a widget of its own, shown in a popup;
- htmlEditor: the AI dialog is a popup carrying .dx-aidialog;
- chat: the message context menu is overlay content;
- cardView: dragging a header item puts a COPY in .dx-sortable-dragging, and
`.dx-sortable-dragging > .dx-cardview-header-item` paints that copy.
cardView's wiring now carries the tier: 81 of the 112 mirrors passed base a frozen
SCSS value, so the published names did nothing. base prints all of them (one
guarded if(type-of == number) — the var takes its calc branch, which is the
documented var-safe form), and the header-panel compensation now recalculates when
the padding is overridden, as the calc policy asks.
Resolve diff against the pre-wave parent, both bundles built by the same target:
0 lost, 0 changed, +2155 tier declarations. One new non-tier pair — the minifier
stops merging base's and the theme's rule for the alpha-channel label now that the
theme's value is a var(); the theme's rule still wins and the computed margin is
the same. generic/material/fluent byte-identical.
Runtime reachability audit extended from 35 widgets to 52 (all wave-H components,
their satellites and the drag portals): 9248 checks / 0 holes. It also stopped
judging elements that do not render — a closed overlay keeps its content inline and
paints nothing, which was 3 false positives — and its synthetic AI-chat portal now
carries .dx-chat, the class the real widget has (grid_core/ai_chat renders a Chat).
jest 81/81, stylelint 0, naming:check 0.
…in screenshots
The wave's defects were all one shape — a rule paints an element the component's
root does not reach — and nothing but the screenshots caught them. Now three gates
do, and the roots the second CI run exposed are fixed.
Roots added (each verified on the built bundle): cardView column chooser,
chat confirmation popup, htmlEditor add-image dialog, filterBuilder field and
operation overlays, list context menu, pivotGrid DnD clone container
(.dx-pivotgrid-fields-container — the dragged field lost its background, border and
shadow), fileManager dialogs and context menu, diagram properties/toolbox popups.
Gates:
- reachability.mjs: a tier read whose selector no root covers must be a REVIEWED
scope. 488 scopes recorded in nested-scopes.json; a new one fails the run with
the two ways out (add a root, or prove nesting in the runtime audit). This is
what .dx-pager would have hit: pagination's rules target a class the component
only carries inside a grid.
- fluent-next-naming: every publishing component must appear in the audit gallery
— the vacuous pass that let wave F stay at 35 widgets while 28 more shipped.
- the audit page now counts roots that match NO element: 30 of them, wave F's
included, were never exercised. 16 remain and are printed on every run.
The gallery reached 12200 checks / 0 holes (was 9248). Two of its own defects are
fixed: gantt and diagram were loaded after dx.all (E1041, both widgets silently
absent), and the synthetic dialog markup lacked .dx-form / .dx-fileuploader /
.dx-treeview, which the real widgets do render — 8 phantom holes.
Resolve diff against the pre-wave parent: 0 lost, 0 changed, +3110 tier
declarations; generic/material/fluent byte-identical. jest 82/82, stylelint 0,
naming:check 0.
apps/demos: looksSameComparisonOptions commented out on request, so the next run
compares without the tolerance that has been hiding small drifts. It must be
restored before merge — it loosens comparison for every theme, not just this one.
EugeniyKiyashko
force-pushed
the
fluent-next/component-tier
branch
from
August 30, 2026 18:16
6d37d8f to
15c7a66
Compare
…ns, mask a known flake
Two more elements the tier could not reach, both read off the DOM the CI
screenshots produced:
- cardView's drop indicator is a dxSortable placeholder built in the VIEWPORT
with the widget's own class on it, so `.dx-cardview-header-item-sort-indicator`
painted an element no root contained. Legacy paints it with literals and before
the tier it read `--dxds-*` from `:root`; the tier names resolved to nothing,
`border-width` collapsed to 0 and the 4px indicator vanished from six
screenshots. Verified live after the fix: 4x32, 2px border, 8px radius.
- the scheduler tooltip renders inside `.dx-scheduler-overlay-panel`, which was
not a root: the appointment marker, the title weight and the paddings were all
dropped declarations.
Both classes are now exercised by the audit gallery (12215 checks / 0 holes).
Etalons, 13 files. The scheduler ones lost a 1px line under the left header cell —
that is PR DevExpress#34753 (Scheduler: Multilevel grouping) restructuring the header borders
with `border-top-color: transparent`, a deliberate change that never got its
screenshots re-shot; the same single line, in the same place, in fluent-next,
fluent and material alike. The two TreeView drag-and-drop demos drifted before this
branch: their etalons still carry #d8d8d8 for a hairline the token package made
#e1e1e1, plus a 1px shift that antialiasingTolerance used to swallow. Every
replacement was accepted pixel by pixel against the file it replaces.
dropdownbox_multiple_selection_gridbox is not a theme difference at all: the grid's
select checkbox is caught focused, and fluent-next paints that state darker
(--dx-check-box-icon-checked-bg-focused, #003c70, against #0f6cbd at rest). It is a
flake — two frameworks of five — and fluent already carries a mask over exactly
that square, so fluent-next gets the same mask instead of a re-shot etalon.
Resolve diff against the pre-wave parent: 0 lost, 0 changed, +3470 tier
declarations; generic/material/fluent byte-identical. jest 82/82, stylelint 0,
naming:check 0.
…t the default `looksSameComparisonOptions` (tolerance 20, antialiasingTolerance 20) was applied to every screenshot of every demo in every theme, and it was spread AFTER the options a test passed, so a demo could not even opt out of it. It hid real regressions for months. Turning it off for one run surfaced: a 1px line under the scheduler's header cell that PR DevExpress#34753 removed deliberately and nobody re-shot; a hairline the token package changed from #d8d8d8 to #e1e1e1; a whole TreeView shifted by a pixel; a checkbox captured in its focused state. Now the default carries `textDiffTreshold` only, per-demo options win over it, and the four demos whose pixels really do wobble ask for the looser comparison themselves — three through their own visualtestrc.json, one at the call site, each with the measurement that justifies it. The schema grew a `comment` field so an exception can carry its reason. Everything else is compared at the comparator's own defaults, which is the point: a drift in a demo that nobody marked as noisy now fails instead of passing quietly.
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.
No description provided.