Skip to content

Commit 9045236

Browse files
authored
Merge pull request #308 from CommunityToolkit/llama/sample-app-links
Sample App Issue/Discussion Links and Markdown Rendering
2 parents ff19ced + d9a813d commit 9045236

File tree

38 files changed

+262
-257
lines changed

38 files changed

+262
-257
lines changed

common/CommunityToolkit.Labs.Core.SourceGenerators.Tests/CommunityToolkit.Labs.Core.SourceGenerators.Tests/ToolkitSampleMetadataTests.Documentation.cs

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Without any front matter.
4848
[DataRow(4, DisplayName = "Keywords")]
4949
[DataRow(7, DisplayName = "Category")]
5050
[DataRow(8, DisplayName = "Subcategory")]
51+
[DataRow(9, DisplayName = "GitHub Discussion Id")]
52+
[DataRow(10, DisplayName = "GitHub Issue Id")]
5153
[TestMethod]
5254
public void MissingFrontMatterField(int removeline)
5355
{
@@ -60,6 +62,8 @@ public void MissingFrontMatterField(int removeline)
6062
- csharp
6163
category: Controls
6264
subcategory: Layout
65+
discussion-id: 0
66+
issue-id: 0
6367
---
6468
# This is some test documentation...
6569
> [!SAMPLE Sample]
@@ -85,6 +89,8 @@ public void MarkdownInvalidSampleReference()
8589
- csharp
8690
category: Controls
8791
subcategory: Layout
92+
discussion-id: 0
93+
issue-id: 0
8894
---
8995
# This is some test documentation...
9096
> [!SAMPLE SampINVALIDle]
@@ -108,9 +114,11 @@ public void DocumentationMissingSample()
108114
- csharp
109115
category: Controls
110116
subcategory: Layout
117+
discussion-id: 0
118+
issue-id: 0
111119
---
112120
# This is some test documentation...
113-
Without any front matter.";
121+
Without any sample.";
114122

115123
VerifyGeneratedDiagnostics<ToolkitSampleMetadataGenerator>(SimpleSource, markdown,
116124
DiagnosticDescriptors.DocumentationHasNoSamples.Id,
@@ -129,11 +137,59 @@ public void DocumentationValid()
129137
- csharp
130138
category: Controls
131139
subcategory: Layout
140+
discussion-id: 0
141+
issue-id: 0
132142
---
133143
# This is some test documentation...
134-
Without any front matter.
144+
Which is valid.
135145
> [!SAMPLE Sample]";
136146

137147
VerifyGeneratedDiagnostics<ToolkitSampleMetadataGenerator>(SimpleSource, markdown);
138148
}
149+
150+
[TestMethod]
151+
public void DocumentationInvalidDiscussionId()
152+
{
153+
string markdown = @"---
154+
title: Canvas Layout
155+
author: mhawker
156+
description: A canvas-like VirtualizingLayout for use in an ItemsRepeater
157+
keywords: CanvasLayout, ItemsRepeater, VirtualizingLayout, Canvas, Layout, Panel, Arrange
158+
dev_langs:
159+
- csharp
160+
category: Controls
161+
subcategory: Layout
162+
discussion-id: https://github.com/1234
163+
issue-id: 0
164+
---
165+
# This is some test documentation...
166+
Without an invalid discussion id.";
167+
168+
VerifyGeneratedDiagnostics<ToolkitSampleMetadataGenerator>(string.Empty, markdown,
169+
DiagnosticDescriptors.MarkdownYAMLFrontMatterException.Id,
170+
DiagnosticDescriptors.DocumentationHasNoSamples.Id);
171+
}
172+
173+
[TestMethod]
174+
public void DocumentationInvalidIssueId()
175+
{
176+
string markdown = @"---
177+
title: Canvas Layout
178+
author: mhawker
179+
description: A canvas-like VirtualizingLayout for use in an ItemsRepeater
180+
keywords: CanvasLayout, ItemsRepeater, VirtualizingLayout, Canvas, Layout, Panel, Arrange
181+
dev_langs:
182+
- csharp
183+
category: Controls
184+
subcategory: Layout
185+
discussion-id: 0
186+
issue-id: https://github.com/1234
187+
---
188+
# This is some test documentation...
189+
Without an invalid discussion id.";
190+
191+
VerifyGeneratedDiagnostics<ToolkitSampleMetadataGenerator>(string.Empty, markdown,
192+
DiagnosticDescriptors.MarkdownYAMLFrontMatterException.Id,
193+
DiagnosticDescriptors.DocumentationHasNoSamples.Id);
194+
}
139195
}

common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/ToolkitFrontMatter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ public sealed class ToolkitFrontMatter : DocsFrontMatter
1818
public ToolkitSampleCategory Category { get; set; }
1919
public ToolkitSampleSubcategory Subcategory { get; set; }
2020

21+
public int DiscussionId { get; set; }
22+
public int IssueId { get; set; }
23+
2124
//// Extra Metadata needed for Sample App
2225
public string? FilePath { get; set; }
23-
2426
public string[]? SampleIdReferences { get; set; }
2527
}

common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleMetadataGenerator.Documentation.cs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ public partial class ToolkitSampleMetadataGenerator
2929
private const string FrontMatterRegexSubcategoryExpression = @"^subcategory:\s*(?<subcategory>.*)$";
3030
private static readonly Regex FrontMatterRegexSubcategory = new Regex(FrontMatterRegexSubcategoryExpression, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
3131

32+
private const string FrontMatterRegexDiscussionIdExpression = @"^discussion-id:\s*(?<discussionid>.*)$";
33+
private static readonly Regex FrontMatterRegexDiscussionId = new Regex(FrontMatterRegexDiscussionIdExpression, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
34+
35+
private const string FrontMatterRegexIssueIdExpression = @"^issue-id:\s*(?<issueid>.*)$";
36+
private static readonly Regex FrontMatterRegexIssueId = new Regex(FrontMatterRegexIssueIdExpression, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
37+
3238
private const string MarkdownRegexSampleTagExpression = @"^>\s*\[!SAMPLE\s*(?<sampleid>.*)\s*\]\s*$";
3339
private static readonly Regex MarkdownRegexSampleTag = new Regex(MarkdownRegexSampleTagExpression, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
3440

@@ -105,9 +111,13 @@ private ImmutableArray<ToolkitFrontMatter> GatherDocumentFrontMatter(SourceProdu
105111
var category = ParseYamlField(ref ctx, file.Path, ref frontmatter, FrontMatterRegexCategory, "category");
106112
var subcategory = ParseYamlField(ref ctx, file.Path, ref frontmatter, FrontMatterRegexSubcategory, "subcategory");
107113

114+
// TODO: Should these just be optional?
115+
var discussion = ParseYamlField(ref ctx, file.Path, ref frontmatter, FrontMatterRegexDiscussionId, "discussionid")?.Trim();
116+
var issue = ParseYamlField(ref ctx, file.Path, ref frontmatter, FrontMatterRegexIssueId, "issueid")?.Trim();
117+
108118
// Check we have all the fields we expect to continue (errors will have been spit out otherwise already from the ParseYamlField method)
109119
if (title == null || description == null || keywords == null ||
110-
category == null || subcategory == null)
120+
category == null || subcategory == null || discussion == null || issue == null)
111121
{
112122
return null;
113123
}
@@ -159,6 +169,28 @@ private ImmutableArray<ToolkitFrontMatter> GatherDocumentFrontMatter(SourceProdu
159169
file.Path));
160170
}
161171

172+
if (!int.TryParse(discussion, out var discussionId) || discussionId < 0)
173+
{
174+
ctx.ReportDiagnostic(
175+
Diagnostic.Create(
176+
DiagnosticDescriptors.MarkdownYAMLFrontMatterException,
177+
Location.Create(file.Path, TextSpan.FromBounds(0, 1), new LinePositionSpan(LinePosition.Zero, LinePosition.Zero)),
178+
file.Path,
179+
"Can't parse discussion-id field, must be a positive integer or zero."));
180+
return null;
181+
}
182+
183+
if (!int.TryParse(issue, out var issueId) || issueId < 0)
184+
{
185+
ctx.ReportDiagnostic(
186+
Diagnostic.Create(
187+
DiagnosticDescriptors.MarkdownYAMLFrontMatterException,
188+
Location.Create(file.Path, TextSpan.FromBounds(0, 1), new LinePositionSpan(LinePosition.Zero, LinePosition.Zero)),
189+
file.Path,
190+
"Can't parse issue-id field, must be a positive integer or zero."));
191+
return null;
192+
}
193+
162194
// Finally, construct the complete object.
163195
return new ToolkitFrontMatter()
164196
{
@@ -168,13 +200,15 @@ private ImmutableArray<ToolkitFrontMatter> GatherDocumentFrontMatter(SourceProdu
168200
Category = categoryValue,
169201
Subcategory = subcategoryValue,
170202
FilePath = filepath,
171-
SampleIdReferences = sampleids.ToArray()
203+
SampleIdReferences = sampleids.ToArray(),
204+
DiscussionId = discussionId,
205+
IssueId = issueId,
172206
};
173207
}
174208
}).OfType<ToolkitFrontMatter>().ToImmutableArray();
175209
}
176210

177-
private string? ParseYamlField(ref SourceProductionContext ctx, string filepath, ref string content, Regex pattern, string fieldname)
211+
private string? ParseYamlField(ref SourceProductionContext ctx, string filepath, ref string content, Regex pattern, string captureGroupName)
178212
{
179213
var match = pattern.Match(content);
180214

@@ -185,11 +219,11 @@ private ImmutableArray<ToolkitFrontMatter> GatherDocumentFrontMatter(SourceProdu
185219
DiagnosticDescriptors.MarkdownYAMLFrontMatterMissingField,
186220
Location.Create(filepath, TextSpan.FromBounds(0, 1), new LinePositionSpan(LinePosition.Zero, LinePosition.Zero)),
187221
filepath,
188-
fieldname));
222+
captureGroupName));
189223
return null;
190224
}
191225

192-
return match.Groups[fieldname].Value.Trim();
226+
return match.Groups[captureGroupName].Value.Trim();
193227
}
194228

195229
private void CreateDocumentRegistry(SourceProductionContext ctx, ImmutableArray<ToolkitFrontMatter> matter)
@@ -223,6 +257,6 @@ private static string FrontMatterToRegistryCall(ToolkitFrontMatter metadata)
223257
var categoryParam = $"{nameof(ToolkitSampleCategory)}.{metadata.Category}";
224258
var subcategoryParam = $"{nameof(ToolkitSampleSubcategory)}.{metadata.Subcategory}";
225259

226-
return @$"yield return new {typeof(ToolkitFrontMatter).FullName}() {{ Title = ""{metadata.Title}"", Author = ""{metadata.Author}"", Description = ""{metadata.Description}"", Keywords = ""{metadata.Keywords}"", Category = {categoryParam}, Subcategory = {subcategoryParam}, FilePath = @""{metadata.FilePath}"", SampleIdReferences = new string[] {{ ""{string.Join("\",\"", metadata.SampleIdReferences)}"" }} }};"; // TODO: Add list of sample ids in document
260+
return @$"yield return new {typeof(ToolkitFrontMatter).FullName}() {{ Title = ""{metadata.Title}"", Author = ""{metadata.Author}"", Description = ""{metadata.Description}"", Keywords = ""{metadata.Keywords}"", Category = {categoryParam}, Subcategory = {subcategoryParam}, DiscussionId = {metadata.DiscussionId}, IssueId = {metadata.IssueId}, FilePath = @""{metadata.FilePath}"", SampleIdReferences = new string[] {{ ""{string.Join("\",\"", metadata.SampleIdReferences)}"" }} }};"; // TODO: Add list of sample ids in document
227261
}
228262
}

common/CommunityToolkit.Labs.Core.SourceGenerators/ToolkitSampleSubcategory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,10 @@ public enum ToolkitSampleSubcategory : byte
3131
/// A sample that focuses input controls.
3232
/// </summary>
3333
Input,
34+
35+
/// <summary>
36+
/// A sample that focuses on media controls or behaviors.
37+
/// </summary>
38+
Media,
3439
}
3540

common/CommunityToolkit.Labs.Shared/CommunityToolkit.Labs.Shared.projitems

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,7 @@
253253
<Content Include="$(MSBuildThisFileDirectory)Assets\Wide310x150Logo.scale-400_altform-colorful_theme-light.png" />
254254
<Content Include="$(MSBuildThisFileDirectory)LinkerConfig.xml" />
255255
</ItemGroup>
256+
<ItemGroup>
257+
<EmbeddedResource Include="$(MSBuildThisFileDirectory)WasmCSS\Fonts.css" />
258+
</ItemGroup>
256259
</Project>

common/CommunityToolkit.Labs.Shared/Helpers/IconHelper.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ public static class IconHelper
1313
IconElement? iconElement = null;
1414
switch (category)
1515
{
16-
case ToolkitSampleCategory.Controls: iconElement = new FontIcon() { Glyph = "\uE73A" }; break;
17-
case ToolkitSampleCategory.Animations: iconElement = new FontIcon() { Glyph = "\uE945" }; break;
18-
case ToolkitSampleCategory.Behaviors: iconElement = new FontIcon() { Glyph = "\uE2AC" }; break;
16+
case ToolkitSampleCategory.Controls: iconElement = new FontIcon() { Glyph = "\ue73a" }; break;
17+
case ToolkitSampleCategory.Animations: iconElement = new FontIcon() { Glyph = "\ue945" }; break;
18+
case ToolkitSampleCategory.Behaviors: iconElement = new FontIcon() { Glyph = "\ue8b1" }; break;
1919
}
2020
return iconElement;
2121
}
@@ -26,8 +26,9 @@ public static string GetSubcategoryIcon(ToolkitSampleSubcategory subcategory)
2626
switch (subcategory)
2727
{
2828
case ToolkitSampleSubcategory.None: imagePath = "ms-appx:///Assets/ControlIcons/Control.png"; break;
29-
case ToolkitSampleSubcategory.Layout: imagePath = "ms-appx:///Assets/ControlIcons/Layout.png"; break;
3029
case ToolkitSampleSubcategory.Input: imagePath = "ms-appx:///Assets/ControlIcons/Input.png"; break;
30+
case ToolkitSampleSubcategory.Layout: imagePath = "ms-appx:///Assets/ControlIcons/Layout.png"; break;
31+
case ToolkitSampleSubcategory.Media: imagePath = "ms-appx:///Assets/ControlIcons/Control.png"; break;
3132
case ToolkitSampleSubcategory.StatusAndInfo: imagePath = "ms-appx:///Assets/ControlIcons/Status.png"; break;
3233
}
3334
return imagePath;

common/CommunityToolkit.Labs.Shared/Pages/GettingStartedPage.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
Fill="{ThemeResource TextFillColorPrimaryBrush}" />
9898
<TextBlock Opacity="0.8"
9999
Style="{StaticResource BodyTextBlockStyle}"
100-
Text="A new way to contribute.." />
100+
Text="A new way to build, contribute, experiment, and collaborate." />
101101

102102
<Button Margin="0,24,0,0">
103103
<Button.Content>

common/CommunityToolkit.Labs.Shared/Renderers/Markdown/MarkdownTextBlock.cs

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,96 @@
1616
#endif
1717
#endif
1818

19+
#if __WASM__
20+
using Markdig;
21+
using Uno.Foundation.Interop;
22+
using Uno.UI.Runtime.WebAssembly;
23+
#endif
24+
1925
namespace CommunityToolkit.Labs.Shared.Renderers;
2026

2127
/// <summary>
2228
/// Provide an abstraction around the Toolkit MarkdownTextBlock for both UWP and WinUI 3 in the same namespace (until 8.0) as well as a polyfill for WebAssembly/WASM.
2329
/// </summary>
30+
#if __WASM__
31+
[HtmlElement("div")]
32+
public partial class MarkdownTextBlock : TextBlock
33+
{
34+
public MarkdownTextBlock()
35+
{
36+
Loaded += this.MarkdownTextBlock_Loaded;
37+
}
38+
39+
protected override void OnTextChanged(string oldValue, string newValue)
40+
{
41+
if (IsLoaded)
42+
{
43+
UpdateText(newValue);
44+
}
45+
}
46+
47+
private void MarkdownTextBlock_Loaded(object sender, RoutedEventArgs e)
48+
{
49+
this.RegisterHtmlEventHandler("resize", HtmlElementResized);
50+
51+
UpdateText(Text);
52+
}
53+
54+
#nullable enable
55+
private void HtmlElementResized(object? sender, EventArgs e)
56+
{
57+
this.InvalidateMeasure();
58+
}
59+
60+
private void UpdateText(string markdown)
61+
{
62+
// TODO: Check color hasn't changed since last time.
63+
var color = (Foreground as SolidColorBrush)?.Color;
64+
if (color != null)
65+
{
66+
this.SetCssStyle(("color", $"#{color!.ToString()!.Substring(3)}"), ("font-family", "Segoe UI"));
67+
}
68+
else
69+
{
70+
this.SetCssStyle("fontFamily", "Segoe UI");
71+
}
72+
73+
this.SetCssClass("fluent-hyperlink-style");
74+
75+
this.SetHtmlContent(Markdown.ToHtml(markdown));
76+
77+
this.InvalidateMeasure();
78+
}
79+
80+
protected override Size MeasureOverride(Size availableSize)
81+
{
82+
var size = this.MeasureHtmlView(availableSize, true);
83+
84+
return size;
85+
}
86+
87+
//// Polyfill dummy for event callback
88+
#pragma warning disable CS0067 // Unused on purpose for polyfill
89+
public event EventHandler<LinkClickedEventArgs>? LinkClicked;
90+
#pragma warning restore CS0067 // Unused on purpose for polyfill
91+
}
92+
#else
2493
public partial class MarkdownTextBlock : ToolkitMTB
2594
{
26-
#if HAS_UNO
95+
#if !HAS_UNO
96+
public MarkdownTextBlock()
97+
{
98+
// Note: TODO: We can't use win:IsTextSelectionEnabled in XAML, for some reason getting a UWP compiler issue...? Maybe confused by inheritance?
99+
IsTextSelectionEnabled = true;
100+
}
101+
#else
27102
//// Polyfill dummy for event callback
28103
#pragma warning disable CS0067 // Unused on purpose for polyfill
29104
public event EventHandler<LinkClickedEventArgs>? LinkClicked;
30105
#pragma warning restore CS0067 // Unused on purpose for polyfill
31-
#endif
106+
#endif
32107
}
108+
#endif
33109

34110
#if HAS_UNO
35111
//// Polyfill dummy for event callback

0 commit comments

Comments
 (0)