release: @bquery/ui 1.15.0 - #18
Conversation
|
Important Review skippedToo many files! This PR contains 151 files, which is 51 over the limit of 100. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (151)
You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
…regex `[^<>]` already matches newlines — a character class is unaffected by the `s` flag — so `(?:[^<>]|\n)*?` gave the group two ways to match the same character. CodeQL flagged it: an unterminated tag such as `<A` followed by many newlines backtracks exponentially. One unambiguous class matches the same text in linear time; the audit still finds the attributes it is meant to catch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Summary
Releases
@bquery/ui1.15.0: the catalog grows from 31 to 49 components, the theme layer gains a semantic tier that actually survives the shadow boundary, and four defects that made components unusable in a real browser are fixed.package.jsonis already at1.15.0and every pinned CDN snippet in the README and docs matches, so no version bump is part of this PR. npm currently serves1.11.0;1.12–1.14were never published, so this release closes that gap.Motivation
Three things were wrong that no test could see, because the suite runs in happy-dom and these only appear in a real browser:
bq-tabsfroze the page.updated()writespanel.idonto its light-DOM panels while aMutationObserverwatches the same subtree forid.setAttributeemits a mutation record even when the value is unchanged, so the observer re-entered its own render and pinned the main thread. Any page using the documented panel markup hung on load —DOMContentLoadednever fired.connected()runs twice for most elements. The runtime mounts an element fromattributeChangedCallbackduring upgrade, and theconnectedCallbackthat follows sees it already mounted and takes its reconnect path. Every listener, observer and timer was registered twice, and the runtime replaced its own scope reference in between, so the first set could never be released.bq-dropdown-menutoggled twice per click and therefore never opened.:host-context()exists only in Chromium, and it sat in a selector list next to:host([data-theme="dark"])— one unknown selector invalidates the whole list, so those browsers dropped the entire dark theme. Separately, alerts, badges, chips, avatars and tags painted from--bq-color-*-100/700; the palette is scheme-independent, so those surfaces stayed near-white on a dark page.Changes included
Fixes
bq-tabs: light-DOM sync now pauses the observer and writes only what changes.utils/component.ts:bind()unwinds a previous scope on re-entry, collapsing the doubleconnected()onto one live set of registrations. AddsscopeOf()for hooks that need to append teardown without resetting.--bq-scheme-*channel instead of:host-context(), sodata-themeworks in every browser and on any subtree, not just the root.autois resolved in CSS, so an OS-level switch repaints with nomatchMedialistener.bq-switch: the track is a sibling of the input again, soinput:checked ~ .trackmatches and the on-state renders.bq-card: header and footer bands are hidden when nothing is slotted (:emptycannot see this — the band always contains a<slot>).bq-radio: radios sharing anamenow clear each other, scoped to the nearest<form>. Each radio lives in its own shadow root, so the platform's one-per-name rule could not see the siblings.scope,colspan,styleandspellcheckwere being stripped silently — the table's "no data" cell spanned one column instead of the row.shadowRoot.innerHTML, so every render destroyed the focused field. Focus and caret now survive the renders a component's own interactions cause, including the very first one (focusing a combobox opened it, which re-rendered it away).Additions
bq-date-picker(UTC date maths,Intl-localised, full keyboard grid),bq-tree(ARIA tree pattern, roving tab stop, typeahead),bq-pin-input,bq-meter,bq-copy-button,bq-icon— plusbq-combobox,bq-tag-input,bq-file-upload,bq-popover,bq-timeline,bq-stepper,bq-rating,bq-number-input,bq-kbd,bq-banner,bq-button-group,bq-avatar-groupfrom earlier work on this branch.<svg>is impossible — the sanitizer forbidssvgandallowTagscannot re-open it — which is why the library drew its affordances with text characters (×,▼,✓) whose weight and baseline were up to the platform font. Icons now inheritcurrentColorand stay a single path at any DPI.iconCss()lets a component pull in only the glyphs it draws.--bq-surface-raised/overlay/sunken), translucent interaction states, per-intent focus rings,--bq-intent-*for tinted status surfaces, a shared control-height scale, and dark-mode elevation.accent-color— the only hook the platform offers. They are drawn now, withforced-colorssupport.Guards
New tests that fail against the previous code:
tests/render-loops.test.ts— asserts a second pass over already-correct DOM writes nothing, which is the invariant that makes the tabs hang impossible.tests/component-utils.test.ts—bind()re-entry.tests/sanitizer-allowlist.test.ts— parses every render template and fails on an attribute the sanitizer would drop.tests/component-css.test.ts— parses each component's shipped stylesheet, and rejects any background painted from a 50/100/200 palette step.Validation
bun run lintbun run lint:typesbun test— 657 passing across 42 filesbun run build(lib, CDN, types) andbun run build:docsAlso rendered in headless Chromium across every component in light and dark mode, plus the overlays driven open with real clicks. That is how the four defects above were found; the existing suite passed the whole time.
Checklist
Notes for reviewers
1.15.0is the intended version before merging.src/utils/styles.tsinstalls a document stylesheet on import. It is the one module every entrypoint pulls in, so it is the only place the theme channel can be registered regardless of how the package is imported. It defines custom properties under[data-theme]and is inert until a page sets the attribute — but it is a side effect that did not exist before.bind()changed contract: calling it twice for one owner now unwinds the first scope. Every component calls it exactly once at the top ofconnected(), so nothing in-tree is affected, but the docstring andAGENT.mdspell out the new rule.connected()is a@bquery/bquerybehaviour, not this repo's.bind()neutralises it for anything registered on the scope; anything else inconnected()still runs twice. Fixing it properly belongs in the framework.@bquery/ui/components/badgedoes not pull in the icons.🤖 Generated with Claude Code