Skip to content

Commit 618e1f3

Browse files
authored
Add troubleshooting prompts and corresponding tests for Fluent UI Blazor (#4479)
* Add troubleshooting prompts and corresponding tests for Fluent UI Blazor - Implemented TroubleshootPrompts class to generate troubleshooting guidance for common Fluent UI Blazor issues. - Added unit tests for TroubleshootPrompts to validate functionality and ensure comprehensive coverage. - Created additional test classes for various prompt functionalities including Accessibility, CompareComponents, ConfigureLocalization, ConfigureTheming, CreateDataGrid, CreateDialog, CreateDrawer, CreateForm, ExplainComponent, Migration, SetupProject, SuggestComponent, and their respective tests. - Each test class includes multiple test cases to verify the expected behavior of the prompt methods. * Refactor prompt handling for DataGrid and Dialog components to improve size parameter processing and enhance explanation detail levels * Refactor prompt documentation across multiple components to enhance clarity and usability, replacing code snippets with concise descriptions and emphasizing the use of MCP tools for implementation guidance.
1 parent 1c767e4 commit 618e1f3

28 files changed

+2959
-62
lines changed

src/Tools/McpServer/Extensions/ServiceCollectionExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ public static IServiceCollection AddFluentUIMcpServer(this IServiceCollection se
6363
.AddMcpServer()
6464
.WithStdioServerTransport()
6565
.WithToolsFromAssembly(Assembly.GetExecutingAssembly())
66-
.WithResourcesFromAssembly(Assembly.GetExecutingAssembly());
66+
.WithResourcesFromAssembly(Assembly.GetExecutingAssembly())
67+
.WithPromptsFromAssembly(Assembly.GetExecutingAssembly());
6768

6869
return services;
6970
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// ------------------------------------------------------------------------
2+
// This file is licensed to you under the MIT License.
3+
// ------------------------------------------------------------------------
4+
5+
using System.ComponentModel;
6+
using System.Globalization;
7+
using System.Text;
8+
using Microsoft.Extensions.AI;
9+
using ModelContextProtocol.Server;
10+
11+
namespace Microsoft.FluentUI.AspNetCore.McpServer.Prompts;
12+
13+
/// <summary>
14+
/// MCP prompts for implementing accessibility in Fluent UI Blazor.
15+
/// </summary>
16+
[McpServerPromptType]
17+
public class AccessibilityPrompts
18+
{
19+
/// <summary>
20+
/// Generates a prompt to help implement accessibility in Fluent UI Blazor applications.
21+
/// </summary>
22+
/// <param name="componentOrFeature">The component or feature to make accessible.</param>
23+
/// <param name="wcagLevel">WCAG compliance level to target.</param>
24+
[McpServerPrompt(Name = "implement_accessibility")]
25+
[Description("Generates guidance for implementing accessibility (a11y) best practices in Fluent UI Blazor applications.")]
26+
public static ChatMessage ImplementAccessibility(
27+
[Description("The component or feature to make accessible (e.g., 'form', 'navigation', 'data table', 'dialog')")]
28+
string componentOrFeature,
29+
[Description("WCAG compliance level: 'A', 'AA' (recommended), or 'AAA'. Default is 'AA'.")]
30+
string wcagLevel = "AA")
31+
{
32+
var sb = new StringBuilder();
33+
sb.AppendLine("# Implement Accessibility in Fluent UI Blazor");
34+
sb.AppendLine();
35+
sb.AppendLine(CultureInfo.InvariantCulture, $"## Target: {componentOrFeature}");
36+
sb.AppendLine(CultureInfo.InvariantCulture, $"## WCAG Level: {wcagLevel}");
37+
sb.AppendLine();
38+
39+
AppendAccessibilityFeatures(sb);
40+
AppendImplementationGuidelines(sb);
41+
AppendComponentGuidelines(sb);
42+
AppendTestingGuidelines(sb);
43+
AppendRequest(sb, componentOrFeature, wcagLevel);
44+
45+
return new ChatMessage(ChatRole.User, sb.ToString());
46+
}
47+
48+
private static void AppendAccessibilityFeatures(StringBuilder sb)
49+
{
50+
sb.AppendLine("## Fluent UI Blazor Accessibility Features");
51+
sb.AppendLine();
52+
sb.AppendLine("Fluent UI Blazor components are built with accessibility in mind and include:");
53+
sb.AppendLine();
54+
sb.AppendLine("- **ARIA attributes** - Proper roles, labels, and states");
55+
sb.AppendLine("- **Keyboard navigation** - Full keyboard support");
56+
sb.AppendLine("- **Focus management** - Visible focus indicators");
57+
sb.AppendLine("- **Screen reader support** - Semantic HTML and announcements");
58+
sb.AppendLine("- **Color contrast** - Compliant color combinations");
59+
sb.AppendLine();
60+
}
61+
62+
private static void AppendImplementationGuidelines(StringBuilder sb)
63+
{
64+
sb.AppendLine("## Implementation Guidelines");
65+
sb.AppendLine();
66+
67+
// Semantic HTML
68+
sb.AppendLine("### 1. Semantic HTML");
69+
sb.AppendLine();
70+
sb.AppendLine("- Use appropriate HTML elements (button, nav, main, etc.)");
71+
sb.AppendLine("- Add landmark roles for page structure");
72+
sb.AppendLine("- Use heading hierarchy correctly (h1, h2, h3...)");
73+
sb.AppendLine();
74+
75+
// Labels
76+
sb.AppendLine("### 2. Labels and Descriptions");
77+
sb.AppendLine();
78+
sb.AppendLine("- Always provide labels for form inputs");
79+
sb.AppendLine("- For icon-only buttons, use the `AriaLabel` parameter");
80+
sb.AppendLine();
81+
82+
// Keyboard
83+
sb.AppendLine("### 3. Keyboard Navigation");
84+
sb.AppendLine();
85+
sb.AppendLine("- Tab order should be logical");
86+
sb.AppendLine("- Interactive elements must be focusable");
87+
sb.AppendLine("- Use `TabIndex` parameter when needed");
88+
sb.AppendLine();
89+
90+
// Focus
91+
sb.AppendLine("### 4. Focus Management");
92+
sb.AppendLine();
93+
sb.AppendLine("- Dialogs trap focus automatically");
94+
sb.AppendLine("- Return focus to trigger element when closing overlays");
95+
sb.AppendLine();
96+
97+
// Color
98+
sb.AppendLine("### 5. Color and Contrast");
99+
sb.AppendLine();
100+
sb.AppendLine("- Don't rely solely on color to convey meaning");
101+
sb.AppendLine("- Use icons, patterns, or text alongside color");
102+
sb.AppendLine();
103+
}
104+
105+
private static void AppendComponentGuidelines(StringBuilder sb)
106+
{
107+
sb.AppendLine("## Component-Specific Guidelines");
108+
sb.AppendLine();
109+
sb.AppendLine("| Component | Key A11y Features |");
110+
sb.AppendLine("|-----------|-------------------|");
111+
sb.AppendLine("| FluentButton | AriaLabel, keyboard activation |");
112+
sb.AppendLine("| FluentTextInput | Label, Required indicator, validation |");
113+
sb.AppendLine("| FluentDataGrid | Arrow key navigation, sort announcements |");
114+
sb.AppendLine("| FluentDialog | Focus trap, Escape to close |");
115+
sb.AppendLine("| FluentMenu | Arrow navigation, type-ahead |");
116+
sb.AppendLine("| FluentTabs | Arrow key navigation, Tab panel roles |");
117+
sb.AppendLine();
118+
}
119+
120+
private static void AppendTestingGuidelines(StringBuilder sb)
121+
{
122+
sb.AppendLine("## Testing Accessibility");
123+
sb.AppendLine();
124+
sb.AppendLine("1. **Keyboard Testing** - Navigate without a mouse");
125+
sb.AppendLine("2. **Screen Reader** - Test with NVDA, JAWS, or VoiceOver");
126+
sb.AppendLine("3. **Browser Tools** - Use Accessibility DevTools");
127+
sb.AppendLine("4. **Automated Testing** - axe DevTools, Lighthouse");
128+
sb.AppendLine();
129+
}
130+
131+
private static void AppendRequest(StringBuilder sb, string componentOrFeature, string wcagLevel)
132+
{
133+
sb.AppendLine("## Request");
134+
sb.AppendLine();
135+
sb.AppendLine(CultureInfo.InvariantCulture, $"Please provide specific accessibility guidance for implementing `{componentOrFeature}` at WCAG {wcagLevel} level, including:");
136+
sb.AppendLine();
137+
sb.AppendLine("1. Required ARIA attributes");
138+
sb.AppendLine("2. Keyboard interaction patterns");
139+
sb.AppendLine("3. Screen reader announcements");
140+
sb.AppendLine("4. Code examples with proper accessibility");
141+
sb.AppendLine("5. Testing checklist");
142+
sb.AppendLine();
143+
sb.AppendLine("**Important:** Use the available MCP tools to retrieve component documentation and code examples from the Fluent UI Blazor library.");
144+
}
145+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// ------------------------------------------------------------------------
2+
// This file is licensed to you under the MIT License.
3+
// ------------------------------------------------------------------------
4+
5+
using System.ComponentModel;
6+
using System.Globalization;
7+
using System.Text;
8+
using Microsoft.Extensions.AI;
9+
using Microsoft.FluentUI.AspNetCore.McpServer.Services;
10+
using ModelContextProtocol.Server;
11+
12+
namespace Microsoft.FluentUI.AspNetCore.McpServer.Prompts;
13+
14+
/// <summary>
15+
/// MCP prompts for comparing Fluent UI Blazor components.
16+
/// </summary>
17+
[McpServerPromptType]
18+
public class CompareComponentsPrompts
19+
{
20+
private readonly FluentUIDocumentationService _documentationService;
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="CompareComponentsPrompts"/> class.
24+
/// </summary>
25+
public CompareComponentsPrompts(FluentUIDocumentationService documentationService)
26+
{
27+
_documentationService = documentationService;
28+
}
29+
30+
/// <summary>
31+
/// Generates a prompt to compare two or more Fluent UI Blazor components.
32+
/// </summary>
33+
/// <param name="components">Comma-separated list of components to compare.</param>
34+
/// <param name="comparisonFocus">What aspect to focus the comparison on.</param>
35+
[McpServerPrompt(Name = "compare_components")]
36+
[Description("Compares two or more Fluent UI Blazor components to help choose the right one for your needs.")]
37+
public ChatMessage CompareComponents(
38+
[Description("Comma-separated list of components to compare (e.g., 'FluentSelect,FluentCombobox,FluentAutocomplete')")]
39+
string components,
40+
[Description("Focus of comparison: 'features', 'performance', 'accessibility', or 'all' (default: 'all')")]
41+
string comparisonFocus = "all")
42+
{
43+
var componentList = components.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
44+
45+
var sb = new StringBuilder();
46+
sb.AppendLine("# Compare Fluent UI Blazor Components");
47+
sb.AppendLine();
48+
49+
AppendComponentsToCompare(sb, componentList);
50+
AppendComponentDetails(sb, componentList);
51+
AppendComparisonFocus(sb, comparisonFocus);
52+
AppendRequest(sb);
53+
54+
return new ChatMessage(ChatRole.User, sb.ToString());
55+
}
56+
57+
private static void AppendComponentsToCompare(StringBuilder sb, string[] componentList)
58+
{
59+
sb.AppendLine("## Components to Compare");
60+
sb.AppendLine();
61+
62+
foreach (var component in componentList)
63+
{
64+
sb.AppendLine(CultureInfo.InvariantCulture, $"- {component}");
65+
}
66+
67+
sb.AppendLine();
68+
}
69+
70+
private void AppendComponentDetails(StringBuilder sb, string[] componentList)
71+
{
72+
sb.AppendLine("## Component Details");
73+
sb.AppendLine();
74+
75+
foreach (var componentName in componentList)
76+
{
77+
var details = _documentationService.GetComponentDetails(componentName.Trim());
78+
79+
if (details != null)
80+
{
81+
sb.AppendLine(CultureInfo.InvariantCulture, $"### {details.Component.Name}");
82+
sb.AppendLine();
83+
84+
if (!string.IsNullOrEmpty(details.Component.Summary))
85+
{
86+
sb.AppendLine(details.Component.Summary);
87+
}
88+
89+
sb.AppendLine(CultureInfo.InvariantCulture, $"- **Category:** {details.Component.Category}");
90+
sb.AppendLine(CultureInfo.InvariantCulture, $"- **Parameters:** {details.Parameters.Count}");
91+
sb.AppendLine(CultureInfo.InvariantCulture, $"- **Events:** {details.Events.Count}");
92+
93+
if (details.Component.IsGeneric)
94+
{
95+
sb.AppendLine("- **Generic:** Yes");
96+
}
97+
98+
sb.AppendLine();
99+
}
100+
else
101+
{
102+
sb.AppendLine(CultureInfo.InvariantCulture, $"### {componentName}");
103+
sb.AppendLine("(Component details not found in documentation)");
104+
sb.AppendLine();
105+
}
106+
}
107+
}
108+
109+
private static void AppendComparisonFocus(StringBuilder sb, string comparisonFocus)
110+
{
111+
sb.AppendLine("## Comparison Focus");
112+
sb.AppendLine();
113+
114+
if (string.Equals(comparisonFocus, "FEATURES", StringComparison.OrdinalIgnoreCase))
115+
{
116+
sb.AppendLine("Focus on: **Feature set and capabilities**");
117+
}
118+
else if (string.Equals(comparisonFocus, "PERFORMANCE", StringComparison.OrdinalIgnoreCase))
119+
{
120+
sb.AppendLine("Focus on: **Performance characteristics**");
121+
}
122+
else if (string.Equals(comparisonFocus, "ACCESSIBILITY", StringComparison.OrdinalIgnoreCase))
123+
{
124+
sb.AppendLine("Focus on: **Accessibility features**");
125+
}
126+
else
127+
{
128+
sb.AppendLine("Focus on: **All aspects** (features, performance, accessibility, use cases)");
129+
}
130+
131+
sb.AppendLine();
132+
}
133+
134+
private static void AppendRequest(StringBuilder sb)
135+
{
136+
sb.AppendLine("## Please Provide");
137+
sb.AppendLine();
138+
sb.AppendLine("1. A comparison table highlighting key differences");
139+
sb.AppendLine("2. When to use each component");
140+
sb.AppendLine("3. Pros and cons of each");
141+
sb.AppendLine("4. Code examples showing similar functionality");
142+
sb.AppendLine("5. Recommendation based on common use cases");
143+
}
144+
}

0 commit comments

Comments
 (0)