Add GhostText parameter to BitTextField (#11390)#12269
Add GhostText parameter to BitTextField (#11390)#12269msynk wants to merge 3 commits intobitfoundation:developfrom
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
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
GhostTextandOnGhostTextAcceptedparameters toBitTextField, 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. |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cs (1)
412-420: Consider gatingsetupGhostTextonGhostTextusage.This JS call attaches a
keydownand ascrolllistener to everyBitTextFieldinstance on first render, even when the component will never use ghost text. SinceGhostTextmay change after first render, a static gate is tricky, but you could lazily set up on first non-null assignment, or invoke it only whenGhostText.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
📒 Files selected for processing (8)
src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razorsrc/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.razor.cssrc/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scsssrc/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.tssrc/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextFieldClassStyles.cssrc/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextFieldJsRuntimeExtensions.cssrc/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razorsrc/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/TextField/BitTextFieldDemo.razor.cs
| var ghostSpan = component.Find(".bit-tfl-ghs"); | ||
| ghostSpan.Click(); | ||
|
|
||
| Assert.AreEqual(ghostText, acceptedGhost); | ||
| Assert.AreEqual(value + ghostText, component.Instance.Value); | ||
| } |
There was a problem hiding this comment.
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.
| var ghostSpan = component.Find(".bit-tfl-ghs"); | ||
| ghostSpan.Click(); | ||
|
|
||
| Assert.AreEqual(1, callCount); | ||
| Assert.AreEqual(ghostText, lastAccepted); | ||
| } |
There was a problem hiding this comment.
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.
| var ghostSpan = component.Find(".bit-tfl-ghs"); | ||
| ghostSpan.Click(); | ||
|
|
||
| Assert.IsNull(acceptedGhost); | ||
| Assert.AreEqual(value, component.Instance.Value); | ||
| } |
There was a problem hiding this comment.
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.
closes #11390
Summary by CodeRabbit
New Features
Documentation