mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
198e58c983
* Add dotnet-blazor plugin and convert-blazor-server-to-webapp skill Add the dotnet-blazor plugin with 9 skills covering Blazor Web App development: - plan-ui-change: Plan and scaffold UI features in Blazor Web Apps - create-blazor-project: Create new Blazor projects with proper render mode setup - author-component: Author Razor components with parameters, events, lifecycle - coordinate-components: Share state across components using CascadingValueSource/scoped services - use-js-interop: Call JavaScript from Blazor and vice versa - fetch-and-send-data: HTTP data access with proper service patterns - configure-auth: Set up ASP.NET Core Identity and authorization in Blazor - collect-user-input: Build forms with EditForm, validation, and file uploads - support-prerendering: Handle prerendering lifecycle and state persistence Add convert-blazor-server-to-webapp skill to the dotnet-aspnet plugin for migrating .NET 7 Blazor Server apps to .NET 8+ Blazor Web App architecture. All 10 skills pass evaluation with quality improvements ranging from 12-50% and overfitting scores within acceptable thresholds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update eng/allowed-external-deps.txt * Update .gitignore --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
159 lines
8.7 KiB
YAML
159 lines
8.7 KiB
YAML
scenarios:
|
|
- name: "Auto-saving notepad that survives page reloads"
|
|
prompt: |
|
|
I need a `Notepad` component for my Blazor Web App.
|
|
|
|
Requirements:
|
|
- A textarea where the user can type notes
|
|
- Content is automatically saved to the browser every 30 seconds
|
|
- When the user returns to the page (or reloads), their last saved content is restored
|
|
- A "Last saved at HH:MM:SS" label that updates after each save
|
|
- If the user has unsaved changes and tries to close the browser tab, show the browser's
|
|
built-in "Leave site?" confirmation dialog
|
|
- A "Clear" button that erases both the textarea and the saved data
|
|
- A "Save now" button for manual saves
|
|
setup: &blazor_project
|
|
commands:
|
|
- "dotnet new blazor --interactivity Server --no-https"
|
|
assertions:
|
|
- type: "output_contains"
|
|
value: "OnAfterRenderAsync"
|
|
- type: "output_contains"
|
|
value: "IAsyncDisposable"
|
|
- type: "output_matches"
|
|
pattern: "(export function|export \\{)"
|
|
rubric:
|
|
- "Uses a collocated .razor.js file with exported functions — not a global script tag or window-level globals"
|
|
- "Initializes interop in OnAfterRenderAsync(firstRender), not in OnInitializedAsync — JS is unavailable during prerendering"
|
|
- "Restores saved content on first interactive render so the user sees their previous notes immediately"
|
|
- "Batches related browser operations into single JS calls where possible — e.g., save content + timestamp together instead of separate interop calls for each"
|
|
- "Registers and removes the beforeunload listener properly — adds on setup, removes on dispose"
|
|
- "Manages the auto-save interval in JS and clears it on dispose — no dangling timers"
|
|
- "Implements IAsyncDisposable and catches JSDisconnectedException so disposal doesn't throw when the circuit is already gone"
|
|
- "Uses InvokeVoidAsync when no return value is needed"
|
|
- "Does not use fire-and-forget (unawaited ValueTask) — all JS calls are properly awaited"
|
|
- "The clear function removes stored data and resets the beforeunload guard in one operation"
|
|
- "Uses sessionStorage (or localStorage) for persistence, the beforeunload event for the unsaved-changes prompt, and setInterval/clearInterval for the auto-save timer"
|
|
timeout: 600
|
|
|
|
- name: "User activity tracker that detects idle timeout"
|
|
prompt: |
|
|
I need a `UserActivityTracker` component for my Blazor Web App.
|
|
|
|
Requirements:
|
|
- Monitors user activity on the page (mouse movement, key presses, clicks, scrolling)
|
|
- After a configurable period of inactivity, triggers an `EventCallback OnIdle` to notify the parent
|
|
- When the user becomes active again after being idle, triggers `EventCallback OnActive`
|
|
- The idle timeout duration is set via `[Parameter] int TimeoutSeconds` (default 300)
|
|
- Wraps child content — can be placed around any section of the app
|
|
- After the component is removed from the page, no leftover timers or event handlers should remain
|
|
setup: *blazor_project
|
|
assertions:
|
|
- type: "output_contains"
|
|
value: "OnAfterRenderAsync"
|
|
- type: "output_contains"
|
|
value: "DotNetObjectReference"
|
|
- type: "output_contains"
|
|
value: "IAsyncDisposable"
|
|
- type: "output_matches"
|
|
pattern: "(export function|export \\{)"
|
|
rubric:
|
|
- "Uses a collocated .razor.js file with exported functions — not global window functions or inline scripts"
|
|
- "Registers activity event listeners (mousemove, keydown, click, scroll) in OnAfterRenderAsync(firstRender) — not during prerendering"
|
|
- "Uses DotNetObjectReference so JS can call back into .NET for idle/active transitions and disposes it in DisposeAsync"
|
|
- "Implements IAsyncDisposable and catches JSDisconnectedException in DisposeAsync"
|
|
- "Manages the idle timeout timer in JavaScript using setTimeout/clearTimeout — not via .NET Task.Delay"
|
|
- "Cleans up all event listeners and timers in the JS dispose function — no dangling handlers after component removal"
|
|
timeout: 600
|
|
|
|
|
|
- name: "Responsive layout that adapts to screen size"
|
|
prompt: |
|
|
My Blazor Web App needs to adapt its layout based on the browser viewport width. I need:
|
|
|
|
1. A `ScreenSizeProvider` component that wraps child content and detects the current
|
|
viewport category:
|
|
- `Mobile` (width < 768px)
|
|
- `Tablet` (768px ≤ width < 1024px)
|
|
- `Desktop` (width ≥ 1024px)
|
|
|
|
2. The current category should be available to all child components as a cascading value
|
|
|
|
3. When the user resizes their browser, the category should update in real time
|
|
|
|
4. The default category (before detection runs) should be `Desktop`
|
|
|
|
Usage:
|
|
```razor
|
|
<ScreenSizeProvider>
|
|
<MyNavigation /> @* can read the cascading ScreenSize value *@
|
|
<MyContent />
|
|
</ScreenSizeProvider>
|
|
```
|
|
setup: *blazor_project
|
|
assertions:
|
|
- type: "output_contains"
|
|
value: "CascadingValue"
|
|
- type: "output_contains"
|
|
value: "OnAfterRenderAsync"
|
|
- type: "output_contains"
|
|
value: "IAsyncDisposable"
|
|
- type: "output_matches"
|
|
pattern: "(export function|export \\{)"
|
|
rubric:
|
|
- "Provides the screen size category to children via CascadingValue — child components can consume it with [CascadingParameter]"
|
|
- "Uses a collocated .razor.js module with exported functions — not global scripts"
|
|
- "Sets up matchMedia listeners or resize listener in OnAfterRenderAsync(firstRender) — not in OnInitializedAsync"
|
|
- "Defaults to Desktop (or a sensible fallback) during prerendering before JavaScript is available"
|
|
- "Updates the category in real time when the browser is resized — uses event-based detection, not polling"
|
|
- "Uses DotNetObjectReference for the JS resize/matchMedia callback and disposes it in DisposeAsync"
|
|
- "The [JSInvokable] callback wraps state changes in InvokeAsync(StateHasChanged)"
|
|
- "Implements IAsyncDisposable and catches JSDisconnectedException in DisposeAsync"
|
|
- "Removes matchMedia/resize event listeners in the JS dispose function — no dangling listeners"
|
|
- "Does not call JS interop during prerendering — guards all JS calls behind firstRender or a flag"
|
|
- "Uses window.matchMedia or the resize event for viewport detection — not polling or server-side user-agent sniffing"
|
|
timeout: 600
|
|
|
|
- name: "Infinite scroll list using IntersectionObserver"
|
|
prompt: |
|
|
I need an `InfiniteScrollList` component for my Blazor Web App that loads more items
|
|
as the user scrolls.
|
|
|
|
Requirements:
|
|
- Displays items in a scrollable container
|
|
- When the user scrolls near the bottom, automatically triggers loading of more items
|
|
- Shows a loading spinner while fetching
|
|
- Stops triggering when there are no more items (parent signals this via a parameter)
|
|
- The component is generic — it accepts a `RenderFragment<TItem>` for rendering each item
|
|
- Uses an efficient browser API for scroll detection (not scroll event listeners)
|
|
|
|
Usage:
|
|
```razor
|
|
<InfiniteScrollList Items="products" HasMore="hasMore"
|
|
OnLoadMore="LoadNextPage" Context="product">
|
|
<div class="product-card">@product.Name — @product.Price</div>
|
|
</InfiniteScrollList>
|
|
```
|
|
setup: *blazor_project
|
|
assertions:
|
|
- type: "output_contains"
|
|
value: "IntersectionObserver"
|
|
- type: "output_contains"
|
|
value: "OnAfterRenderAsync"
|
|
- type: "output_contains"
|
|
value: "IAsyncDisposable"
|
|
- type: "output_contains"
|
|
value: "DotNetObjectReference"
|
|
rubric:
|
|
- "Uses a collocated .razor.js file with exported functions — not a global script"
|
|
- "Creates IntersectionObserver in OnAfterRenderAsync(firstRender) — not during prerendering"
|
|
- "Uses a sentinel element (observed by IntersectionObserver) at the bottom of the list to detect when to load more"
|
|
- "Uses DotNetObjectReference to receive the intersection callback from JS and disposes it in DisposeAsync"
|
|
- "The [JSInvokable] callback is public and wraps state changes in InvokeAsync(StateHasChanged)"
|
|
- "Calls observer.disconnect() in the JS dispose function — no dangling observers"
|
|
- "Implements IAsyncDisposable and catches JSDisconnectedException in DisposeAsync"
|
|
- "Uses ElementReference for the sentinel element — not a string ID"
|
|
- "Respects the HasMore parameter — does not trigger loads or observe the sentinel when there are no more items"
|
|
- "Uses IntersectionObserver (not scroll event listeners) for efficient visibility detection"
|
|
timeout: 600
|