Skip to content

Add GhostText parameter to BitTextField (#11390)#12269

Open
msynk wants to merge 3 commits intobitfoundation:developfrom
msynk:11390-blazorui-textfield-ghosttext
Open

Add GhostText parameter to BitTextField (#11390)#12269
msynk wants to merge 3 commits intobitfoundation:developfrom
msynk:11390-blazorui-textfield-ghosttext

Conversation

@msynk
Copy link
Copy Markdown
Member

@msynk msynk commented Apr 21, 2026

closes #11390

Summary by CodeRabbit

  • New Features

    • Added inline ghost text (suggestion) feature to TextField components for both single-line and multiline inputs. Users can accept suggestions via Tab key or click interaction. Includes customizable styling for ghost text overlay.
  • Documentation

    • Added demo examples showcasing the new ghost text feature with practical use cases.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7ce6d2c9-c16b-47c2-80dc-8044da8d7648

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This pull request implements a GhostText feature for the BitTextField component, allowing inline suggestion display. Changes include new parameters, event callbacks, JavaScript interop for ghost text insertion and scroll synchronization, CSS styling for overlay positioning, and demo examples.

Changes

Cohort / File(s) Summary
Core Component Markup
BitTextField.razor
Added wrapper div with ghost text overlay container. When GhostText is present, displays current value and clickable ghost suggestion text wired to acceptance handler.
Component Logic & Parameters
BitTextField.razor.cs
Added GhostText parameter and OnGhostTextAccepted callback. Introduced HandleGhostTextAccept() method to append ghost text and trigger scroll-to-end. Extended initialization to setup ghost text JS handler.
Styling
BitTextField.scss
Added new CSS classes: .bit-tfl-ghw (wrapper flex container), .bit-tfl-gho (absolutely positioned overlay), .bit-tfl-ghv (transparent value display), .bit-tfl-ghs (clickable ghost text span with primary color).
JavaScript Interop
BitTextField.ts, BitTextFieldJsRuntimeExtensions.cs
Added setupGhostText() to intercept Tab key, insert ghost text at cursor, and sync overlay scrolling. Added scrollToEnd() for cursor positioning. Added extension methods for JS runtime invocation.
Styling Configuration
BitTextFieldClassStyles.cs
Added three new properties: GhostTextWrapper, GhostTextOverlay, and GhostText for customizable CSS hooks.
Demo Pages
BitTextFieldDemo.razor, BitTextFieldDemo.razor.cs
Replaced "Trim" demo with new "GhostText" demo showcasing single-line and multiline inputs with suggestion display. Added demo state management and suggestion generator. Renumbered subsequent demo examples from example12 through example19.

Sequence Diagram

sequenceDiagram
    participant User
    participant Input as BitTextField Input
    participant Overlay as Ghost Text Overlay
    participant JS as JavaScript Handler
    participant Component as Blazor Component
    participant Callback as OnGhostTextAccepted

    User->>Input: Types text
    Input->>Component: OnChange event
    Component->>Component: Update CurrentValue
    Component->>Overlay: Display ghost suggestion
    User->>Overlay: Presses Tab on ghost text
    JS->>JS: Intercept Tab keydown
    JS->>Input: Insert ghost text at cursor
    JS->>Input: Update cursor position
    JS->>Input: Dispatch input event
    Input->>Component: Value updated
    Component->>Component: Append ghost text to value
    Component->>Component: Schedule scroll-to-end
    Component->>JS: Trigger scrollToEnd()
    JS->>Input: Scroll to end
    Component->>Callback: Invoke OnGhostTextAccepted
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A whisper of text dances in the field,
Ghost suggestions blooming as you yield,
Tab the magic, watch it flow,
Inline hints that help you go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a GhostText parameter to the BitTextField component. It is clear, concise, and specific.
Linked Issues check ✅ Passed All code changes implement the GhostText feature for BitTextField as specified in issue #11390, including parameter addition, JS interop, styling, and demo updates.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the GhostText feature; no unrelated modifications were detected in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a “ghost text” (inline suggestion) capability to BitTextField, aimed at AI/autocomplete scenarios, and wires it into the demo/documentation so users can discover and try the feature.

Changes:

  • Added GhostText and OnGhostTextAccepted parameters to BitTextField, plus corresponding markup/CSS to render an overlay suggestion and accept it.
  • Implemented JS support for accepting ghost text via Tab, syncing overlay scroll, and scrolling caret to the end when needed.
  • Updated the demo page and API/style tables to include the new parameters and styling hooks.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs Adds new public parameters and acceptance handler; hooks JS setup and post-accept scroll behavior.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor Wraps input/textarea to host a ghost-text overlay and click acceptance UI.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scss Adds styling for the ghost overlay/wrapper and inline suggestion rendering.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.ts Adds setupGhostText (Tab acceptance + scroll sync) and scrollToEnd.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextFieldJsRuntimeExtensions.cs Exposes new JS interop calls for ghost setup and scroll-to-end.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextFieldClassStyles.cs Adds new styling hooks for ghost wrapper/overlay/span.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor Adds a new GhostText demo example and renumbers subsequent examples.
src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs Adds GhostText demo fields/helpers and updates demo API/style metadata + sample code strings.

Comment thread src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor Outdated
Comment thread src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs Outdated
Comment thread src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs (1)

412-420: Consider gating setupGhostText on GhostText usage.

This JS call attaches a keydown and a scroll listener to every BitTextField instance on first render, even when the component will never use ghost text. Since GhostText may change after first render, a static gate is tricky, but you could lazily set up on first non-null assignment, or invoke it only when GhostText.HasValue() is true during first render and (re)invoke on subsequent renders when it transitions from null → non-null. Purely an optimization; functional behavior is unaffected.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs`
around lines 412 - 420, The JS ghost-text setup is currently invoked on every
first render via _js.BitTextFieldSetupGhostText regardless of GhostText, causing
unnecessary listeners; change BitTextField to only call
_js.BitTextFieldSetupGhostText when GhostText.HasValue() on firstRender, and
also detect transitions from null → non-null on subsequent renders (track a
private field like _previousGhostText) to lazily invoke the setup when GhostText
is assigned later; ensure you update _previousGhostText after invoking setup so
the setup runs only once per transition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor`:
- Around line 72-82: The multiline ghost-text overlay renders an unwanted
newline/indentation between the CurrentValueAsString span and the GhostText span
(causing visible whitespace due to white-space: pre-wrap); update the multiline
branch in BitTextField.razor (the block that checks GhostText.HasValue()) so the
two span elements for CurrentValueAsString and the clickable GhostText (which
calls HandleGhostTextAccept and uses Classes?.GhostText/Styles?.GhostText) are
rendered adjacent with no intervening newline or spaces (same approach as the
single-line branch) to prevent the extra whitespace from being emitted.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scss`:
- Around line 260-275: Replace the deprecated CSS property word-wrap with the
standard overflow-wrap in the BitTextField stylesheet: update occurrences inside
the .bit-tfl-gho, .bit-tfl-ghv, and .bit-tfl-ghs rules so they use
overflow-wrap: break-word (preserving the original behavior) instead of
word-wrap, leaving all other declarations (display, white-space, line-height,
padding) unchanged.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.ts`:
- Around line 56-81: The Tab handler in setupGhostText currently inserts
ghostText at the caret, bypasses ReadOnly/IsEnabled guards and doesn’t invoke
the C# acceptance callback; update the Tab branch so it mirrors
HandleGhostTextAccept: first check the element’s readonly/disabled state (or
call into C# to reuse the same guards), append ghostSpan.textContent to the end
of inputElement.value (not at selectionStart..selectionEnd), set the caret to
the end, dispatch the input event, and ensure OnGhostTextAccepted is invoked
(preferably by calling the existing HandleGhostTextAccept via
DotNetObjectReference to centralize behavior and state checks).

In
`@src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs`:
- Around line 864-874: The demo's GetGhostSuggestion helper should split on all
whitespace and remove empty entries to match the displayed example and handle
multiline input: update the implementation in GetGhostSuggestion to use
value.Split(new[] {' ', '\t', '\r', '\n'},
StringSplitOptions.RemoveEmptyEntries) (or the equivalent overload) when
computing lastWord, remove the commented-out alternative line, and also update
the example11CsharpCode string so its shown code matches the actual
implementation.

---

Nitpick comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs`:
- Around line 412-420: The JS ghost-text setup is currently invoked on every
first render via _js.BitTextFieldSetupGhostText regardless of GhostText, causing
unnecessary listeners; change BitTextField to only call
_js.BitTextFieldSetupGhostText when GhostText.HasValue() on firstRender, and
also detect transitions from null → non-null on subsequent renders (track a
private field like _previousGhostText) to lazily invoke the setup when GhostText
is assigned later; ensure you update _previousGhostText after invoking setup so
the setup runs only once per transition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: be397bb8-5134-47c9-b2ea-a67eab85361e

📥 Commits

Reviewing files that changed from the base of the PR and between 19f78b8 and a1e00da.

📒 Files selected for processing (8)
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scss
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextFieldClassStyles.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextFieldJsRuntimeExtensions.cs
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor
  • src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs

Comment thread src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Comment thread src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Comment on lines +739 to +744
var ghostSpan = component.Find(".bit-tfl-ghs");
ghostSpan.Click();

Assert.AreEqual(ghostText, acceptedGhost);
Assert.AreEqual(value + ghostText, component.Instance.Value);
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These ghost-text acceptance tests rely on ghostSpan.Click() updating the component value and invoking OnGhostTextAccepted, but the click acceptance behavior is implemented in JS (setupGhostText) and there is no Blazor @onclick handler on .bit-tfl-ghs. bUnit won’t execute the JS to wire up listeners, so these assertions will fail. Consider simulating acceptance by calling the JS-invokable .NET method directly (and asserting only the callback), or moving the click-accept logic into Blazor so it can be unit-tested here.

Copilot uses AI. Check for mistakes.
Comment on lines +762 to +767
var ghostSpan = component.Find(".bit-tfl-ghs");
ghostSpan.Click();

Assert.AreEqual(1, callCount);
Assert.AreEqual(ghostText, lastAccepted);
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also depends on JS-wired click handling on .bit-tfl-ghs to invoke OnGhostTextAccepted. Since the component doesn’t register a Blazor click handler for the ghost span, bUnit won’t trigger the acceptance path and the callback assertions won’t be reliable. Adjust the test to validate behavior that occurs in .NET (e.g., invoking the JSInvokable entry point) or cover the JS click interaction via an integration/E2E test harness.

Copilot uses AI. Check for mistakes.
Comment on lines +785 to +790
var ghostSpan = component.Find(".bit-tfl-ghs");
ghostSpan.Click();

Assert.IsNull(acceptedGhost);
Assert.AreEqual(value, component.Instance.Value);
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the other click-based ghost-text tests, this relies on JS behavior (preventing acceptance when disabled) that won’t run in bUnit because .bit-tfl-ghs has no Blazor click handler. As written, ghostSpan.Click() won’t exercise the disabled/read-only acceptance logic at all. Consider restructuring these tests to invoke the .NET entry point and assert it early-returns when IsEnabled is false / ReadOnly is true, or move this interaction to a JS-capable integration test.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The GhostText feature of the BitTextField component

4 participants