Conversation
* feat: Add ASHX and AXD URL handling to middleware (#423) - Add EnableAshxHandling and EnableAxdHandling options (default: true) - Add AshxRedirectMappings dictionary for custom .ashx redirects - AshxHandlerMiddleware: 410 Gone default, 301 redirect with mapping - AxdHandlerMiddleware: 404 for most .axd, 410 Gone for ChartImg.axd - Existing .aspx rewriting behavior unchanged Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: Add deprecation guidance for Web Forms patterns (#438) Document migration guidance for runat=server, ViewState, UpdatePanel, Page_Load/IsPostBack, ScriptManager, and 3 additional patterns. 887-line guide with before/after code examples. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: Add 46 middleware tests for ASHX/AXD handling (#423) TestServer-based tests covering .ashx→410, .axd→404, ChartImg→410, custom redirect mappings, options toggling, .aspx regression guards, and edge cases (mixed case, query strings, substrings). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: Add TextBoxWatermarkExtender
Implements placeholder text for TextBox controls, matching the original
Ajax Control Toolkit TextBoxWatermarkExtender behavior.
- WatermarkText property for placeholder text
- WatermarkCssClass for styling the watermark state
- JS behavior handles focus/blur/input events
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: Add DragPanelExtender
Makes panels draggable by a handle element. Matches the original
Ajax Control Toolkit DragPanelExtender behavior.
- DragHandleID property to specify the drag handle element
- Falls back to entire target if no handle specified
- Handles edge cases (text selection, left-click only)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: Add ResizableControlExtender
Allows users to resize elements by dragging a handle. Matches the original
Ajax Control Toolkit ResizableControlExtender behavior.
- HandleCssClass for styling the resize handle
- ResizableCssClass applied while actively resizing
- Min/Max width and height constraints
- Creates resize handle if not found
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add DropShadowExtender and AlwaysVisibleControlExtender
- DropShadowExtender: CSS box-shadow with opacity, width, rounded corners, position tracking
- AlwaysVisibleControlExtender: Fixed positioning with 9 anchor points, offsets, animation support
- New enums: HorizontalSide, VerticalSide for positioning
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add RoundedCornersExtender and UpdatePanelAnimationExtender
- RoundedCornersExtender: Selective border-radius with BoxCorners enum, optional background color
- UpdatePanelAnimationExtender: MutationObserver-based update detection with CSS class and fade animations
- New enum: BoxCorners (flags for corner selection)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add PasswordStrength and MaskedEditExtender
- PasswordStrength: Real-time password quality indicator with text/bar modes, 5 strength levels
- MaskedEditExtender: Input masking for phone/date/SSN with configurable mask patterns
- New enums: DisplayPosition, StrengthIndicatorType, MaskType, InputDirection, AcceptNegative, DisplayMoney
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add ValidatorCalloutExtender and HoverMenuExtender
- ValidatorCalloutExtender: Validation error callout bubbles with positioning, highlight CSS, icons
- HoverMenuExtender: Hover-triggered popup menus with delays and positioning
- New enum: PopupPosition (TopLeft, TopRight, BottomLeft, BottomRight)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add SlideShowExtender
- SlideShowExtender: Image carousel with auto-play, navigation controls, slide titles/descriptions
- Client-side slide management via Slides parameter or data-slides attribute
- Play/pause, next/previous controls with configurable interval
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add ListSearchExtender and BalloonPopupExtender
- ListSearchExtender: Type-to-filter for ListBox/DropDownList with Contains/StartsWith matching
- BalloonPopupExtender: Styled tooltip balloons with pointer arrows, multiple trigger modes
- New enums: PromptPosition, QueryPattern, BalloonStyle, BalloonSize, BalloonPosition
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add docs, tests, and migration guide for new ACT extenders
Documentation (12 new files in docs/AjaxToolkit/):
- TextBoxWatermark, DragPanel, ResizableControl, DropShadow
- AlwaysVisibleControl, RoundedCorners, UpdatePanelAnimation
- PasswordStrength, ValidatorCallout, SlideShow, ListSearch, BalloonPopup
- Updated index.md with all new components
bUnit Tests (6 new test files, 104 tests):
- TextBoxWatermarkExtender, DropShadowExtender, PasswordStrength
- ValidatorCalloutExtender, ListSearchExtender, BalloonPopupExtender
Migration Toolkit:
- Updated AJAX-TOOLKIT.md with migration patterns for all 12 extenders
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Set correct ACT defaults for HoverMenuExtender (HoverDelay=300, PopDelay=100)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(.squad): log ajax nav session and dashboard deferral decision
- Log AJAX nav fix session (alphabetize items, collapsed state on desktop)
- Document decision to defer AJAX Control Toolkit from health dashboard
- Route Jubilee agent for sample nav updates
- Merge inbox decision to decisions.md
* fix(samples): alphabetize AJAX nav items and start collapsed
- Sort GetByCategory() results alphabetically by name
- Exclude AJAX category from desktop auto-expansion
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(.squad): Jubilee history + ajax nav decision
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(tests): scope DetailsView pager locator to main content
The page-wide locator a:has-text('2') was matching collapsed nav
sidebar links after AJAX nav alphabetization. Scope to .main-content
to only match pager links within the component demo area.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: strip [RouteData] from method params instead of replacing with [Parameter] The [RouteData] → [Parameter] conversion placed [Parameter] on method parameters, but ParameterAttribute targets properties only (CS0592). This caused build failures in ProductDetails.razor.cs and ProductList.razor.cs (Run 15 regression). Fix: strip [RouteData] entirely and leave a /* TODO */ block comment directing Layer 2 to promote the value to a [Parameter] property. Block comments avoid absorbing the closing ) of method signatures. All 15 L1 tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add ID rendering to 5 data controls Add ClientID-based id attribute rendering to GridView, DropDownList, FormView, DataList, and DataGrid following the established pattern from Button/TextBox/Label/Panel/CheckBox. DetailsView and HiddenField already had ID rendering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add Styling Components utility documentation Create comprehensive guide for the 66 style sub-components covering cascading parameter patterns, Web Forms to Blazor migration examples, and complete inventory organized by parent control. Added to mkdocs.yml under Utility Features navigation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(ai-team): Merge audit gap fixes — script stabilization, data control ID rendering, style docs Session: 2026-03-18T15-48-15Z-audit-gap-fixes Requested by: Team orchestration Changes: - Bishop: Fixed RouteData→Parameter conversion in bwfc-migrate.ps1 (CS0592, 15/15 L1 tests pass) - Cyclops: Extended ID rendering to data controls (GridView, DropDownList, FormView, DataList, DataGrid) - Beast: Created StylingComponents.md documentation (all 66 style sub-components) - Merged 5 decisions from inbox; deleted inbox files - Appended cross-agent team updates to Cyclops and Beast history Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(ai-team): Merge Forge's ID rendering approval decision Appended Forge's decision to decisions.md (ID rendering pattern approval for Bishop and Cyclops work). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add 23 quick-win missing properties across 15 components Batch implementation of properties identified by gap analysis: - CausesValidation + ValidationGroup on 5 list controls (DropDownList, CheckBoxList, RadioButtonList, ListBox, BulletedList) - Calendar.TodaysDate, HyperLink.ImageUrl - DataPager: PagedControlID, QueryStringField - DataList: DataKeyField, EditItemIndex - Menu: ScrollDownText, ScrollUpText, IncludeStyleBlock - TreeView.NodeWrap - Validator stubs: EnableClientScript, ShowMessageBox, ControlToCompare - ScriptManager.ScriptPath, SiteMapPath.SkipLinkText Build passes clean (0 errors). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: Chart Phase 1 — Web Forms property compatibility via Pattern B+ Add 10 Web Forms Chart parameters with migration-compatible mappings: - 5 new enums: AntiAliasingStyles, GradientStyle, ChartHatchStyle, ImageStorageMode, TextAntiAliasingQuality - 8 [Parameter] properties: AntiAliasing, BackGradientStyle, BackHatchStyle, BackSecondaryColor, BorderlineDashStyle, ImageLocation, ImageStorageMode, TextAntiAliasingQuality - 2 EventCallback parameters: CustomizeLegend, CustomizeMapAreas - CSS rendering: BackGradientStyle maps to linear/radial-gradient, BorderlineDashStyle maps to border-style on container div - Server-only properties marked [Obsolete] with migration guidance Migrated Chart markup now compiles unchanged. Coverage: 25% -> 100% markup acceptance with 50% real visual fidelity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: add 135 bUnit tests for new properties, Chart Phase 1, and ID rendering - 40 tests for quick-win properties across 15 components (CausesValidation/ValidationGroup, TodaysDate, ImageUrl, etc.) - 22 tests for Chart Phase 1 params and CSS gradient/border rendering - 10 tests for ID rendering on GridView, DropDownList, FormView, DataList, DataGrid - Total test suite: 2373 tests, 0 failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: update 8 sample pages with new property demonstrations - Chart/Styling: BackGradientStyle, BorderlineDashStyle, AntiAliasing demos - HyperLink: ImageUrl example - Calendar: TodaysDate override demo - Menu: ScrollDownText/ScrollUpText with Unicode arrows - TreeView: NodeWrap with long wrapping text - ValidationSummary: ShowMessageBox migration compatibility - DataPager: PagedControlID and QueryStringField section - DataList: DataKeyField and EditItemIndex demo Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: add 16 Playwright integration tests for sample pages - 15 smoke tests covering uncovered Ajax Toolkit sample pages - 1 Chart Styling render test verifying canvas elements and CSS - Every sample page now has at least one integration test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add Chart property mapping strategy proposal Comprehensive analysis of Web Forms Chart vs Chart.js architecture with 4 implementation options per property, plugin analysis, and recommended Pattern B+ approach. Covers migration-first vs fidelity-first tradeoffs with full property mapping table. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: implement 10 high-impact gap items across 5 components Batch A - Validators: - BaseValidator: EnableClientScript (stub), AssociatedControlID (stub) Covers all 5 validators via inheritance Batch B - FormView: - AllowPaging (wired to pager visibility) - CellPadding, CellSpacing, GridLines (table rendering, GridView pattern) Batch C - Login events: - ChangePassword: OnSendingMail, OnSendMailError - CreateUserWizard: OnSendingMail, OnSendMailError (copied from PasswordRecovery pattern) Batch D - GridView + Menu: - GridView: AutoGenerateDeleteButton (wired to command column) - Menu: DynamicHorizontalOffset, DynamicVerticalOffset Build passes clean (0 errors). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: add 29 bUnit tests for next batch gap items - BaseValidator EnableClientScript/AssociatedControlID on RequiredFieldValidator + CompareValidator - FormView AllowPaging, CellPadding, CellSpacing, GridLines - GridView AutoGenerateDeleteButton (including rendered delete link verification) - ChangePassword + CreateUserWizard OnSendingMail/OnSendMailError events - Menu DynamicHorizontalOffset/DynamicVerticalOffset Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updated the description to clarify the purpose of the library.
…ide (#481) The [HandlerRoute] attribute and MapBlazorWebFormsHandlers() were eliminated during the design pivot to Minimal API registration. The actual API uses MapHandler<T>("/path") in Program.cs, but the documentation still referenced the old attribute-based pattern throughout. Changes: - Remove all [HandlerRoute(...)] decorations from code examples - Replace MapBlazorWebFormsHandlers() with explicit MapHandler<T>() calls - Update Quick Start checklist step 5 to reference Program.cs registration - Add Program.cs registration snippets to all handler examples - Update troubleshooting table and summary section Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… 100%) (#483) * Tier 2: Property/event gaps + health detection fixes Health detection: - Add TypeAliases mapping in ComponentHealthService for components whose class name differs from the tracked name (AspNetValidationSummary) - Fix reference baselines: FileUpload read-only props, TreeView DataSourceID on stop type, CustomValidator ServerValidate as Func not EventCallback Component property/event additions: - TreeView: 9 new properties (image URLs, tooltips, config stubs) - BulletedList: 4 properties + 3 events (Click alias, SelectedIndexChanged, TextChanged, AutoPostBack, SelectedIndex, SelectedValue, Text) - SiteMapPath: 2 events (ItemCreated, ItemDataBound) + SiteMapNodeItemEventArgs - CustomValidator: 2 properties (ClientValidationFunction, IsValid) Fix existing AutoGenerateDataBindings="False" "false" in sample + test Health: 6 components raised to 100% (44 50/59) Tests: 2,470 pass (22 new) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add docs, samples, and integration tests for Tier 2 features New sample pages: - TreeView/ImageAndConfig: CollapseImageUrl, ExpandImageUrl, tooltips, MaxDataBindDepth, AutoGenerateDataBindings, EnableClientScript - BulletedList/Selection: Click alias, SelectedIndex, SelectedValue, AutoPostBack, Text, SelectedIndexChanged, TextChanged - SiteMapPath/Events: ItemCreated and ItemDataBound events Updated sample: - CustomValidator: ClientValidationFunction and IsValid properties Documentation updates: - TreeView.md: Image Customization and Data Binding Config references - BulletedList.md: Selection properties, Click alias, events - SiteMapPath.md: Events section, removed ItemDataBound from NOT Supported - CustomValidator.md: Full documentation (was _TODO_) Integration tests: - Added 3 new InlineData entries for new sample pages Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix navigation for new sample sub-pages Add SubPages entries to ComponentCatalog.cs so the sample app navigation links to the new pages: - BulletedList: added "Selection" sub-page - SiteMapPath: added "Events" sub-page - TreeView: added "ImageAndConfig" sub-page (alphabetical order) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: BulletedList Selection demo separate interactive vs migration stubs - Click alias demo remains interactive with clear CTA - New OnClick+SelectedIndex tracking demo shows functional selection - Migration stub section clearly labeled as markup compatibility only - Removed misleading SelectedIndexChanged/TextChanged interactive demos - Updated docs to clarify stubs are accepted but not fired Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: wire up SiteMapPath ItemCreated/ItemDataBound events - Events now fire during OnParametersSetAsync when navigation path changes - Uses URL change tracking to prevent re-render loops - ItemCreated fires for each node after path is built - ItemDataBound fires for each node after ItemCreated - All 2,470 bUnit tests pass - All 243 integration tests pass (including SiteMapPath/Events) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: Add docs and sample pages for 6 sub-100% components Add missing documentation and sample pages to bring 6 components to 100% health score (44 -> 50 components at full health). Documentation added: - View.md - MultiView child container component - Content.md - Master page content region component - ContentPlaceHolder.md - Master page placeholder component - MasterPage.md - Master page layout component Sample pages added: - RadioButton - standalone radio button with grouping, alignment, events - NamingContainer - ID scoping with nesting, ctl00 prefix, visibility Catalog entries added for View, Content, ContentPlaceHolder, RadioButton, and NamingContainer in ComponentCatalog.cs. Updated mkdocs.yml navigation. All 2,448 tests pass. MkDocs strict build passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: Remove duplicate RadioButton sample page causing route conflict The PR added Components/Pages/ControlSamples/RadioButton/Index.razor but Pages/ControlSamples/RadioButton/Index.razor already existed with the same @page route. The ambiguous route crashed the Blazor circuit, cascading failures to 32 integration tests. All 243 integration tests now pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add standalone sample pages for Content, ContentPlaceHolder, and View
(previously only available via shared MasterPage/MultiView group pages)
- Update ComponentCatalog routes to point to individual pages
- Create BaseValidator and BaseCompareValidator documentation
- Add to mkdocs.yml navigation
- Add 14 integration tests: 3 smoke tests + 11 interaction tests
- Validator interaction tests for CompareValidator, RangeValidator,
RegularExpressionValidator, CustomValidator, ValidationSummary
- Content, ContentPlaceHolder, View smoke and render tests
- Fix validator test assertions to check visibility (not DOM text content)
since validators keep error text in DOM when hidden
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…samples feat: Quick wins - sample pages, docs, and integration tests
…Substitution promotion (#485) - Add Known Fidelity Divergences documentation (ListView DOM, Calendar sub-IDs, Label, FormView, ID coverage) - Add DataList ID rendering tests (3 tests: table with ID, table without ID, flow layout with ID) - Add HiddenField ID rendering tests (2 tests: with and without ID) - Update Substitution status from Deferred to active in tracked-components.json - Correct audit: 7/8 data controls already have id=ClientID rendering - Mark audit priority #2 (Extend ID rendering) as DONE - Add fidelity doc to Migration section in mkdocs.yml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 8 Roslyn analyzers with code fixes for Web Forms migration patterns: - BWFC001: Missing [Parameter] attribute - BWFC002: ViewState usage - BWFC003: IsPostBack usage - BWFC004: Response.Redirect - BWFC005: Session usage - BWFC010: Required component attributes - BWFC011: Web Forms event handler signatures - BWFC012: runat=server leftovers Includes 89 tests, MkDocs documentation, and sample app content. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rm CI Code fix providers for BWFC002, BWFC004, BWFC010, BWFC012 hardcoded \r\n in EndOfLine trivia, causing test failures on Linux CI runners where source files have \n line endings. Added SyntaxExtensions.DetectEndOfLine() helper that reads the first EndOfLineTrivia from the syntax tree, ensuring code fixes match the document's existing line ending style. Fixes failing tests in PR #487. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fix: cross-platform line endings in analyzer code fix providers
* feat: Add BWFC013/014 analyzers, architecture guide, and CI docs Analyzer Expansion: - BWFC013: Detects Response.Write/WriteFile/Clear/Flush/End usage - BWFC014: Detects Request.Form/Cookies/Headers/Files/QueryString access - Both produce WARNING diagnostics with guidance-only code fixes (TODO comments) - 21 new tests (111 total, all passing) Documentation: - dev-docs/ANALYZER-ARCHITECTURE.md: Contributor guide for building analyzers - docs/Migration/Analyzers.md: BWFC013/014 entries, CI/CD integration section, .editorconfig severity tuning, prioritization guide Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(ai-team): Analyzer sprint 1 complete orchestration & decision logs merged Session: 2026-03-20T14-18-05Z-analyzer-sprint1 Requested by: Scribe Changes: - Orchestration logs for Cyclops (BWFC013/014 analyzers, 6 files, 111 tests) and Beast (architecture guide, 579 lines) - Session log for analyzer sprint 1 execution - Merged 10 decision inbox files decisions.md (BWFC013/014 IDs, analyzer docs, deprecation guidance, ASHX/AXD middleware, RouteData fix, component audit, navigation UX, sample pages, middleware testing) - Deleted all inbox files after merge - Appended team updates to Cyclops, Beast, Forge, Jubilee, Rogue history.md files - PR #487 opened on upstream targeting dev branch * feat: bundle analyzers in main NuGet + add -Prescan switch to L1 script Task 1: Added ProjectReference from BlazorWebFormsComponents to BlazorWebFormsComponents.Analyzers with OutputItemType=Analyzer so consumers get Roslyn analyzers automatically via the main NuGet package. Task 2: Added -Prescan switch to bwfc-migrate.ps1 that scans source .cs files for 9 BWFC analyzer patterns (BWFC001-005, 011-014) and outputs a JSON summary report without performing any migration. Includes human-readable breakdown via Write-Host. Build: 0 errors, 111 analyzer tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: add Roslyn analyzer tests to build and squad-ci workflows - build.yml: restore, build, run, upload, and publish analyzer test results - squad-ci.yml: replace placeholder with dotnet restore/build/test for both suites - Add setup-dotnet step to squad-ci.yml for .NET 10.0.x Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use DetectEndOfLine() for cross-platform CI compatibility All 4 code fix providers (BWFC001, BWFC004, BWFC013, BWFC014) used hardcoded \r\n in SyntaxFactory.EndOfLine(), causing test failures on Linux CI where source strings use \n. - Add SyntaxExtensions.DetectEndOfLine() helper that reads line endings from the existing syntax tree - Replace hardcoded \r\n with root.DetectEndOfLine() in all 4 code fix providers - Replace foreach+if with LINQ .Where() (CodeQL suggestion) All 111 analyzer tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use DetectEndOfLine() in MissingParameterAttributeCodeFixProvider The using directive insertion used SyntaxFactory.CarriageReturnLineFeed which hardcodes CRLF, failing on Linux CI where source uses LF. Replace with newRoot.DetectEndOfLine() for cross-platform compatibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Initial plan * Add sitemap meta tag to docs site via MkDocs Material theme override Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com> Agent-Logs-Url: https://github.com/FritzAndFriends/BlazorWebFormsComponents/sessions/0a9bccbb-1c5c-4660-af93-5084ce1fc875 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: csharpfritz <78577+csharpfritz@users.noreply.github.com>
|
|
||
| private async Task<Document> CommentOutIsPostBackAsync(Document document, SyntaxNode diagnosticNode, CancellationToken cancellationToken) | ||
| { | ||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); |
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
To fix the problem, remove the useless assignment to root. Since the value of root is never read, we can safely delete the entire line that calls document.GetSyntaxRootAsync(...). There is no need to replace it with anything (such as discarding to _), because the call itself is not needed and has no required side effects.
Concretely, in src/BlazorWebFormsComponents.Analyzers/IsPostBackUsageCodeFixProvider.cs, inside the CommentOutIsPostBackAsync method, delete line 41:
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);No other lines in this file need to change, and no additional imports, methods, or definitions are required. The method will then start directly by locating the StatementSyntax from diagnosticNode, as it already does after the unused root assignment.
| @@ -38,7 +38,6 @@ | ||
|
|
||
| private async Task<Document> CommentOutIsPostBackAsync(Document document, SyntaxNode diagnosticNode, CancellationToken cancellationToken) | ||
| { | ||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
| var statement = diagnosticNode.FirstAncestorOrSelf<StatementSyntax>(); | ||
| if (statement == null) | ||
| return document; |
|
|
||
| private async Task<Document> CommentOutViewStateAsync(Document document, SyntaxNode diagnosticNode, CancellationToken cancellationToken) | ||
| { | ||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); |
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
To fix the problem, remove the useless assignment to root and the associated call to GetSyntaxRootAsync, since its result is not used anywhere in CommentOutViewStateAsync. This keeps the method’s observable behavior the same while avoiding unnecessary work and eliminating the dead local variable.
Concretely, in src/BlazorWebFormsComponents.Analyzers/ViewStateUsageCodeFixProvider.cs, inside the CommentOutViewStateAsync method, delete the line:
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);No additional imports, methods, or definitions are required. The rest of the method already uses document and diagnosticNode directly and does not depend on root, so functionality remains unchanged.
| @@ -38,7 +38,6 @@ | ||
|
|
||
| private async Task<Document> CommentOutViewStateAsync(Document document, SyntaxNode diagnosticNode, CancellationToken cancellationToken) | ||
| { | ||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
| var statement = diagnosticNode.FirstAncestorOrSelf<StatementSyntax>(); | ||
| if (statement == null) | ||
| return document; |
| // Only report if this node is not already the expression of another | ||
| // member access that we'd also flag. | ||
| var parent = memberAccess.Parent; | ||
| if (parent is MemberAccessExpressionSyntax parentMember) |
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
In general, to fix a “useless assignment to local variable” you either remove the variable (and possibly the whole statement) if it is not needed, or start using it in the logic if it was supposed to be used. Here, the concrete pattern match introduces parentMember but never uses it; only the fact that parent might be a MemberAccessExpressionSyntax appears to have been of interest. The simplest fix that preserves current behavior is to remove the variable from the pattern and keep only a type check, or remove the entire if if that check is also unnecessary.
The single best minimal fix without changing functionality is to change line 72 from a pattern with a bound variable to a plain type check: if (parent is MemberAccessExpressionSyntax). This keeps the structure and comments about “parent is MemberAccessExpressionSyntax” but removes the unused parentMember variable, silencing the CodeQL warning. No new imports, methods, or additional definitions are required. Only the AnalyzeMemberAccess method in src/BlazorWebFormsComponents.Analyzers/SessionUsageAnalyzer.cs needs modification, at the if (parent is MemberAccessExpressionSyntax parentMember) line.
| @@ -69,7 +69,7 @@ | ||
| // Only report if this node is not already the expression of another | ||
| // member access that we'd also flag. | ||
| var parent = memberAccess.Parent; | ||
| if (parent is MemberAccessExpressionSyntax parentMember) | ||
| if (parent is MemberAccessExpressionSyntax) | ||
| { | ||
| // If parent is HttpContext.Current.Session, and that's followed by element access, | ||
| // we'll catch the Session["key"] separately. But HttpContext.Current itself is the problem. |
| cut.FindComponent<AspNetValidationSummary>().Instance.ShowMessageBox.ShouldBeTrue(); | ||
| } | ||
|
|
||
| private ExampleModel model = new ExampleModel(); |
Check notice
Code scanning / CodeQL
Missed 'readonly' opportunity Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
In general, to fix a “Missed 'readonly' opportunity” for a field, you add the readonly modifier to the field declaration when the field is only assigned at declaration or within constructors of the same class. This enforces that the field cannot be reassigned later, while leaving mutation of the object’s internal state (its properties) still possible.
For this specific case, update the field declaration on line 51 in src/BlazorWebFormsComponents.Test/Validations/ValidationSummary/NewProperties.razor from private ExampleModel model = new ExampleModel(); to private readonly ExampleModel model = new ExampleModel();. No other changes are required: the tests only read from model via Model="@model" in the EditForm, and the ExampleModel class remains mutable through its Name property. No imports or new methods are needed.
| @@ -48,7 +48,7 @@ | ||
| cut.FindComponent<AspNetValidationSummary>().Instance.ShowMessageBox.ShouldBeTrue(); | ||
| } | ||
|
|
||
| private ExampleModel model = new ExampleModel(); | ||
| private readonly ExampleModel model = new ExampleModel(); | ||
|
|
||
| public class ExampleModel | ||
| { |
| cut.FindComponent<CompareValidator<int>>().Instance.ControlToCompare.ShouldBe("OtherInput"); | ||
| } | ||
|
|
||
| private ExampleModel model = new ExampleModel(); |
Check notice
Code scanning / CodeQL
Missed 'readonly' opportunity Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
In general, to fix this kind of issue you add the readonly modifier to any field that is only assigned at its declaration or within a constructor of the same class. This guarantees the field reference cannot be changed after object initialization, while not affecting mutations to the object it refers to.
For this specific file, the best fix is to update the declaration of the private ExampleModel model field on line 40 to include the readonly modifier: private readonly ExampleModel model = new ExampleModel();. This does not change any behavior, because the field was never reassigned elsewhere in the shown code. No additional methods, imports, or other definitions are required.
| @@ -37,7 +37,7 @@ | ||
| cut.FindComponent<CompareValidator<int>>().Instance.ControlToCompare.ShouldBe("OtherInput"); | ||
| } | ||
|
|
||
| private ExampleModel model = new ExampleModel(); | ||
| private readonly ExampleModel model = new ExampleModel(); | ||
|
|
||
| public class ExampleModel | ||
| { |
| foreach (var expr in creation.Initializer.Expressions) | ||
| { | ||
| if (expr is AssignmentExpressionSyntax assignment && | ||
| assignment.Left is IdentifierNameSyntax propName) | ||
| { | ||
| result.Add(propName.Identifier.Text); | ||
| } | ||
| } |
Check notice
Code scanning / CodeQL
Missed opportunity to use Where Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
In general, to fix this class of issues you move the filtering predicate from inside the loop body into a LINQ .Where(...) on the sequence you are iterating, so the foreach only runs over already‑filtered elements. This avoids an initial if (!condition) continue; or wrapping the whole body in if (condition).
Here, we should change foreach (var expr in creation.Initializer.Expressions) to iterate only over those expressions that are AssignmentExpressionSyntax with an IdentifierNameSyntax on the left. We can do this with a Where plus pattern matching in the lambda. The rest of the method (result.Add(...), returning the set) remains unchanged, so behavior is preserved.
Concretely, in src/BlazorWebFormsComponents.Analyzers/RequiredAttributeAnalyzer.cs, within GetInitializerAssignments, replace:
- the
foreach (var expr in creation.Initializer.Expressions)header, and - the inner
if (expr is AssignmentExpressionSyntax ... ) { ... }
with a foreach that iterates over creation.Initializer.Expressions.Where(expr => expr is AssignmentExpressionSyntax { Left: IdentifierNameSyntax }), and a simplified pattern match inside the loop. No new imports are needed because System.Linq is already included.
| @@ -130,10 +130,9 @@ | ||
| var result = new HashSet<string>(); | ||
| if (creation.Initializer != null) | ||
| { | ||
| foreach (var expr in creation.Initializer.Expressions) | ||
| foreach (var expr in creation.Initializer.Expressions.Where(expr => expr is AssignmentExpressionSyntax { Left: IdentifierNameSyntax })) | ||
| { | ||
| if (expr is AssignmentExpressionSyntax assignment && | ||
| assignment.Left is IdentifierNameSyntax propName) | ||
| if (expr is AssignmentExpressionSyntax { Left: IdentifierNameSyntax propName }) | ||
| { | ||
| result.Add(propName.Identifier.Text); | ||
| } |
| foreach (var required in requiredProps) | ||
| { | ||
| if (!assignedProperties.Contains(required)) | ||
| { | ||
| var diagnostic = Diagnostic.Create(Rule, creation.GetLocation(), baseTypeName, required); | ||
| context.ReportDiagnostic(diagnostic); | ||
| } | ||
| } |
Check notice
Code scanning / CodeQL
Missed opportunity to use Where Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
In general, to fix this kind of issue you identify the foreach loop that conditionally processes elements based on an if inside the loop, and refactor it to iterate over a filtered sequence using Where. That is, instead of looping over all items and skipping those that fail the condition, you pre-filter the enumerable and then loop over only the relevant items.
For this specific code, we should change the loop:
foreach (var required in requiredProps)
{
if (!assignedProperties.Contains(required))
{
var diagnostic = Diagnostic.Create(Rule, creation.GetLocation(), baseTypeName, required);
context.ReportDiagnostic(diagnostic);
}
}to instead iterate only over those required properties that are not in assignedProperties. Since requiredProps is an array of strings and System.Linq is already imported (line 7), we can use:
foreach (var required in requiredProps.Where(required => !assignedProperties.Contains(required)))
{
var diagnostic = Diagnostic.Create(Rule, creation.GetLocation(), baseTypeName, required);
context.ReportDiagnostic(diagnostic);
}This preserves the existing behavior exactly: diagnostics are still reported for each missing required property, but the filter is expressed with Where instead of an if inside the loop. No additional imports, methods, or type changes are needed; the change is localized to the foreach loop around line 90 in RequiredAttributeAnalyzer.cs.
| @@ -87,13 +87,10 @@ | ||
| } | ||
| } | ||
|
|
||
| foreach (var required in requiredProps) | ||
| foreach (var required in requiredProps.Where(required => !assignedProperties.Contains(required))) | ||
| { | ||
| if (!assignedProperties.Contains(required)) | ||
| { | ||
| var diagnostic = Diagnostic.Create(Rule, creation.GetLocation(), baseTypeName, required); | ||
| context.ReportDiagnostic(diagnostic); | ||
| } | ||
| var diagnostic = Diagnostic.Create(Rule, creation.GetLocation(), baseTypeName, required); | ||
| context.ReportDiagnostic(diagnostic); | ||
| } | ||
| } | ||
|
|
| if (i == 0) | ||
| commentedLines[i] = "// " + lineCode; | ||
| else | ||
| commentedLines[i] = lineIndent + "// " + lineCode; |
Check notice
Code scanning / CodeQL
Missed ternary opportunity Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
To fix the problem, replace the if statement that assigns to commentedLines[i] in both branches with a single assignment using the conditional (?:) operator. This keeps the logic identical while making the code more concise and clearly expressing that only the value assigned changes based on the condition.
Concretely, in src/BlazorWebFormsComponents.Analyzers/IsPostBackUsageCodeFixProvider.cs, within the loop that builds commentedLines, replace lines 80–83:
- Remove the multi-line
if (i == 0) ... else ...block. - Add a single line:
commentedLines[i] = i == 0 ? "// " + lineCode : lineIndent + "// " + lineCode;
No new methods, imports, or definitions are needed; this is a pure expression-level refactor.
| @@ -77,10 +77,7 @@ | ||
| var lineIndent = line.Substring(0, ws); | ||
| var lineCode = line.Substring(ws); | ||
|
|
||
| if (i == 0) | ||
| commentedLines[i] = "// " + lineCode; | ||
| else | ||
| commentedLines[i] = lineIndent + "// " + lineCode; | ||
| commentedLines[i] = i == 0 ? "// " + lineCode : lineIndent + "// " + lineCode; | ||
| } | ||
| var commentedText = string.Join(newLine, commentedLines); | ||
|
|
| foreach (var trivia in method.GetLeadingTrivia()) | ||
| { | ||
| if (trivia.IsKind(SyntaxKind.WhitespaceTrivia)) | ||
| { | ||
| newLeadingTrivia = newLeadingTrivia.Add(trivia); | ||
| break; | ||
| } | ||
| } |
Check notice
Code scanning / CodeQL
Missed opportunity to use Where Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
Generally, to fix this kind of issue, replace a foreach loop that immediately filters inside the loop body with a LINQ query such as .Where(...), potentially combined with .FirstOrDefault()/.First() if only the first matching element is needed. This moves the filter predicate into the sequence definition instead of being embedded in loop control logic.
In this specific case, lines 64–71 iterate over method.GetLeadingTrivia(), look for the first trivia whose kind is SyntaxKind.WhitespaceTrivia, append that trivia to newLeadingTrivia, then break. To keep the behavior identical, we can compute the first whitespace trivia via LINQ, then append it if present:
var indentationTrivia = method.GetLeadingTrivia()
.FirstOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia));
if (!indentationTrivia.Equals(default(SyntaxTrivia)))
{
newLeadingTrivia = newLeadingTrivia.Add(indentationTrivia);
}This removes the implicit filtering foreach entirely, replacing it with explicit filtering using LINQ (FirstOrDefault with a predicate, which is equivalent to Where(...).FirstOrDefault()). No new imports are required because System.Linq is already imported at the top of the file. The rest of the method and file remain unchanged.
| @@ -61,13 +61,12 @@ | ||
| .Add(root.DetectEndOfLine()); | ||
|
|
||
| // Collect indentation from original method | ||
| foreach (var trivia in method.GetLeadingTrivia()) | ||
| var indentationTrivia = method.GetLeadingTrivia() | ||
| .FirstOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia)); | ||
|
|
||
| if (!indentationTrivia.Equals(default(SyntaxTrivia))) | ||
| { | ||
| if (trivia.IsKind(SyntaxKind.WhitespaceTrivia)) | ||
| { | ||
| newLeadingTrivia = newLeadingTrivia.Add(trivia); | ||
| break; | ||
| } | ||
| newLeadingTrivia = newLeadingTrivia.Add(indentationTrivia); | ||
| } | ||
|
|
||
| var newMethod = method.WithLeadingTrivia(newLeadingTrivia); |
| foreach (var trivia in method.GetLeadingTrivia()) | ||
| { | ||
| if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) && | ||
| trivia.ToString().Contains("TODO: Convert to EventCallback")) | ||
| { | ||
| return document; | ||
| } | ||
| } |
Check notice
Code scanning / CodeQL
Missed opportunity to use Where Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 27 days ago
In general, to fix a “missed opportunity to use Where” in a foreach, convert the pattern:
foreach (var x in sequence)
{
if (!predicate(x))
continue;
// use x
}into something that filters first and then operates, e.g.:
foreach (var x in sequence.Where(predicate))
{
// use x
}or, if the body only returns or breaks, use Any/FirstOrDefault with a predicate.
In this specific code, the loop only checks whether any leading trivia is a single-line comment containing "TODO: Convert to EventCallback" and, if found, returns the document early. This is more idiomatically written using Any with a predicate on the trivia sequence, eliminating the explicit loop. We already have using System.Linq;, so we can write:
if (method.GetLeadingTrivia().Any(trivia =>
trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) &&
trivia.ToString().Contains("TODO: Convert to EventCallback")))
{
return document;
}This preserves the existing behavior: if such a TODO comment exists anywhere in the leading trivia, we return the original document; otherwise we proceed to build and insert the TODO comment. The change is localized to the loop in AddTodoCommentAsync (lines 47–54); no additional imports or helper methods are required.
| @@ -44,13 +44,11 @@ | ||
| var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
|
|
||
| // Don't add if a TODO comment already exists in leading trivia | ||
| foreach (var trivia in method.GetLeadingTrivia()) | ||
| if (method.GetLeadingTrivia().Any(trivia => | ||
| trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) && | ||
| trivia.ToString().Contains("TODO: Convert to EventCallback"))) | ||
| { | ||
| if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) && | ||
| trivia.ToString().Contains("TODO: Convert to EventCallback")) | ||
| { | ||
| return document; | ||
| } | ||
| return document; | ||
| } | ||
|
|
||
| var todoComment = SyntaxFactory.Comment( |
v0.18 Release
Includes all work merged to dev since v0.14: