diff --git a/.ai-team/agents/beast/history.md b/.ai-team/agents/beast/history.md index 97f179f08..7f3234bea 100644 --- a/.ai-team/agents/beast/history.md +++ b/.ai-team/agents/beast/history.md @@ -7,111 +7,69 @@ ## Learnings - + - +### Core Context (2026-02-10 through 2026-02-27) -### Summary: Documentation Conventions (2026-02-10 through 2026-02-12) +**Doc structure:** title → intro (MS docs link) → Features Supported → NOT Supported → Web Forms syntax → Blazor syntax → HTML Output → Migration Notes (Before/After) → Examples → See Also. Admonitions for gotchas. mkdocs.yml nav alphabetical within categories. Migration section: "Getting started" and "Migration Strategies" at top. -**Doc structure:** title → intro (MS docs link) → Features Supported → NOT Supported → Web Forms syntax → Blazor syntax → HTML Output → Migration Notes (Before/After) → Examples → See Also. Admonitions for gotchas. mkdocs.yml nav is alphabetical within category sections. Migration section keeps "Getting started" and "Migration Strategies" at top. +**Key patterns:** Style migration: TableItemStyle → CSS class string parameters. DeferredControls.md has dual role (fully deferred + partially implemented). Chart screenshots at `docs/images/{component}/chart-{type}.png`. Shared sub-component docs linked from parents. PagerSettings is first shared sub-component with own doc page. Structural components (no HTML output) lead with "renders no HTML" callout. Audit reports at `planning-docs/AUDIT-REPORT-M{N}.md` with historical snapshot headers. Branch naming: `copilot/create-*`. -**Key patterns:** Style migration: Web Forms `TableItemStyle` child elements → Blazor CSS class string parameters. Deferred controls use `docs/Migration/DeferredControls.md` with What/Why/Alternatives/Before-After (no Features sections). ImageMap is in Navigation Controls. Branch naming: `copilot/create-*` on upstream. Chart doc introduces JS interop "HTML Output Exception" pattern and multi-component (child) doc pattern. Chart Type Gallery at `docs/images/chart/chart-{type}.png`. -### Core Context (2026-02-10 through 2026-02-25) +**Doc work completed:** M1–M3 docs (PasswordRecovery 3-step wizard, DetailsView generic component). Chart doc (JS interop "HTML Output Exception" pattern, Chart Type Gallery, child component doc pattern). M8 release-readiness polish (Substitution/Xml deferred in status.md, Chart Phase 1 hedging removed, README link fixes). M9 Doc Gap Audit (FormView, DetailsView, DataGrid, ChangePassword, PagerSettings.md created). ToolTip universality in Migration/readme.md. ThemesAndSkins.md updated for M10 PoC. NamingContainer.md created with IDRendering.md cross-refs. M9 Consolidated Audit Report (29 findings → M10 issues). -Established doc structure: title → intro (MS docs link) → Features Supported → NOT Supported → Web Forms syntax → Blazor syntax → HTML Output → Migration Notes → Examples → See Also. mkdocs.yml nav alphabetical within categories. Branch naming: `copilot/create-*`. Chart doc introduced JS interop "HTML Output Exception" pattern and multi-component (child) doc pattern. Chart Type Gallery at `docs/images/chart/chart-{type}.png`. Created PasswordRecovery doc (3-step wizard pattern), DetailsView doc (generic component pattern). Deferred controls use `docs/Migration/DeferredControls.md`. Feature audit: AccessKey/ToolTip base class gap, Substitution/Xml deferred, Style is computed. +**Pending doc needs:** ClientIDMode property documentation (M16). Menu dual rendering modes. ListView CRUD events. Menu styles (IMenuStyleContainer). Post-M15 verification badges if new exact matches achieved. Login+Identity deferred — do not schedule docs. -**Key patterns:** Style migration: TableItemStyle → CSS class string parameters. DeferredControls.md has dual role (fully deferred + partially implemented). Chart screenshots at `docs/images/{component}/`. Shared sub-component docs linked from parent control docs. +- **M17 AJAX Controls documentation (6 pages):** Created documentation for 6 AJAX-era Web Forms controls added in M17: + 1. **Timer.md** (`docs/EditorControls/Timer.md`) — Interval-based tick events using System.Threading.Timer internally. No ScriptManager dependency. Full before/after migration with auto-refresh and countdown examples. + 2. **ScriptManager.md** (`docs/EditorControls/ScriptManager.md`) — Migration stub that renders nothing. Documented all accepted-but-ignored properties. Emphasized "scaffolding" approach: include during migration, remove when stable. + 3. **ScriptManagerProxy.md** (`docs/EditorControls/ScriptManagerProxy.md`) — Migration stub for content pages. Documented IJSRuntime replacement for script registration. + 4. **UpdatePanel.md** (`docs/EditorControls/UpdatePanel.md`) — Structural wrapper rendering `
` or ``. Key message: Blazor already does partial rendering, UpdatePanel is for HTML structure preservation. Documented RenderMode Block/Inline. + 5. **UpdateProgress.md** (`docs/EditorControls/UpdateProgress.md`) — Loading indicator with ProgressTemplate. Key migration pattern: replace automatic UpdatePanel association with explicit `bool IsLoading` state management. + 6. **Substitution.md** (`docs/EditorControls/Substitution.md`) — Renders callback output directly. Migrated from "deferred" to "implemented" in DeferredControls.md summary table. + - Added "AJAX Controls" section to mkdocs.yml nav (alphabetical within section, between Login Controls and Utility Features). + - Added AJAX Controls category to README.md component listing with links to all 6 doc pages. + - Updated `docs/Migration/DeferredControls.md` — changed Substitution from ❌ Deferred to ✅ Complete with implementation note. +- **Migration stub documentation pattern:** ScriptManager and ScriptManagerProxy establish a new "migration stub" doc pattern: lead with a `!!! warning "Migration Stub Only"` admonition, document all accepted-but-ignored properties, and include explicit "include → remove" lifecycle guidance. Reuse this pattern for any future no-op migration compatibility components. +- **AJAX Controls nav category:** Created a new "AJAX Controls" nav section in mkdocs.yml separate from "Editor Controls" to group the AJAX-era controls (Timer, ScriptManager, ScriptManagerProxy, UpdatePanel, UpdateProgress, Substitution). This keeps them discoverable as a cohesive migration topic. - Team update (2026-02-12): Milestone 4 planned Chart component with Chart.js via JS interop. 8 work items, design review required before implementation. decided by Forge + Squad + Team update (2026-02-27): Branching workflow directive feature PRs from personal fork to upstream dev, only devmain on upstream decided by Jeffrey T. Fritz -- **Chart doc is first JS interop component:** The Chart component is unique in the library — it's the first to use JavaScript interop (Chart.js via ES module import). The doc template needed a new "HTML Output Exception" admonition pattern to explain why `` replaces ``. This pattern should be reused for any future components that deviate from identical HTML output. -- **DeferredControls.md updated for partial implementation:** Chart moved from fully-deferred to partially-implemented. The DeferredControls page now has a dual role: documenting controls not implemented at all (Substitution, Xml) AND documenting unsupported sub-features of implemented controls (27 unsupported chart types). This "partially implemented" pattern may apply to future controls. -- **Child component docs pattern:** Chart introduces a multi-component documentation pattern (Chart, ChartSeries, ChartArea, ChartLegend, ChartTitle) with separate parameter tables for each. This nested-component doc approach should be used for any future components with required child components. -- **Chart Type Gallery added:** Added a "Chart Type Gallery" section to `docs/DataControls/Chart.md` between "Chart Palettes" and "Web Forms Features NOT Supported". Contains 8 subsections (Column, Line, Bar, Pie, Doughnut, Area, Scatter, Stacked Column) each with a screenshot, `SeriesChartType` enum value, and 1-2 sentence usage guidance. Includes `!!! warning` admonitions on Pie and Doughnut for the Phase 1 palette limitation (single series color instead of per-segment colors). -- **Chart image path convention:** Chart screenshots live at `docs/images/chart/chart-{type}.png` (lowercase, hyphenated). Referenced from Chart.md using relative paths: `../images/chart/chart-{type}.png`. This `docs/images/{component}/` pattern should be used for any future component screenshots. -### Summary: Feature Audit Findings (2026-02-23) + Team update (2026-02-27): Issues must be closed via PR references using 'Closes #N' syntax, no manual closures decided by Jeffrey T. Fritz -AccessKey/ToolTip missing from base classes (universal gap). Label needs BaseStyledComponent. ListControl-derived components share common gaps (AppendDataBoundItems, DataTextFormatString, CausesValidation). Literal/Localize/PlaceHolder/View/MultiView near-complete. Substitution/Xml permanently deferred. Style property is computed (not directly settable). Panel is most feature-complete styled control. + Team update (2026-02-27): M17 AJAX controls implemented ScriptManager/Proxy are no-op stubs, Timer shadows Enabled, UpdatePanel uses ChildContent, UpdateProgress renders hidden, Substitution uses Func callback, new AJAX/Migration Helper categories decided by Cyclops - Team update (2026-02-23): AccessKey/ToolTip must be added to BaseStyledComponent decided by Beast, Cyclops - Team update (2026-02-23): Chart implementation architecture consolidated (10 decisions) decided by Cyclops, Forge - Team update (2026-02-23): DetailsView/PasswordRecovery branch (sprint3) must be merged forward decided by Forge - Team update (2026-02-23): BaseListControl introduced docs should reflect shared base for list controls decided by Cyclops - Team update (2026-02-23): Label AssociatedControlID switches rendered element document accessibility benefit decided by Cyclops - Team update (2026-02-23): Login controls now inherit BaseStyledComponent update docs for outer style support decided by Rogue, Cyclops - Team update (2026-02-23): Milestone 6 Work Plan ratified 54 WIs, Beast assigned branding (UI-11) and docs (UI-12) decided by Forge - Team update (2026-02-23): Menu Orientation requires Razor local variable workaround document this pattern decided by Jubilee + Team update (2026-02-27): M17 sample pages created for Timer, UpdatePanel, UpdateProgress, ScriptManager, Substitution. Default.razor filenames. ComponentCatalog already populated decided by Jubilee -- **Milestone 8 release-readiness docs polish:** Formally deferred Substitution and Xml controls in `status.md` (changed from 🔴 Not Started to ⏸️ Deferred with rationale). Added Deferred column to summary table. Updated `docs/Migration/DeferredControls.md` to mark Chart as fully implemented (removed "Phase 1"/"Partial" hedging). Removed all "Phase 1"/"Phase 2/3" hedging from `docs/DataControls/Chart.md`. Fixed duplicate `DeferredControls.md` entry in `mkdocs.yml` and re-alphabetized Migration nav. Fixed broken `ImageMap` link in `README.md` (pointed to EditorControls, should be NavigationControls). Added missing doc links in README for MultiView, View, ChangePassword, CreateUserWizard. Marked Xml as deferred in README component list. + Team update (2026-02-27): M17 audit fixes resolved 5 fidelity issues fixed (EnablePartialRendering default, Scripts collection, CssClass rendering, display:block style, ScriptReference properties). 9 new tests, 1367 total. PR #402 decided by Forge, Cyclops - Team update (2026-02-24): Menu auto-ID pattern components with JS interop should auto-generate IDs decided by Cyclops - Team update (2026-02-24): M8 scope excludes version bump to 1.0 and release decided by Jeffrey T. Fritz - Team update (2026-02-24): PagerSettings shared sub-component created update docs when component stabilizes decided by Cyclops + Team update (2026-02-27): No-op stub property coverage intentionally limited (41-50% acceptable) deep AJAX infrastructure properties omitted decided by Forge -- **M9 Doc Gap Audit (WI-09):** Audited all docs against M6-M8 features. GridView, TreeView, Menu, Validators (ControlToValidate), and Login are fully documented. Gaps found in: FormView (ItemCommand event, styles, PagerSettings not in Blazor sections), DetailsView (Caption missing, styles/PagerSettings listed as unsupported but may be stale), DataGrid (paging listed as unsupported, needs verification), ChangePassword (Orientation and TextLayout not documented despite Login having them), and PagerSettings (no dedicated doc page exists). Full report in `.ai-team/decisions/inbox/beast-m9-doc-audit.md`. -- **M9 Planning-Docs Historical Headers (WI-10):** Added `> ⚠️ Historical Snapshot (Pre-Milestone 6)` header to all 54 per-control audit files and SUMMARY.md in `planning-docs/`. Excluded README.md and MILESTONE*-PLAN.md files (still current/active). This prevents future contributors from treating pre-M6 gap data as current. -- **ChangePassword/Login parity gap:** Login.md documents Orientation and TextLayout with full reference tables and migration examples, but ChangePassword.md has neither. Both controls should have identical coverage for these shared layout properties. -- **ToolTip universality documented (WI-04):** Added ToolTip to Features Supported in Label.md, TextBox.md, and GridView.md (Button.md already had it). Added "Common Properties on All Styled Controls" section to Migration/readme.md explaining that ToolTip (renders as `title` attribute) is universally available on all BaseStyledComponent-derived controls. Used `!!! tip` admonition and code examples. No dedicated Common Properties page created — kept it inline in the migration guide for minimal footprint. + Team update (2026-02-27): UpdatePanel Triggers collection deliberately omitted Blazor rendering model makes it unnecessary decided by Forge - Team update (2026-02-25): ToolTip moved to BaseStyledComponent (28+ controls), ValidationSummary comma-split fixed, SkinID boolstring fixed decided by Cyclops - Team update (2026-02-25): M9 plan ratified 12 WIs across P0/P1/P2, migration fidelity theme decided by Forge +- **Issue #359 — M6-M8 doc page updates (5 pages):** + 1. **ChangePassword** (`docs/LoginControls/ChangePassword.md`) — Verified already complete. Orientation and TextLayout sections with enum tables, migration examples, and `@using BlazorWebFormsComponents.Enums` tip were already present from a prior session. + 2. **PagerSettings** (`docs/DataControls/PagerSettings.md`) — Verified already complete. Properties reference, enum tables, parent control usage examples, and migration notes all match `PagerSettings.cs` source. + 3. **FormView** (`docs/DataControls/FormView.md`) — Added individual CRUD event names (OnItemDeleting/Deleted, OnItemInserting/Inserted, OnItemUpdating/Updated) to Features section. Added "Web Forms Features NOT Supported" section (DataSourceID, ViewState, Theming, RenderTable). Added CRUD event handling example with FormViewUpdateEventArgs/FormViewDeleteEventArgs. + 4. **DetailsView** (`docs/DataControls/DetailsView.md`) — Added Caption/CaptionAlign attributes to Web Forms declarative syntax. Added all 10 style sub-component elements and PagerSettings child element to the Web Forms syntax block, bringing it to parity with actual Web Forms control markup. + 5. **DataGrid** (`docs/DataControls/DataGrid.md`) — Removed stale "not every syntax element supported" caveat (features were implemented in M6-M8). Enhanced paging section with property reference table, PagerStyle example, and admonition explaining DataGrid's built-in numeric pager vs. GridView/FormView/DetailsView PagerSettings. Added PagerSettings cross-reference in See Also. + - All 5 pages verified present in `mkdocs.yml` nav. No nav changes needed. + - **Pattern discovered:** DataGrid is the only pageable data control without PagerSettings sub-component support — it always uses a numeric pager. Worth noting for future migration guidance. -- **M9 Consolidated Audit Report:** Created `planning-docs/AUDIT-REPORT-M9.md` combining findings from three M9 audits: Doc Gap Audit (5 findings → #359), Integration Test Coverage Audit (5 findings → #358), and Sample Navigation Audit (19 findings → #350). All 29 findings mapped to M10 GitHub Issues with 100% coverage. Report includes 6 additional post-M9 findings (component gaps and TreeView bug). Used the planning-docs historical snapshot header convention (`> ⚠️ Historical Snapshot (Milestone 9)`). -- **Audit report convention established:** Consolidated audit reports should live at `planning-docs/AUDIT-REPORT-M{N}.md` with the standard historical snapshot header, summary table, per-audit sections with findings + resolution status, and an appendix issue tracker. This pattern can be reused for future milestone audits. + Team update (2026-02-28): Cyclops fixed MenuItemStyle Font- attributes (SetFontsFromAttributes) and CheckBox bare input id may need doc updates. Issue #379 (LinkButton CssClass) verified as already fixed in M15, can be closed. - Team update (2026-02-25): TreeView NodeImage now checks ShowExpandCollapse independently of ShowLines; ExpandCollapseImage() helper added (#361) decided by Cyclops +- **M10 Skins & Themes Developer Guide (`docs/Migration/SkinsAndThemes.md`):** + - Created comprehensive developer guide following the Utility Feature Documentation Template. + - Structure: Background → Web Forms Usage → Blazor Implementation → Migration Path (6 steps) → Code Examples (4 scenarios) → Limitations (PoC) → Moving On. + - Uses tabbed before/after comparison (pymdownx.tabbed) for the complete migration example. + - Coexists with existing `ThemesAndSkins.md` (strategy/architecture comparison). New doc is the practical "how to use it" guide; existing doc is the "why this approach" analysis. + - Added to `mkdocs.yml` nav as "Skins and Themes Guide" (alphabetical before "Themes and Skins Strategy"). + - Updated `README.md` to replace "skins or themes" deferred statement with active ThemeProvider link. + - **Key convention:** When a feature has both a strategy/comparison doc and a practical guide, use separate files with clear nav labels distinguishing them ("Guide" vs "Strategy"). + Team update (2026-03-01): SkinBuilder uses expression trees for nested property access if API changes, update SkinsAndThemes.md examples first (primary dev-facing doc) decided by Cyclops +📌 Team update (2026-03-02): FontInfo.Name/Names now auto-synced bidirectionally. Theme font-family renders correctly. Update SkinsAndThemes.md if font examples need revision — decided by Cyclops, Rogue +📌 Team update (2026-03-02): CascadedTheme (not Theme) is the cascading parameter name on BaseWebFormsComponent — decided by Cyclops +📌 Team update (2026-03-02): Theming sample page uses 6-section progressive layout (Jubilee). Docs should reference this pattern for theming examples — decided by Jubilee - Team update (2026-02-25): M12 introduces Migration Analysis Tool PoC (`bwfc-migrate` CLI, regex-based ASPX parsing, 3-phase roadmap) decided by Forge - -- **Issue #359 doc updates (M6-M8 features):** Updated 4 existing doc pages and created 1 new page to close gaps identified in the M9 Doc Gap Audit: - 1. **ChangePassword.md** — Added Orientation and TextLayout documentation with reference tables, enum usage examples, and migration Before/After, mirroring the Login.md pattern. - 2. **PagerSettings.md** (NEW) — Created dedicated doc page in DataControls/ covering all properties, PagerButtons/PagerPosition enums, usage with FormView/DetailsView/GridView, and migration notes. Added to mkdocs.yml nav alphabetically. - 3. **FormView.md** — Added ItemCommand, ItemCreated, PageIndexChanging/PageIndexChanged events to features and Blazor syntax. Added Caption/CaptionAlign, PagerSettings child element, PagerTemplate, and 7 style sub-components (RowStyle, EditRowStyle, InsertRowStyle, HeaderStyle, FooterStyle, EmptyDataRowStyle, PagerStyle) with reference table. - 4. **DetailsView.md** — Added Caption/CaptionAlign with reference table. Moved styles and PagerSettings from "NOT Supported" to "Supported" (were stale). Added 10-row style sub-components table and PagerSettings child element to Blazor syntax. Updated migration notes to reflect style child elements. - 5. **DataGrid.md** — Moved Paging, Sorting, Selection, and Editing from "NOT Supported" to "Supported" (all now implemented). Added AllowPaging/PageSize/CurrentPageIndex/PageIndexChanged, AllowSorting/SortCommand, SelectedIndex/EditItemIndex to Blazor syntax. Added paging and sorting examples with event handlers. -- **Documentation pattern: PagerSettings as shared sub-component doc:** PagerSettings is the first shared (non-control) sub-component to get its own dedicated doc page. It's referenced via `[PagerSettings](PagerSettings.md)` links from FormView, DetailsView, and GridView docs. Future shared sub-components (e.g., TableItemStyle if ever documented separately) should follow this pattern. - - - Team update (2026-02-25): All login controls (Login, LoginView, ChangePassword, PasswordRecovery, CreateUserWizard) now inherit from BaseStyledComponent decided by Cyclops - - Team update (2026-02-25): ComponentCatalog.cs now links all sample pages; new samples must be registered there decided by Jubilee - - - Team update (2026-02-25): Future milestone work should include a doc review pass to catch stale 'NOT Supported' entries decided by Beast - - Team update (2026-02-25): Shared sub-components of sufficient complexity get their own doc page (e.g., PagerSettings) decided by Beast - - Team update (2026-02-25): ListView now has full CRUD event parity (7 new events) docs may need updating decided by Cyclops - Team update (2026-02-25): Menu styles use MenuItemStyle pattern with IMenuStyleContainer docs may need updating decided by Cyclops - - Team update (2026-02-25): All new work MUST use feature branches pushed to origin with PR to upstream/dev. Never commit directly to dev. decided by Jeffrey T. Fritz - - - Team update (2026-02-25): Theme core types (#364) use nullable properties for StyleSheetTheme semantics, case-insensitive keys, empty-string default skin key. ThemeProvider is infrastructure, not a WebForms control. GetSkin returns null for missing entries. decided by Cyclops - - - Team update (2026-02-25): SkinID defaults to empty string, EnableTheming defaults to true. [Obsolete] removed these are now functional [Parameter] properties. decided by Cyclops - - - Team update (2026-02-25): ThemeConfiguration CascadingParameter wired into BaseStyledComponent (not BaseWebFormsComponent). ApplySkin runs in OnParametersSet with StyleSheetTheme semantics. Font properties checked individually. decided by Cyclops - -- **ThemesAndSkins.md updated for M10 PoC:** Updated `docs/Migration/ThemesAndSkins.md` to reflect the actual PoC implementation. Key changes: (1) Replaced "Current Status" admonition — removed stale warnings about `[Obsolete]` and `bool` SkinID, replaced with "PoC Implemented (M10)" status. (2) Updated Approach 2 code examples to use real class names (`ThemeConfiguration`, `ControlSkin`, `ThemeProvider`) and actual API (`AddSkin`/`GetSkin` with string control type name). (3) Updated "Recommended Approach" → "Implemented Approach" with present tense. (4) Updated Implementation Roadmap — Phase 1 marked ✅ Complete, Phase 2 deferred items listed for M11. (5) Updated migration Before/After example to use real API with `using BlazorWebFormsComponents.Theming`. (6) Added "PoC Decisions" section documenting 7 design decisions (StyleSheetTheme default, missing SkinID handling, namespace, string keys, ControlSkin mirroring, BaseStyledComponent placement, .skin parser deferral). (7) Added disambiguation note to Approach 4 (DI) since its hypothetical class has the same name as the real implementation. All alternative approaches (1, 3, 4, 5) preserved as reference context. - - - - Team update (2026-02-25): HTML audit strategy approved decided by Forge - - Team update (2026-02-25): HTML audit milestones M11-M13 defined, existing M12M14, Skins/ThemesM15+ decided by Forge per Jeff's directive - - Team update (2026-02-26): Menu RenderingMode=Table added docs may need updating for dual rendering modes decided by Cyclops - - Team update (2026-02-26): Login+Identity strategy defined handler delegates, separate Identity package docs needed when implemented decided by Forge - -- **NamingContainer.md created:** New doc page at `docs/UtilityFeatures/NamingContainer.md` covering the NamingContainer component — a structural (no-HTML) component that establishes naming scopes for child component ID generation, equivalent to Web Forms `INamingContainer`. Documented UseCtl00Prefix parameter, nesting behavior, migration before/after, and relationship to WebFormsPage (which inherits NamingContainer). Added to mkdocs.yml nav alphabetically. Updated IDRendering.md with cross-references to NamingContainer and WebFormsPage in both the Naming Containers section and Related Documentation. -- **Structural component doc pattern:** Components that render no HTML of their own (purely structural) should lead with that fact prominently, since developers expect Blazor components to produce markup. The "renders no HTML" callout and the relationship comparison table (NamingContainer vs WebFormsPage) are reusable patterns for future structural/infrastructure component docs. - - Team update (2026-02-26): WebFormsPage unified wrapper inherits NamingContainer, adds Theme cascading, replaces separate wrappers decided by Jeffrey T. Fritz, Forge - Team update (2026-02-26): Login+Identity controls deferred to future milestone do not schedule docs decided by Jeffrey T. Fritz diff --git a/.ai-team/agents/colossus/history-archive.md b/.ai-team/agents/colossus/history-archive.md new file mode 100644 index 000000000..087c3d1b0 --- /dev/null +++ b/.ai-team/agents/colossus/history-archive.md @@ -0,0 +1,15 @@ +# Colossus — History Archive + + + +## Summary: Milestones 1–3 Integration Tests (2026-02-10 through 2026-02-12) + +Audited 74 sample routes, added 32 missing smoke tests. Added interaction tests for Sprint 2 (MultiView, ChangePassword, CreateUserWizard, Localize) and Sprint 3 (DetailsView paging/edit, PasswordRecovery 3-step flow). Fixed 7 pre-existing failures: missing `@using BlazorWebFormsComponents.LoginControls` on ChangePassword/CreateUserWizard, external placeholder URLs → local SVGs, duplicate ImageMap InlineData, Calendar console error filter, TreeView broken image path. 116 integration tests passing. + +## Summary: Milestone 4 Chart + Utility Tests (2026-02-12) + +Chart: 8 smoke tests + 11 canvas tests + 19 enhanced visual tests (dimensions, Chart.js initialization, multi-series datasets, canvas context). Used `WaitUntilState.DOMContentLoaded` for Chart tests. DataBinder + ViewState: 4 utility feature tests (Eval rendering, ViewState counter increment). Enhanced Chart tests use `BoundingBoxAsync()`, `page.EvaluateAsync` for Chart.js internals, ±10px tolerance for dimensions. Total: 120 integration tests. + +**Key patterns:** `LocatorWaitForOptions` instead of `Expect()` (no PageTest inheritance). `PressSequentiallyAsync` + Tab for Blazor Server InputText binding. ID-specific selectors for multi-instance pages. Filter ISO 8601 timestamps from console errors. + +📌 Team update (2026-02-12): LoginControls sample pages MUST include `@using BlazorWebFormsComponents.LoginControls`. Never use external image URLs. — Colossus diff --git a/.ai-team/agents/colossus/history.md b/.ai-team/agents/colossus/history.md index 020f01191..2f6f3dfc4 100644 --- a/.ai-team/agents/colossus/history.md +++ b/.ai-team/agents/colossus/history.md @@ -1,111 +1,52 @@ # Colossus — History - + -## Summary: Milestones 1–3 Integration Tests (2026-02-10 through 2026-02-12) +## Core Context -Audited 74 sample routes, added 32 missing smoke tests. Added interaction tests for Sprint 2 (MultiView, ChangePassword, CreateUserWizard, Localize) and Sprint 3 (DetailsView paging/edit, PasswordRecovery 3-step flow). Fixed 7 pre-existing failures: missing `@using BlazorWebFormsComponents.LoginControls` on ChangePassword/CreateUserWizard, external placeholder URLs → local SVGs, duplicate ImageMap InlineData, Calendar console error filter, TreeView broken image path. 116 integration tests passing. +Integration test engineer. Built test coverage from M1 through M19. 130+ integration tests (smoke + interaction) covering all milestone sample pages. Key patterns established: `WaitUntilState.DOMContentLoaded` for async-bound components, `Filter(HasTextString)` for specific element targeting, ISO timestamp filtering for console errors, `PressSequentiallyAsync` + Tab for Blazor Server inputs. LoginControls pages require `@using BlazorWebFormsComponents.LoginControls`. Never use external image URLs. Full early history in `history-archive.md`. -## Summary: Milestone 4 Chart + Utility Tests (2026-02-12) +## Key Learnings (Consolidated) -Chart: 8 smoke tests + 11 canvas tests + 19 enhanced visual tests (dimensions, Chart.js initialization, multi-series datasets, canvas context). Used `WaitUntilState.DOMContentLoaded` for Chart tests. DataBinder + ViewState: 4 utility feature tests (Eval rendering, ViewState counter increment). Enhanced Chart tests use `BoundingBoxAsync()`, `page.EvaluateAsync` for Chart.js internals, ±10px tolerance for dimensions. Total: 120 integration tests. +- FormView/ListView bind data in `OnAfterRenderAsync`/`OnAfterRender` — use `DOMContentLoaded` + `WaitForSelectorAsync`. +- Menu interaction tests: skip console error checks (JS interop produces expected errors in headless Playwright). +- Playwright `text=` locator matches innermost element — use `Filter(HasTextString)` on parent container instead. +- For strict-mode violations with duplicate text, target specific element (e.g., `page.Locator("td").Filter(...).First`). +- Use specific selectors like `button:has-text('Edit')` instead of generic selectors to avoid premature wait resolution. +- When sample data models change, interaction test assertions must be updated in lockstep (smoke tests won't catch text mismatches). +- Panel/BackImageUrl has external URLs — smoke test sufficient, no interaction test needed. +- Timer interaction test needs 3-second wait for 2000ms interval tick. +- AJAX controls form a natural test category group. -**Key patterns:** `LocatorWaitForOptions` instead of `Expect()` (no PageTest inheritance). `PressSequentiallyAsync` + Tab for Blazor Server InputText binding. ID-specific selectors for multi-instance pages. Filter ISO 8601 timestamps from console errors. +## Summary: M1–M9 (archived) -📌 Team update (2026-02-12): LoginControls sample pages MUST include `@using BlazorWebFormsComponents.LoginControls`. Never use external image URLs. — Colossus +Covered milestones 1–9: initial smoke tests, Calendar/Chart/FileUpload/ImageMap integration tests, Sprint 2–3 components, M7 data controls (GridView, TreeView, Menu, DetailsView, FormView — 9 smoke + 9 interaction). M9 audit found 105 routes, 100 covered, 5 gaps identified. - Team update (2026-02-23): Milestone 6 Work Plan ratified 54 WIs across P0/P1/P2 tiers decided by Forge - Team update (2026-02-23): UI overhaul requested Colossus assigned integration tests (UI-9) decided by Jeffrey T. Fritz +## Summary: Issue #358 — Smoke + Interaction Tests (2026-02-25 to 2026-02-27) -## Summary: Milestone 7 Integration Tests (2026-02-24) +Added 5 smoke test InlineData entries (M9 audit gaps: ListView/CrudOperations, Label, Panel/BackImageUrl, LoginControls/Orientation, DataGrid/Styles). Later added 5 interaction tests: ListView CRUD (2 tests), Label AssociatedControlID, DataGrid Styles, LoginControls Orientation. Panel/BackImageUrl skipped (static). All gaps closed. -Added 9 smoke tests and 9 interaction tests for M7 sample pages: GridView Selection/DisplayProperties, TreeView Selection/ExpandCollapse, Menu Selection, DetailsView Styles/Caption, FormView Events/Styles. Menu Selection test skips console error checks (JS interop). FormView tests use DOMContentLoaded (items bound in OnAfterRenderAsync). Build verified green. - -## Learnings - -- FormView sample pages bind Items in `OnAfterRenderAsync`, so tests must use `WaitUntilState.DOMContentLoaded` + explicit `WaitForSelectorAsync` instead of `NetworkIdle`. -- Menu interaction tests should always skip console error checks — the Menu component's JS interop (`bwfc.Page.AddScriptElement`) produces expected console errors in headless Playwright. -- GridView Selection pages render Select links as `` elements inside `` rows — use `tbody tr:first-child a` with `HasTextString = "Select"` to target them. -- DetailsView Caption renders actual `` HTML elements that can be directly queried. -- **Playwright `text=` locator gotcha:** `page.Locator("text=Label:")` matches the *innermost* element containing that text. When the markup is `

Label: value

`, the locator returns the ``, not the parent `

` — so the value portion is excluded from `TextContentAsync()`. Fix: use `page.Locator("p").Filter(new() { HasTextString = "Label:" })` (or the appropriate parent tag) to match the container element that holds both the label and value. -- For `

` containers with multiple `` labels (e.g., TreeView/Menu feedback panels), use `page.Locator("div").Filter(new() { HasTextString = "Target label:" }).Last` to match the specific container div. -- When waiting for FormView to render its item template buttons, use a specific selector like `button:has-text('Edit')` instead of generic `button, input[type='submit']` — the latter matches sidebar/nav buttons that already exist, causing the wait to resolve prematurely before the FormView renders. -- To avoid strict-mode violations when text appears in both rendered output AND code examples, target the specific rendered element (e.g., `page.Locator("td").Filter(new() { HasTextString = "Widget Catalog" }).First`) rather than using bare `text=` locators. - - Team update (2026-02-24): Menu auto-ID pattern Menu now auto-generates IDs, JS interop crash fixed decided by Cyclops - Team update (2026-02-24): M8 scope excludes version bump to 1.0 and release decided by Jeffrey T. Fritz - - Team update (2026-02-25): Deployment pipeline patterns established compute Docker version with nbgv before build, gate on secrets, dual NuGet publishing decided by Forge - -## Summary: M9 Integration Test Coverage Audit (WI-11) - -Audited all sample page `@page` routes against ControlSampleTests.cs and InteractiveComponentTests.cs. Found 105 sample routes total; 100 covered by smoke tests, 57 interaction tests exist. Identified **5 pages without any smoke test**: ListView/CrudOperations (M7 — highest priority), Label, Panel/BackImageUrl, LoginControls/Orientation, and DataGrid/Styles. All other M7 features (GridView Selection/DisplayProperties, TreeView Selection/ExpandCollapse, Menu Selection, FormView Events/Styles, DetailsView Styles/Caption) have full smoke + interaction test coverage. Report written to `.ai-team/decisions/inbox/colossus-m9-test-audit.md`. - - Team update (2026-02-25): ToolTip moved to BaseStyledComponent (28+ controls), ValidationSummary comma-split fixed, SkinID boolstring fixed decided by Cyclops - Team update (2026-02-25): M9 plan ratified 12 WIs, migration fidelity decided by Forge - Team update (2026-02-25): Test coverage audit merged 5 gaps identified, P0: ListView CrudOperations decided by Colossus - - Team update (2026-02-25): Consolidated audit reports now use `planning-docs/AUDIT-REPORT-M{N}.md` pattern for all milestone audits decided by Beast - - - Team update (2026-02-25): M12 introduces Migration Analysis Tool PoC (`bwfc-migrate` CLI, regex-based ASPX parsing, 3-phase roadmap) decided by Forge - -## Summary: Issue #358 — 5 Missing Smoke Tests (2026-02-25) - -Added 5 missing smoke test InlineData entries to ControlSampleTests.cs covering all gaps identified in M9 audit: ListView/CrudOperations, Label, Panel/BackImageUrl, LoginControls/Orientation, DataGrid/Styles. All 5 sample pages verified to exist. Tests added as InlineData to existing Theory methods (EditorControl, DataControl, LoginControl). Build verified green (0 errors). - -## Learnings - -- Panel/BackImageUrl sample page uses external placeholder URLs (`via.placeholder.com`). The existing `VerifyPageLoadsWithoutErrors` filter for "Failed to load resource" handles this, so the smoke test works despite the team convention against external image URLs. -- LoginControls/Orientation is at `/ControlSamples/LoginControls/Orientation` (not under `/ControlSamples/Login` or `/ControlSamples/ChangePassword` as initially suggested in the issue). - - - - Team update (2026-02-25): Future milestone work should include a doc review pass to catch stale 'NOT Supported' entries decided by Beast - - Team update (2026-02-25): Shared sub-components of sufficient complexity get their own doc page (e.g., PagerSettings) decided by Beast - - Team update (2026-02-25): All login controls (Login, LoginView, ChangePassword, PasswordRecovery, CreateUserWizard) now inherit from BaseStyledComponent decided by Cyclops - - Team update (2026-02-25): ComponentCatalog.cs now links all sample pages; new samples must be registered there decided by Jubilee - - Team update (2026-02-25): ListView now has full CRUD event parity (7 new events) interaction tests may be needed decided by Cyclops - Team update (2026-02-25): Menu styles use MenuItemStyle with IMenuStyleContainer interaction tests may be needed decided by Cyclops - - Team update (2026-02-25): All new work MUST use feature branches pushed to origin with PR to upstream/dev. Never commit directly to dev. decided by Jeffrey T. Fritz - - - Team update (2026-02-25): Theme core types (#364) use nullable properties for StyleSheetTheme semantics, case-insensitive keys, empty-string default skin key. ThemeProvider is infrastructure, not a WebForms control. GetSkin returns null for missing entries. decided by Cyclops - - - Team update (2026-02-25): SkinID defaults to empty string, EnableTheming defaults to true. [Obsolete] removed these are now functional [Parameter] properties. decided by Cyclops - - - Team update (2026-02-25): ThemeConfiguration CascadingParameter wired into BaseStyledComponent (not BaseWebFormsComponent). ApplySkin runs in OnParametersSet with StyleSheetTheme semantics. Font properties checked individually. decided by Cyclops - - - Team update (2026-02-25): Calendar selection behavior review found 7 issues (1 P0: external SelectedDate sync, 4 P1: SelectWeekText default, SelectedDates sorting/mutability, style layering, 2 P2: test gaps, allocation) decided by Forge - - - Team update (2026-02-25): HTML audit strategy approved decided by Forge - - Team update (2026-02-25): HTML audit milestones M11-M13 defined, existing M12M14, Skins/ThemesM15+ decided by Forge per Jeff's directive - - Team update (2026-02-26): Menu RenderingMode=Table integration tests may need table-mode variants decided by Cyclops - - Team update (2026-02-26): Login+Identity strategy defined integration tests needed when handlers implemented decided by Forge - - Team update (2026-02-26): Data control divergence: normalization pipeline needs stripping and Blazor data control normalization decided by Forge +## Summary: PR #377 DetailsView Integration Test Fix (2026-02-26) - Team update (2026-02-26): Post-fix capture: normalizer needs GUID ID stripping and empty style="" removal decided by Rogue +Fixed 5 stale Customer→Product assertions in InteractiveComponentTests.cs after DetailsView sample pages migrated to Product model. All 7 DetailsView integration tests passing. - Team update (2026-02-26): WebFormsPage unified wrapper inherits NamingContainer, adds Theme cascading, replaces separate wrappers decided by Jeffrey T. Fritz, Forge - Team update (2026-02-26): SharedSampleObjects is the single source for sample data parity between Blazor and WebForms decided by Jeffrey T. Fritz +## Summary: M17 AJAX Control Integration Tests (2026-02-27) -## Summary: PR #377 DetailsView Integration Test Fix (2026-02-26) +Added 5 smoke tests (Timer, UpdatePanel, UpdateProgress, ScriptManager, Substitution) as `AjaxControl_Loads_WithoutErrors` Theory group. Added 1 interaction test for Timer auto-increment. Build green. -Fixed 5 stale Customer→Product assertions in InteractiveComponentTests.cs after DetailsView sample pages migrated from Customer to Product model (SharedSampleObjects.Models.Product). Changes: "Customer Details"→"Product Details" (Styles), "Customer Record"→"Product Record" (Caption), "No customers found."→"No products found." (EmptyData), Customer field names→Product field names in EditMode assertion message. All 7 DetailsView integration tests passing. +## Team Updates (Current) -## Learnings +📌 Team update (2026-02-26): WebFormsPage unified wrapper — inherits NamingContainer, adds Theme cascading — decided by Jeffrey T. Fritz, Forge +📌 Team update (2026-02-26): SharedSampleObjects is the single source for sample data parity — decided by Jeffrey T. Fritz +📌 Team update (2026-02-26): M15 HTML fidelity strategy — full audit pipeline re-run assigned to Colossus — decided by Forge +📌 Team update (2026-02-27): Branching workflow directive — feature PRs from personal fork to upstream dev — decided by Jeffrey T. Fritz +📌 Team update (2026-02-27): Issues must be closed via PR references using 'Closes #N' syntax — decided by Jeffrey T. Fritz +📌 Team update (2026-02-27): M17 AJAX controls implemented — decided by Cyclops +📌 Team update (2026-02-27): M17 audit fixes resolved — 5 fidelity issues, 9 new tests, PR #402 — decided by Forge, Cyclops +📌 Team update (2026-02-27): Timer duplicate [Parameter] bug fixed; 47 M17 tests — decided by Rogue +📌 Team update (2026-02-28): Cyclops fixed CheckBox bare input missing id attribute — integration tests targeting CheckBox by id may now work in no-text scenarios. All 5 M9 audit gap pages now have interaction test coverage. -- When sample data models change (e.g., Customer→Product), integration test assertions referencing model-specific text (header text, empty data messages, caption text, field name lists in assertion messages) must be updated in lockstep. Smoke tests won't catch these because they only verify page loads without errors — interactive tests with text-matching assertions are the ones that break. + Team update (2026-03-01): Normalizer pipeline order is fixed regex rules style norm empty style strip boolean attrs GUID IDs attr sort artifact cleanup whitespace. Case-insensitive file pairing enabled decided by Cyclops + Team update (2026-03-01): D-11 through D-14 formally registered. D-12 boolean attrs and GUID IDs now handled by normalizer decided by Forge +📌 Team update (2026-03-02): FontInfo.Name/Names now auto-synced bidirectionally. Theme font-family renders correctly. Integration tests targeting font-family should now work — decided by Cyclops, Rogue +📌 Team update (2026-03-02): CascadedTheme (not Theme) is the cascading parameter name on BaseWebFormsComponent — decided by Cyclops diff --git a/.ai-team/agents/cyclops/history.md b/.ai-team/agents/cyclops/history.md index a2254f40d..10a33c6e2 100644 --- a/.ai-team/agents/cyclops/history.md +++ b/.ai-team/agents/cyclops/history.md @@ -1,192 +1,78 @@ # Project Context - **Owner:** Jeffrey T. Fritz -- **Project:** BlazorWebFormsComponents — Blazor components emulating ASP.NET Web Forms controls for migration +- **Project:** BlazorWebFormsComponents Blazor components emulating ASP.NET Web Forms controls for migration - **Stack:** C#, Blazor, .NET, ASP.NET Web Forms, bUnit, xUnit, MkDocs, Playwright - **Created:** 2026-02-10 ## Learnings - + -### Core Context (2026-02-10 through 2026-02-25) +### Core Context (2026-02-10 through 2026-02-27) -**M1–M3 components:** Calendar (enum fix, async events), ImageMap (BaseStyledComponent, Guid IDs), FileUpload (InputFile integration, path sanitization), PasswordRecovery (3-step wizard), DetailsView (DataBoundComponent, auto-field reflection, 10 events), Chart (BaseStyledComponent, CascadingValue "ParentChart", JS interop via ChartJsInterop, ChartConfigBuilder pure static). +**M1M3 components:** Calendar (enum fix, async events), ImageMap (BaseStyledComponent, Guid IDs), FileUpload (InputFile integration, path sanitization), PasswordRecovery (3-step wizard), DetailsView (DataBoundComponent, auto-field reflection, 10 events), Chart (BaseStyledComponent, CascadingValue "ParentChart", JS interop via ChartJsInterop, ChartConfigBuilder pure static). -**M6 base class fixes:** DataBoundComponent chain → BaseStyledComponent (14 data controls). BaseListControl for 5 list controls. CausesValidation on CheckBox/RadioButton/TextBox. Label AssociatedControlID switches span→label. Login/ChangePassword/CreateUserWizard → BaseStyledComponent. Validator ControlToValidate dual-path: ForwardRef + string ID via reflection. +**M6 base class fixes:** DataBoundComponent chain BaseStyledComponent (14 data controls). BaseListControl for 5 list controls (DataTextFormatString, AppendDataBoundItems). CausesValidation on CheckBox/RadioButton/TextBox. Label AssociatedControlID switches spanlabel. Login/ChangePassword/CreateUserWizard BaseStyledComponent. Validator ControlToValidate dual-path: ForwardRef + string ID via reflection. -**M6 Menu overhaul:** → BaseStyledComponent. Selection tracking (SelectedItem/SelectedValue, MenuItemClick, MenuItemDataBound). MenuEventArgs, MaximumDynamicDisplayLevels, Orientation enum + CSS horizontal class, MenuLevelStyle lists. +**M6 Menu overhaul:** BaseStyledComponent. Selection tracking (SelectedItem/SelectedValue, MenuItemClick, MenuItemDataBound). MenuEventArgs, MaximumDynamicDisplayLevels, Orientation enum + CSS horizontal class. MenuLevelStyle lists. StaticMenuStyle sub-component + IMenuStyleContainer interface. RenderFragment parameters for all menu styles. RenderingMode=Table added (M14) with inline Razor for AngleSharp compatibility. -**M7 style sub-components:** GridView (8), DetailsView (10), FormView (7), DataGrid (7) — all CascadingParameter + UiTableItemStyle. Style priority: Edit > Selected > Alternating > Row. TreeView: TreeNodeStyle + 6 sub-components, selection, ExpandAll/CollapseAll, FindNode, ExpandDepth, NodeIndent. GridView: selection, 10 display props. FormView/DetailsView events + PagerTemplate + Caption. DataGrid paging/sorting. ListView CRUD events + templates. Panel BackImageUrl. Login Orientation + TextLayout. +**M7 style sub-components:** GridView (8), DetailsView (10), FormView (7), DataGrid (7) all CascadingParameter + UiTableItemStyle. Style priority: Edit > Selected > Alternating > Row. TreeView: TreeNodeStyle + 6 sub-components, selection, ExpandAll/CollapseAll, FindNode, ExpandDepth, NodeIndent. GridView: selection, 10 display props. FormView/DetailsView events + PagerTemplate + Caption. DataGrid paging/sorting. ListView 10 CRUD events + EditItemTemplate/InsertItemTemplate. Panel BackImageUrl. Login Orientation + TextLayout. Shared PagerSettings (12 props, IPagerSettingsContainer) for GridView/FormView/DetailsView. -**M8 bug fixes:** Menu JS null guard, Calendar conditional scope, Menu auto-ID. Shared PagerSettings sub-component (12 props, IPagerSettingsContainer) wired into GridView/FormView/DetailsView. +**M8 bug fixes:** Menu JS null guard + Calendar conditional scope + Menu auto-ID (`menu_{GetHashCode():x}`). -**M9 migration-fidelity:** ToolTip → BaseStyledComponent (removed from 8 components, added `title="@ToolTip"` to 32 components). ValidationSummary comma-split fix. SkinID bool→string fix. TreeView NodeImage fallback restructured (ShowExpandCollapse check + ExpandCollapseImage helper). +**M9 migration-fidelity:** ToolTip BaseStyledComponent (removed from 8, added title="@ToolTip" to 32 components). ValidationSummary comma-split fix (IndexOf + Substring). SkinID boolstring. TreeView NodeImage fallback restructured (ShowExpandCollapse + ExpandCollapseImage helper). -**M10 batch 1:** Panel BackImageUrl already done. LoginView/PasswordRecovery → BaseStyledComponent. Login controls with `` add `class="@CssClass" style="border-collapse:collapse;@Style" title="@ToolTip"`. +**M10 Theming:** ControlSkin (nullable props, StyleSheetTheme semantics). ThemeConfiguration (case-insensitive keys, empty-string default skin, GetSkin returns null). ThemeProvider as CascadingValue wrapper. SkinID="" default, EnableTheming=true, [Obsolete] removed. CascadingParameter in BaseStyledComponent, ApplySkin in OnParametersSet. LoginView/PasswordRecovery BaseStyledComponent. - +**M15 HTML fidelity fixes:** Button `` rendering. BulletedList `` removal + `
    ` CSS-only (no HTML type attr, GetStartAttribute returns int?). LinkButton class + aspNetDisabled. Image longdesc conditional. Calendar structural (tbody, width:14%, day titles, abbr headers, align center, border-collapse, navigation sub-table). FileUpload clean ID. CheckBox span verified. GridView UseAccessibleHeader default falsetrue. 27 test files updated for Button ``. 10 new tests. All 1283 pass. -### Summary: Milestone 6 P0/P1 Implementation (2026-02-23) +**M16:** LoginView wrapper `
    ` for styles (#352). ClientIDMode enum (Inherit/AutoID/Static/Predictable) on BaseWebFormsComponent. ComponentIdGenerator refactored: GetEffectiveClientIDMode(), BuildAutoID(), BuildPredictableID(). UseCtl00Prefix only in AutoID mode. NamingContainer auto-sets AutoID when UseCtl00Prefix=true. -**Base class fixes:** DataBoundComponent chain → BaseStyledComponent (WI-07, 14 data controls). BaseListControl created for 5 list controls with DataTextFormatString + AppendDataBoundItems (WI-47/48). CausesValidation added to CheckBox/RadioButton/TextBox (WI-49). Label AssociatedControlID switches span→label (WI-51). Login/ChangePassword/CreateUserWizard → BaseStyledComponent (WI-52). Validator ControlToValidate dual-path: ForwardRef (ControlRef) + string ID via reflection (WI-36). +**Key patterns:** Orientation enum collides with parameter name use `Enums.Orientation.Vertical`. `_ = callback.InvokeAsync()` for render-time events. `Path.GetFileName()` for file save security. CI secret-gating: env var indirection. Null-returning helpers for conditional HTML attributes. aspNetDisabled class for disabled controls. Always test default parameter values explicitly. -**Menu overhaul (WI-19/21/23/47/50):** → BaseStyledComponent. Selection tracking (SelectedItem/SelectedValue, MenuItemClick, MenuItemDataBound). MenuEventArgs, Value, Target, ValuePath, SkipLinkText. MaximumDynamicDisplayLevels. Orientation enum + CSS horizontal class. MenuLevelStyle (public IStyle class) with LevelMenuItemStyles/LevelSelectedStyles/LevelSubMenuStyles lists. -### Summary: Milestone 7 Data Control Depth (2026-02-24) + -**Style sub-components:** GridView (8 styles, IGridViewStyleContainer), DetailsView (10 styles), FormView (7 styles), DataGrid (7 styles, IDataGridStyleContainer). All follow CascadingParameter pattern with UiTableItemStyle base. Style priority: Edit > Selected > Alternating > Row. +### M17-M20 Wave 1 Context (2026-02-27 through 2026-03-01) -**TreeView enhancements (WI-11/13/15):** TreeNodeStyle (5 props) + 6 sub-components. Selection support (SelectedNode, SelectedNodeChanged, keyboard). ExpandAll/CollapseAll, FindNode, ExpandDepth, NodeIndent, PathSeparator. +**M17 AJAX audit fixes:** ScriptManager EnablePartialRendering default false>true. Scripts collection added (List). UpdateProgress conditional CssClass + display:block;visibility:hidden for non-dynamic mode. ScriptReference gained ScriptMode/NotifyScriptLoaded/ResourceUICultures. Lesson: C# bool defaults false, but Web Forms often defaults true. -**GridView (WI-02/05/07):** Selection (SelectedIndex, AutoGenerateSelectButton, SelectedRow/Value). 10 display props (ShowHeader/Footer, Caption, GridLines, CellPadding/Spacing, EmptyDataTemplate, UseAccessibleHeader). +**M18 bug verification:** #380 BulletedList, #382 CheckBox span, #383 FileUpload GUID all verified already fixed in M15. FileUpload blazor:elementReference is inherent InputFile artifact. Lesson: verify current state before assuming bugs still exist. -**Other controls:** FormView events (ModeChanged, ItemCommand, paging with cancellation) + PagerTemplate + Caption (WI-31/33). DetailsView Caption + PageCount (WI-28). DataGrid paging/sorting events (WI-45). ListView 10 CRUD events + EditItemTemplate/InsertItemTemplate (WI-41). Panel BackImageUrl (WI-48). Login/ChangePassword Orientation + TextLayout (WI-49, LoginTextLayout enum). +**M18 deterministic IDs & Menu fonts:** CheckBox bare input gained id (#386). MenuItemStyle needed SetFontsFromAttributes(OtherAttributes) in OnInitialized (#360) Font-Bold maps to Font.Bold sub-property. Lesson: CaptureUnmatchedValues + Font- attrs need explicit SetFontsFromAttributes handling. -**Key patterns:** Orientation enum collides with parameter name in Razor — use `Enums.Orientation.Vertical` fully-qualified. +**Issue #379:** LinkButton CssClass verified already correct from M15. GetCssClassOrNull() returns null for empty, appends aspNetDisabled when disabled. Edge case: IsNullOrEmpty not IsNullOrWhiteSpace. -📌 Team update (2026-02-23): Milestone 6 Work Plan ratified — 54 WIs across P0/P1/P2 tiers targeting ~345 feature gaps — decided by Forge -📌 Team update (2026-02-23): UI overhaul requested — ComponentCatalog (UI-2) and search (UI-8) assigned to Cyclops — decided by Jeffrey T. Fritz -**Key patterns:** `_ = callback.InvokeAsync()` for render-time events. `Path.GetFileName()` for file save security. Orientation enum collides with parameter name in Razor — use `Enums.Orientation.Vertical`. CI secret-gating: use env var indirection, not `secrets.*` in step-level `if:`. +**Issue #387 normalizer:** 4 enhancements case-insensitive file pairing, boolean attr collapse (6 attrs), empty style stripping, GUID ID placeholders. Pipeline order: regex > style norm > empty style strip > boolean attrs > GUID IDs > attr sort > artifact cleanup > whitespace. Key files: scripts/normalize-html.mjs, scripts/normalize-rules.json. -📌 Team update (2026-02-25): M12 introduces Migration Analysis Tool PoC — decided by Forge +**Issues #364/#365 theming:** SkinBuilder uses expression trees for Set() supports direct (s.BackColor) and nested (s.Font.Bold) via recursive GetOrCreateValue. ThemeConfiguration gained ForControl(name, configure) fluent methods. ThemeProvider cascades via unnamed CascadingValue nesting naturally overrides. WebColor.FromHtml() added. 14 unit + 4 bUnit tests. Lesson: expression trees require recursive auto-init of intermediate nulls. CascadingValue by type sufficient for unique types. -### ListView CRUD Events Completion (#356) +Team update (2026-02-27): Branching workflow feature PRs from personal fork to upstream dev, only dev>main on upstream decided by Jeffrey T. Fritz +Team update (2026-02-27): Issues must be closed via PR references using 'Closes #N' decided by Jeffrey T. Fritz +Team update (2026-02-27): AJAX Controls nav category; migration stub doc pattern for no-ops decided by Beast +Team update (2026-02-27): M17 sample pages created decided by Jubilee +Team update (2026-02-27): Forge approved M17 with 4 non-blocking follow-ups decided by Forge +Team update (2026-02-27): Timer duplicate [Parameter] bug fixed; 47 M17 tests decided by Rogue +Team update (2026-02-27): No-op stub property coverage 41-50% acceptable decided by Forge +Team update (2026-02-27): UpdatePanel Triggers deliberately omitted decided by Forge +Team update (2026-02-28): GetCssClassOrNull() uses IsNullOrEmpty not IsNullOrWhiteSpace low priority noted by Rogue + Team update (2026-03-01): Skins & Themes has dual docs SkinsAndThemes.md (practical guide, update first) and ThemesAndSkins.md (architecture). Update SkinsAndThemes.md first for API changes decided by Beast + Team update (2026-03-01): D-11 through D-14 formally registered D-11 GUID IDs needs fix, D-12 boolean attrs intentional, D-13/D-14 Calendar fixes recommended decided by Forge -- **Sorting/Sorted events:** Added `ListViewSortEventArgs` (SortExpression, SortDirection, Cancel) and `Sorting`/`Sorted` EventCallback parameters. Sort command routed through `HandleCommand("Sort", expression, index)`. Toggles direction when sorting same expression (matches GridView pattern). `SortExpression` and `SortDirection` properties added to ListView. -- **SelectedIndexChanging/SelectedIndexChanged events:** Added `ListViewSelectEventArgs` (NewSelectedIndex, Cancel) and `SelectedIndexChanging`/`SelectedIndexChanged` EventCallback parameters. Select command routed through `HandleCommand("Select", null, index)`. Follows GridView `SelectRow` pattern with cancellation support. -- **PagePropertiesChanging/PagePropertiesChanged events:** Added `ListViewPagePropertiesChangingEventArgs` (StartRowIndex, MaximumRows) and `PagePropertiesChanging`/`PagePropertiesChanged` EventCallback parameters. Exposed via `SetPageProperties(startRowIndex, maximumRows)` public method. Added `StartRowIndex` and `MaximumRows` properties. -- **LayoutCreated event:** Converted from `EventHandler OnLayoutCreated` to `EventCallback OnLayoutCreated`. Wired invocation via `RaiseLayoutCreated()` internal method called in ListView.razor after LayoutTemplate is resolved. -- **Key patterns followed:** EventArgs classes follow Web Forms signatures. Pre-operation events (`Sorting`, `SelectedIndexChanging`) support `Cancel` flag. Post-operation events (`Sorted`, `SelectedIndexChanged`, `PagePropertiesChanged`) fire after state updates. HandleCommand routes "sort" and "select" commands. `SortDirection` enum alias needed in test files to avoid `Shouldly.SortDirection` ambiguity. +### Issue #366 — Base Class Theme Integration (2026-03-01) -- **Menu JS interop crash (Bug 1):** `Menu.js` `Sys.WebForms.Menu` constructor crashes when `getElement()` returns null (e.g., headless Chrome timing). Fixed by adding null guard after `getElement()` (early return if element missing) and wrapping entire constructor body in try/catch to prevent unhandled exceptions from killing the Blazor circuit. File: `src/BlazorWebFormsComponents/wwwroot/Menu/Menu.js`. -- **Calendar attribute rendering (Bug 2):** `Calendar.razor` line 64 used raw Razor expression injection to conditionally add `scope="col"` to `
` tags. This caused `@(UseAccessibleHeader` to appear literally in server logs due to Razor parsing issues. Fixed by replacing with proper conditional attribute: `scope="@(UseAccessibleHeader ? "col" : null)"` -- Blazor omits the attribute entirely when value is null. File: `src/BlazorWebFormsComponents/Calendar.razor`. -- **Menu auto-ID generation (Bug 3):** Menu JS interop requires a DOM element ID, but when no `ID` parameter is provided, it passes an empty string causing null element lookup. Fixed by adding `OnParametersSet` override in `Menu.razor.cs` that auto-generates `menu_{GetHashCode():x}` when ID is null/empty. File: `src/BlazorWebFormsComponents/Menu.razor.cs`. -- **Shared PagerSettings sub-component:** Created `PagerSettings` class (plain C# POCO, not a Blazor component) with all 12 Web Forms PagerSettings properties (Mode, PageButtonCount, First/Last/Next/PreviousPageText, image URLs, Position, Visible). Created `PagerPosition` enum in `Enums/` (PagerButtons already existed). Created `IPagerSettingsContainer` interface in `Interfaces/`. Created `UiPagerSettings` abstract base component following the `UiTableItemStyle` CascadingParameter pattern but for settings instead of styles. Created 3 concrete sub-component pairs: `GridViewPagerSettings`, `FormViewPagerSettings`, `DetailsViewPagerSettings` — each inherits `UiPagerSettings` and uses `[CascadingParameter(Name = "ParentXxx")]` to set properties on the parent's `PagerSettings` instance. Wired into GridView, FormView, DetailsView: added `IPagerSettingsContainer` to each control's interface list, added `PagerSettings` property + `PagerSettingsContent` RenderFragment parameter, rendered `@PagerSettingsContent` inside existing `` block. Key files: `Enums/PagerPosition.cs`, `PagerSettings.cs`, `Interfaces/IPagerSettingsContainer.cs`, `UiPagerSettings.cs`, `GridViewPagerSettings.razor(.cs)`, `FormViewPagerSettings.razor(.cs)`, `DetailsViewPagerSettings.razor(.cs)`. - Team update (2026-02-24): Substitution/Xml formally deferred no implementation needed decided by Beast - Team update (2026-02-24): M8 scope excludes version bump to 1.0 and release decided by Jeffrey T. Fritz +- **Theme wiring moved to BaseWebFormsComponent:** Added `[CascadingParameter] ThemeConfiguration CascadedTheme` to BaseWebFormsComponent. Added `OnParametersSet` override that resolves the ControlSkin via `GetType().Name + SkinID` and calls virtual `ApplyThemeSkin(ControlSkin)`. Base implementation is no-op; BaseStyledComponent overrides to apply IStyle properties. +- **BaseStyledComponent simplified:** Removed `[CascadingParameter] Theme` and `OnParametersSet` override. Renamed `ApplySkin` to `ApplyThemeSkin` (protected override). StyleSheetTheme semantics preserved: theme sets defaults, explicit values win. +- **Property naming:** Named the cascading parameter `CascadedTheme` (not `Theme`) because `_Imports.razor` has `@inherits BaseWebFormsComponent` making ALL `.razor` files inherit from it. WebFormsPage and ThemeProvider both have their own `[Parameter] Theme` — same name would cause Blazor's "declares more than one parameter" error. +- **ThemeProvider fix:** Added `@inherits ComponentBase` to ThemeProvider.razor so it doesn't inherit BaseWebFormsComponent via _Imports.razor. ThemeProvider is infrastructure, not a Web Forms control. +- **WebFormsPage fix:** Changed `` to `Value="@(Theme ?? CascadedTheme)"` so the cascade works whether the user passes Theme explicitly or inherits it from a parent ThemeProvider. +- **Lesson:** `_Imports.razor @inherits BaseWebFormsComponent` affects ALL .razor files in the project, including infrastructure components like ThemeProvider. When adding properties to BaseWebFormsComponent, check for name conflicts with every .razor component's @code block. +- **Lesson:** C# `virtual`/`override` on properties with different attributes ([CascadingParameter] vs [Parameter]) does NOT work for Blazor — reflection returns the base class's attribute, not the override's. Use different property names instead. - Team update (2026-02-25): Deployment pipeline patterns established compute Docker version with nbgv before build, gate on secrets, dual NuGet publishing decided by Forge +### FontInfo Name/Names Auto-Sync Fix (2026-03-01) -### Milestone 9 Migration-Fidelity Fixes (2026-02-25) - -- **ToolTip → BaseStyledComponent (WI-01):** Added `[Parameter] public string ToolTip { get; set; }` to `BaseStyledComponent.cs`. Removed duplicate ToolTip declarations from 8 components: Button, Calendar, DataList, FileUpload, HyperLink, Image, ImageButton, ImageMap. All controls inheriting BaseStyledComponent (28+) now get ToolTip automatically. Intentionally preserved ToolTip on sub-component types (ChartSeries, DataPoint, MenuItem, TreeNode) and binding fields (MenuItemBinding.ToolTipField, TreeNodeBinding.ToolTipField) since those are semantically different item-level tooltips. -- **ValidationSummary comma-split bug fix (WI-05):** `AspNetValidationSummary.razor.cs` used `Split(',')[1]` to extract error messages, which truncated messages containing commas. Fixed to use `IndexOf(',')` + `Substring()` to take everything after the first comma. This is a data corruption bug — any validation message with a comma would silently lose content. -- **SkinID type fix (WI-07):** Changed `SkinID` property in `BaseWebFormsComponent.cs` from `bool` to `string`. Web Forms SkinID is the name of a skin to apply (a string), not a boolean flag. The `[Obsolete]` attribute was preserved since theming is not available in Blazor. -- **ToolTip rendering in templates (WI-03):** Audited all `.razor` files inheriting BaseStyledComponent (directly or via DataBoundComponent chain). Added `title="@ToolTip"` to outermost HTML elements on 32 components that were missing it. Components already rendering ToolTip (Button, Calendar, DataList, FileUpload, HyperLink, Image, ImageButton, ImageMap) were left alone. Skipped: ListView and Repeater (no wrapper element), all style sub-components (GridViewRowStyle, CalendarDayStyle, etc.), and GridViewRow/DataGridRow (row sub-components). For multi-layout components (CheckBoxList, RadioButtonList, Panel, CheckBox, RadioButton), added title to every branch's outermost element. For Login controls (Login, ChangePassword, CreateUserWizard), added title to inner `` elements since outer `` is a Blazor component. TextBox uses `CalculatedAttributes` dictionary — added ToolTip there. All 1206 tests pass. - - Team update (2026-02-25): Doc audit found DetailsView/DataGrid features needing implementation verification decided by Beast - Team update (2026-02-25): Test audit found 5 missing smoke tests (P0: ListView CrudOperations) decided by Colossus - Team update (2026-02-25): M9 plan ratified 12 WIs, migration fidelity decided by Forge - -### Bug Fix: TreeView caret not rotating on expand/collapse (#361) - -- **NodeImage fallback logic restructured (TreeNode.razor.cs lines 176–240):** The `NodeImage` property's three fallback paths (for root, parent, and leaf nodes when `ShowLines=false`) did not check `ShowExpandCollapse`. They relied on `ImageSet.Collapse` being non-empty to determine whether to show expand/collapse images vs `Default_NoExpand.gif`. For any ImageSet where `Collapse` returned empty, nodes would always show `Default_NoExpand.gif` regardless of expanded state. Fixed by adding explicit `if (ParentTreeView.ShowExpandCollapse)` checks in the non-ShowLines paths. Extracted `ExpandCollapseImage(bool expanded)` private helper to DRY the ImageSet→filename resolution with guaranteed fallbacks (`Default_Collapse.gif` / `Default_Expand.gif`). When `ShowExpandCollapse=false`, the method now explicitly returns `Default_NoExpand.gif`. -- **Key files:** `src/BlazorWebFormsComponents/TreeNode.razor.cs` (NodeImage property + ExpandCollapseImage helper). Template in `TreeNode.razor` was already correct — it only renders `NodeImage` when `ShowExpandCollapse=true`. -- **Pattern:** TreeView expand/collapse image resolution has three tiers: (1) ShowLines+ShowExpandCollapse → line-style images (Dash/T/L variants), (2) ShowExpandCollapse only → ImageSet images with Default fallback, (3) neither → NoExpand. The `TreeViewImageSet` base class's `Collapse`/`Expand` properties never return empty for built-in sets, but the code must not assume this. - - Team update (2026-02-25): M12 introduces Migration Analysis Tool PoC (`bwfc-migrate` CLI, regex-based ASPX parsing, 3-phase roadmap) decided by Forge - -### Menu Level Styles — StaticMenuStyle, IMenuStyleContainer (#360) - -- **StaticMenuStyle sub-component:** Added `StaticMenuStyle` class to `MenuItemStyle.razor.cs` following the same pattern as `DynamicMenuStyle` — inherits `MenuItemStyle`, sets `ParentMenu.StaticMenuStyle = this` in `OnInitialized`. Renders CSS to `ul.level1` in `Menu.razor`. -- **IMenuStyleContainer interface:** Created `Interfaces/IMenuStyleContainer.cs` exposing `DynamicMenuStyle`, `StaticMenuStyle`, `DynamicMenuItemStyle`, `StaticMenuItemStyle` as `MenuItemStyle` properties. Menu implements `IMenuStyleContainer` via explicit interface implementation since the concrete Menu properties use derived types (`DynamicMenuStyle`, `StaticMenuStyle`, etc.). -- **RenderFragment parameters:** Added `DynamicMenuStyleContent`, `StaticMenuStyleContent`, `DynamicMenuItemStyleContent`, `StaticMenuItemStyleContent` RenderFragment parameters to Menu. Rendered inside `` block before `@ChildContent`. Added `IsFixed="true"` to the CascadingValue. -- **CSS rendering:** `StaticMenuStyle` CSS applied to `#{ID} ul.level1` in Menu.razor's `

-

+

diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Button/ValidationGroup.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Button/ValidationGroup.razor index 4710066fa..6b5d54a4d 100644 --- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Button/ValidationGroup.razor +++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Button/ValidationGroup.razor @@ -37,7 +37,7 @@ ForeColor="Red" />
-
+
+ + + + + + + + + + + + + + + + + +
PropertyTypeDefaultNotes
EnablePartialRenderingboolfalseNo effect in Blazor
EnablePageMethodsboolfalseNo effect in Blazor
ScriptModeScriptModeAutoNo effect in Blazor
AsyncPostBackTimeoutint90No effect in Blazor
EnableCdnboolfalseNo effect in Blazor
EnableScriptGlobalizationboolfalseNo effect in Blazor
EnableScriptLocalizationboolfalseNo effect in Blazor
+ +
+ +

Web Forms Equivalent

+
<!-- Web Forms — required on every AJAX-enabled page -->
+<asp:ScriptManager ID="ScriptManager1" runat="server"
+    EnablePartialRendering="true"
+    EnablePageMethods="true"
+    AsyncPostBackTimeout="90" />
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Substitution/Default.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Substitution/Default.razor new file mode 100644 index 000000000..363b54939 --- /dev/null +++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Substitution/Default.razor @@ -0,0 +1,78 @@ +@page "/ControlSamples/Substitution" +@using Microsoft.AspNetCore.Http + +

Substitution Component Samples

+ +

The Substitution component emulates the Web Forms asp:Substitution control, which was used to inject dynamic content into output-cached pages. In Blazor, this component invokes a Func<HttpContext, string> callback and renders the returned markup.

+ +
+ +

Basic Callback

+

The SubstitutionCallback parameter accepts a function that receives the current HttpContext and returns an HTML string.

+ + + +
<Substitution SubstitutionCallback="@@GetTimestamp" />
+
+@@code {
+    private string GetTimestamp(HttpContext? context) =>
+        $"<em>Rendered at: {DateTime.Now:h:mm:ss tt}</em>";
+}
+ +
+ +

Using HttpContext

+

The callback receives the current HttpContext, allowing access to request information such as headers, query strings, or the user agent.

+ + + +
<Substitution SubstitutionCallback="@@GetRequestInfo" />
+
+@@code {
+    private string GetRequestInfo(HttpContext? context)
+    {
+        if (context == null) return "<em>No HttpContext available</em>";
+        var method = context.Request.Method;
+        var path = context.Request.Path;
+        return $"<span>Request: {method} {path}</span>";
+    }
+}
+ +
+ +

MethodName Property

+

The MethodName property is preserved for migration reference. In Web Forms, this specified the name of a static callback method. In Blazor, use the SubstitutionCallback parameter instead.

+ + + +
<Substitution MethodName="GetDynamicContent" SubstitutionCallback="@@GetDynamicContent" />
+ +
+ +

Web Forms Equivalent

+
<!-- Web Forms — used inside output-cached pages -->
+<%@@ OutputCache Duration="60" VaryByParam="None" %>
+
+<asp:Substitution ID="Sub1" runat="server" MethodName="GetCurrentTime" />
+
+<!-- Code-behind -->
+public static string GetCurrentTime(HttpContext context)
+{
+    return DateTime.Now.ToString("h:mm:ss tt");
+}
+ +@code { + private string GetTimestamp(HttpContext? context) => + $"Rendered at: {DateTime.Now:h:mm:ss tt}"; + + private string GetRequestInfo(HttpContext? context) + { + if (context == null) return "No HttpContext available"; + var method = context.Request.Method; + var path = context.Request.Path; + return $"Request: {method} {path}"; + } + + private string GetDynamicContent(HttpContext? context) => + $"Dynamic content generated at {DateTime.Now:h:mm:ss tt}"; +} diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Theming/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Theming/Index.razor index 05ed8b799..8556c9070 100644 --- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Theming/Index.razor +++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Theming/Index.razor @@ -5,54 +5,134 @@

Skins & Themes PoC

-

This sample demonstrates the Skins & Themes system, which emulates ASP.NET Web Forms' -.skin file behavior using Blazor's cascading values.

+

This sample demonstrates the Skins & Themes system, which emulates +ASP.NET Web Forms' .skin file behavior using Blazor's cascading values. +A ThemeProvider wraps components and delivers a ThemeConfiguration +via CascadingValue. Each control automatically picks up its matching skin.

-

Themed Components

-

These components receive their visual properties from a ThemeConfiguration -provided via ThemeProvider.

+@* ───────────────────────── Section 1: Default Skins ───────────────────────── *@ - -

Default Skin

-

Button and Label with default theme skin applied:

- + + +
+ Status: @(timerEnabled ? "Running" : "Stopped") — + Seconds elapsed: @secondsElapsed +
+ +
<Timer Interval="1000" Enabled="@@timerEnabled" OnTick="OnToggleTick" />
+
+<button @@onclick="ToggleTimer">
+    @@(timerEnabled ? "Stop Timer" : "Start Timer")
+</button>
+
+@@code {
+    private bool timerEnabled = false;
+    private int secondsElapsed = 0;
+
+    private void ToggleTimer() => timerEnabled = !timerEnabled;
+    private void OnToggleTick() => secondsElapsed++;
+}
+ +
+ +

Web Forms Equivalent

+
<!-- Web Forms -->
+<asp:ScriptManager runat="server" />
+<asp:Timer ID="Timer1" runat="server" Interval="2000" OnTick="Timer1_Tick" />
+<asp:UpdatePanel runat="server">
+    <ContentTemplate>
+        <asp:Label ID="lblCount" runat="server" />
+    </ContentTemplate>
+    <Triggers>
+        <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
+    </Triggers>
+</asp:UpdatePanel>
+ +@code { + private int tickCount = 0; + private bool timerEnabled = false; + private int secondsElapsed = 0; + + private void OnCounterTick() => tickCount++; + private void ToggleTimer() => timerEnabled = !timerEnabled; + private void OnToggleTick() => secondsElapsed++; +} diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/UpdatePanel/Default.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/UpdatePanel/Default.razor new file mode 100644 index 000000000..710cca102 --- /dev/null +++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/UpdatePanel/Default.razor @@ -0,0 +1,83 @@ +@page "/ControlSamples/UpdatePanel" +@using BlazorWebFormsComponents.Enums + +

UpdatePanel Component Samples

+ +

The UpdatePanel component emulates the Web Forms asp:UpdatePanel for migration compatibility. In Web Forms, UpdatePanel enabled partial-page updates via AJAX. In Blazor, all updates are already partial — Blazor's diff-based rendering only updates changed DOM elements. The UpdatePanel component renders a <div> (Block mode) or <span> (Inline mode) wrapper around its content.

+ +
+ +

Block Mode (Default)

+

Renders as a <div> element. This is the default RenderMode.

+ + + +

This content is inside an UpdatePanel in Block mode (renders as a <div>).

+

Click count: @blockClickCount

+ +
+
+ +
<UpdatePanel>
+    <ChildContent>
+        <p>Content inside Block mode UpdatePanel.</p>
+        <button @@onclick="IncrementBlock">Click Me</button>
+    </ChildContent>
+</UpdatePanel>
+ +
+ +

Inline Mode

+

Renders as a <span> element, useful for inline content.

+ +

+ The time is: + + + @currentTime + + + +

+ +
<p>
+    The time is:
+    <UpdatePanel RenderMode="UpdatePanelRenderMode.Inline">
+        <ChildContent>
+            <strong>@@currentTime</strong>
+        </ChildContent>
+    </UpdatePanel>
+</p>
+ +
+ +

UpdatePanel Properties

+

The component also supports UpdateMode and ChildrenAsTriggers properties for migration compatibility, though in Blazor these have no behavioral effect since rendering is always differential.

+ + + +
+ This UpdatePanel has UpdateMode="Conditional" and ChildrenAsTriggers="false". + In Web Forms these controlled when the panel refreshed. In Blazor, the component simply preserves these properties for migration compatibility. +
+
+
+ +
+ +

Web Forms Equivalent

+
<!-- Web Forms -->
+<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
+    <ContentTemplate>
+        <asp:Label ID="lblMessage" runat="server" />
+        <asp:Button ID="btnClick" runat="server" Text="Click Me" OnClick="btnClick_Click" />
+    </ContentTemplate>
+</asp:UpdatePanel>
+ +@code { + private int blockClickCount = 0; + private string currentTime = DateTime.Now.ToString("h:mm:ss tt"); + + private void IncrementBlock() => blockClickCount++; + private void RefreshTime() => currentTime = DateTime.Now.ToString("h:mm:ss tt"); +} diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/UpdateProgress/Default.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/UpdateProgress/Default.razor new file mode 100644 index 000000000..5d708caac --- /dev/null +++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/UpdateProgress/Default.razor @@ -0,0 +1,86 @@ +@page "/ControlSamples/UpdateProgress" + +

UpdateProgress Component Samples

+ +

The UpdateProgress component emulates the Web Forms asp:UpdateProgress control, which displayed a loading indicator during asynchronous postbacks. The component renders its ProgressTemplate content, hidden by default using either display:none (DynamicLayout=true) or visibility:hidden (DynamicLayout=false).

+ +
+ +

DynamicLayout = true (Default)

+

When DynamicLayout is true (the default), the progress content is rendered with display:none, which removes it from the page layout entirely.

+ + + +
+ + Loading, please wait... +
+
+
+ +
<UpdateProgress DynamicLayout="true">
+    <ProgressTemplate>
+        <div class="alert alert-info">
+            <span class="spinner-border spinner-border-sm"></span>
+            Loading, please wait...
+        </div>
+    </ProgressTemplate>
+</UpdateProgress>
+ +

Note: The content above is hidden with display:none — it takes no space in the layout.

+ +
+ +

DynamicLayout = false

+

When DynamicLayout is false, the progress content is rendered with visibility:hidden, which hides it but reserves its layout space.

+ + + +
+ + Processing your request... +
+
+
+ +
<UpdateProgress DynamicLayout="false">
+    <ProgressTemplate>
+        <div class="alert alert-warning">
+            Processing your request...
+        </div>
+    </ProgressTemplate>
+</UpdateProgress>
+ +

Note: The content above is hidden with visibility:hidden — it still occupies space in the layout.

+ +
+ +

Associated with an UpdatePanel

+

Use AssociatedUpdatePanelID to link the progress indicator to a specific UpdatePanel. The DisplayAfter property sets the delay in milliseconds before the progress content appears (default is 500ms).

+ + + +
+ + Updating panel content... +
+
+
+ +
<UpdateProgress AssociatedUpdatePanelID="myPanel" DisplayAfter="200">
+    <ProgressTemplate>
+        Updating panel content...
+    </ProgressTemplate>
+</UpdateProgress>
+ +
+ +

Web Forms Equivalent

+
<!-- Web Forms -->
+<asp:UpdateProgress ID="UpdateProgress1" runat="server"
+    AssociatedUpdatePanelID="UpdatePanel1" DisplayAfter="500">
+    <ProgressTemplate>
+        <img src="loading.gif" alt="Loading..." />
+        Please wait...
+    </ProgressTemplate>
+</asp:UpdateProgress>
diff --git a/scripts/normalize-html.mjs b/scripts/normalize-html.mjs index 1fadd12da..86d1a4db0 100644 --- a/scripts/normalize-html.mjs +++ b/scripts/normalize-html.mjs @@ -184,6 +184,40 @@ function normalizeWhitespace(html) { return result; } +/** + * Normalize boolean HTML attributes so that both the empty-string form + * (e.g., selected="") and the self-named form (e.g., selected="selected") + * collapse to the bare attribute (e.g., selected). + */ +function normalizeBooleanAttributes(html) { + const boolAttrs = ['selected', 'checked', 'disabled', 'readonly', 'multiple', 'nowrap']; + for (const attr of boolAttrs) { + const re = new RegExp(`\\b${attr}=(?:"(?:${attr}|)"|'(?:${attr}|)')`, 'gi'); + html = html.replace(re, attr); + } + return html; +} + +/** + * Strip empty style="" attributes that add no styling information. + */ +function stripEmptyStyles(html) { + return html.replace(/\bstyle=""\s*/gi, ''); +} + +/** + * Replace GUID patterns inside id attribute values with a stable placeholder + * so that auto-generated IDs (CheckBox, RadioButtonList, FileUpload) don't + * cause false divergences. + */ +function normalizeGuidIds(html) { + const guidPattern = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi; + return html.replace(/\bid="([^"]*)"/gi, (_match, idVal) => { + const normalized = idVal.replace(guidPattern, 'GUID'); + return `id="${normalized}"`; + }); +} + /** * Clean up residual empty attribute artifacts left by regex stripping. * e.g., double-spaces from removed attributes. @@ -203,6 +237,9 @@ function cleanupArtifacts(html) { function normalizeHtml(html, rules) { html = applyRegexRules(html, rules); html = normalizeStyleAttributes(html); + html = stripEmptyStyles(html); + html = normalizeBooleanAttributes(html); + html = normalizeGuidIds(html); html = sortAttributes(html); html = cleanupArtifacts(html); html = normalizeWhitespace(html); @@ -347,16 +384,21 @@ function runCompare(dirA, dirB, reportPath) { const filesA = collectHtmlFiles(dirA).map(f => relative(dirA, f)); const filesB = collectHtmlFiles(dirB).map(f => relative(dirB, f)); - const allFiles = [...new Set([...filesA, ...filesB])].sort(); - // Group files by control (first path segment) + // Case-insensitive file pairing to avoid HyperLink/Hyperlink dupes + const mapA = new Map(filesA.map(f => [f.toLowerCase(), f])); + const mapB = new Map(filesB.map(f => [f.toLowerCase(), f])); + const allKeys = [...new Set([...mapA.keys(), ...mapB.keys()])].sort(); + + // Group files by control (first path segment), prefer source A casing const controls = {}; - for (const f of allFiles) { - const parts = f.split(sep); + for (const key of allKeys) { + const displayRel = mapA.get(key) || mapB.get(key); + const parts = displayRel.split(sep); const control = parts.length > 1 ? parts[0] : '(root)'; - const variant = basename(f, extname(f)); + const variant = basename(displayRel, extname(displayRel)); if (!controls[control]) controls[control] = []; - controls[control].push({ rel: f, variant }); + controls[control].push({ relA: mapA.get(key), relB: mapB.get(key), variant }); } let totalCompared = 0; @@ -376,17 +418,17 @@ function runCompare(dirA, dirB, reportPath) { const rows = []; const diffSections = []; - for (const { rel, variant } of variants) { + for (const { relA, relB, variant } of variants) { totalCompared++; - const pathA = join(dirA, rel); - const pathB = join(dirB, rel); + const pathA = relA ? join(dirA, relA) : null; + const pathB = relB ? join(dirB, relB) : null; - if (!existsSync(pathA)) { + if (!pathA) { divergences++; rows.push(`| ${variant} | ❌ Missing in source A | File only exists in second directory |`); continue; } - if (!existsSync(pathB)) { + if (!pathB) { divergences++; rows.push(`| ${variant} | ❌ Missing in source B | File only exists in first directory |`); continue; diff --git a/scripts/normalize-rules.json b/scripts/normalize-rules.json index 11aab2361..02946473a 100644 --- a/scripts/normalize-rules.json +++ b/scripts/normalize-rules.json @@ -152,6 +152,30 @@ "pattern": "CUSTOM_FUNCTION", "replacement": "Handled by normalizeWhitespace() in code", "flags": "" + }, + { + "id": "normalize-boolean-attrs", + "description": "Collapse boolean attributes (selected=\"selected\", selected=\"\") to bare form (selected)", + "enabled": true, + "pattern": "CUSTOM_FUNCTION", + "replacement": "Handled by normalizeBooleanAttributes() in code", + "flags": "" + }, + { + "id": "strip-empty-styles", + "description": "Remove empty style=\"\" attributes that add no styling information", + "enabled": true, + "pattern": "CUSTOM_FUNCTION", + "replacement": "Handled by stripEmptyStyles() in code", + "flags": "" + }, + { + "id": "normalize-guid-ids", + "description": "Replace GUID patterns in id values with GUID placeholder to avoid false divergences from auto-generated IDs", + "enabled": true, + "pattern": "CUSTOM_FUNCTION", + "replacement": "Handled by normalizeGuidIds() in code", + "flags": "" } ] } diff --git a/src/BlazorWebFormsComponents.Test/CheckBox/TextAlign.razor b/src/BlazorWebFormsComponents.Test/CheckBox/TextAlign.razor index c48a2473e..1478c232e 100644 --- a/src/BlazorWebFormsComponents.Test/CheckBox/TextAlign.razor +++ b/src/BlazorWebFormsComponents.Test/CheckBox/TextAlign.razor @@ -38,4 +38,42 @@ elements[1].TagName.ShouldBe("LABEL"); } + // --- Bug fix #382: Combined alignment + no-span-wrapper verification --- + + [Fact] + public void CheckBox_TextAlignRight_RendersInputThenLabelWithNoSpanWrapper() + { + // Bug #382: TextAlign=Right must render then