Skip to content

fix(code-block)!: errors following a full review of the component (#DS-5482) - #1988

Draft
artembelik wants to merge 2 commits into
mainfrom
fix/code-block-signals
Draft

fix(code-block)!: errors following a full review of the component (#DS-5482)#1988
artembelik wants to merge 2 commits into
mainfrom
fix/code-block-signals

Conversation

@artembelik

Copy link
Copy Markdown
Contributor

What

A full review of code-block, in the same shape as the 20.3.0 component reviews.

maxHeight lied about being required

readonly maxHeight = input<number, unknown>(undefined!, { transform: numberAttribute });

A code block with no [maxHeight] binding reported undefined from a non-nullable type: an assignment to a number held undefined, and maxHeight() > 0 was a NaN comparison that never matched. It reports number | undefined now — the same pathology, and the same fix, as KbqSplitButton.disabled in the first review wave.

KbqCodeBlockHighlight.file was write-only

@Input({ required: true })
set file(file: KbqCodeBlockFile) {  this.highlight(file); }

A required input with no getter, whose setter kicked off highlighting as a side effect — which is exactly why the automated signal migration skipped it. It is input.required() driven by an effect now, so it can finally be read.

The max-height binding never re-evaluated on its own

calculatedMaxHeight was a getter read from [style.max-height.px], so it only recomputed when something else marked the view dirty. It is a computed over maxHeight and viewAll now.

That binding turned out to have no test coverage at all — the new test caught a mistake I made in the very same change (binding the computed without calling it), which is worth knowing about the component.

What deliberately stays an accessor input

softWrap, viewAll, canDownload, activeFileIndex and files are backed by signals now, but stay accessor inputs with the same types and the same two-way …Change outputs. They are written by the component (toggleSoftWrap(), onSelectedTabChange()) as well as by the binding, and a model() cannot carry the booleanAttribute / numberAttribute transform a valueless attribute needs — the same conclusion checkbox and the reviewed button-toggle reached.

No call site changes for those five. What does change is that a template reading them re-renders on its own instead of waiting for change detection, and the // TODO: Skipped for migration comments are gone.

Migration

code-block-optional-max-height runs from ng update @koobiq/components@20. It is warn-only: narrowing number | undefined back to number is a decision the call site owns, and turning a file write into a [file] binding is a template edit.

Documented in docs/guides/migration.{en,ru}.md, section 18.

Testing

  • code-block.spec.ts: 50 → 53 tests. New coverage for the unbound maxHeight, the max-height style the component applies and drops on viewAll, and the now-readable file on the highlight directive.
  • code-block-optional-max-height/index.spec.ts: 4 tests.
  • Full packages/components (4996 tests) and packages/schematics (438 tests) suites pass.
  • check-api is in sync.

BREAKING CHANGE

🤖 Generated with Claude Code

`maxHeight` was published as `InputSignal<number>` over an `undefined!` default,
so a code block with no `[maxHeight]` binding reported `undefined` from a
non-nullable type — an assignment to a `number` held `undefined`, and any
arithmetic on it produced NaN. It reports `number | undefined` now.

`KbqCodeBlockHighlight.file` was a write-only required input: a setter with no
getter that kicked off highlighting as a side effect. It is a required signal
input driven by an effect now, so it can finally be read.

The `max-height` applied while `viewAll` is off is a `computed`. It was a getter
read from a `[style.max-height.px]` binding, so it only re-evaluated when
something else marked the view dirty.

`softWrap`, `viewAll`, `canDownload`, `activeFileIndex` and `files` are backed by
signals. They stay accessor inputs with the same types and the same two-way
outputs: the component writes them as well as the binding, and a `model()` cannot
carry the `booleanAttribute` / `numberAttribute` transform they need.

BREAKING CHANGE: `KbqCodeBlock.maxHeight` reports `number | undefined` instead of
`number`, and `KbqCodeBlockHighlight.file` is a readable required signal input
rather than a write-only setter. Reported by the
`code-block-optional-max-height` schematic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added bug Something isn't working breaking changes labels Sep 3, 2026
`files.length < activeFileIndex` left the first out-of-range index in place:
three files with an active index of 2, shrunk to two, kept index 2 and the
template rendered `files[2].content` from undefined. The new test fails against
the old comparison and passes against `<=`.

The review's own narrowing fix hoisted the `maxHeight` read out of
`checkOverflow`, freezing the threshold at `ngAfterViewInit` so the resize
observer compared against a stale value. The read is back inside the callback.

`KbqCodeBlockHighlight` started a second highlight.js import for any `file` that
arrived while the first was still loading, registering the line-numbers plugin
twice, and neither continuation was cancelled on teardown. Deduped behind
`loadOnce()` with an `onCleanup` guard, and the promise is cleared on failure so
a later file change can still retry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking changes bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant