Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit b077787

Browse files
author
N. Taylor Mullen
committed
Add Label TagHelper.
- Validated label TagHelper functionality. #1249
1 parent 8edcc0c commit b077787

File tree

2 files changed

+254
-0
lines changed

2 files changed

+254
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNet.Mvc.Rendering;
5+
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
6+
using Microsoft.AspNet.Razor.TagHelpers;
7+
8+
namespace Microsoft.AspNet.Mvc.TagHelpers
9+
{
10+
/// <summary>
11+
/// <see cref="ITagHelper"/> implementation targeting &lt;label&gt; elements with <c>for</c> attributes.
12+
/// </summary>
13+
[ContentBehavior(ContentBehavior.Modify)]
14+
public class LabelTagHelper : TagHelper
15+
{
16+
[Activate]
17+
protected internal ViewContext ViewContext { get; set; }
18+
19+
[Activate]
20+
protected internal IHtmlGenerator Generator { get; set; }
21+
22+
/// <summary>
23+
/// An expression to be evaluated against the current model.
24+
/// </summary>
25+
public ModelExpression For { get; set; }
26+
27+
/// <inheritdoc />
28+
public override void Process(TagHelperContext context, TagHelperOutput output)
29+
{
30+
if (For != null)
31+
{
32+
var tagBuilder = Generator.GenerateLabel(ViewContext,
33+
For.Metadata,
34+
For.Name,
35+
labelText: null,
36+
htmlAttributes: null);
37+
38+
if (tagBuilder != null)
39+
{
40+
output.MergeAttributes(tagBuilder);
41+
42+
// We check for whitespace to detect scenarios such as:
43+
// <label for="Name">
44+
// </label>
45+
if (string.IsNullOrWhiteSpace(output.Content))
46+
{
47+
output.Content = tagBuilder.InnerHtml;
48+
}
49+
50+
output.TagName = tagBuilder.TagName;
51+
}
52+
}
53+
}
54+
}
55+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNet.Mvc.ModelBinding;
8+
using Microsoft.AspNet.Mvc.Razor;
9+
using Microsoft.AspNet.Mvc.Rendering;
10+
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
11+
using Xunit;
12+
13+
namespace Microsoft.AspNet.Mvc.TagHelpers
14+
{
15+
public class LabelTagHelperTest
16+
{
17+
// Model (List<Model> or Model instance), container type (Model or NestModel), model accessor,
18+
// property path, TagHelperOutput.Content values.
19+
public static TheoryData<object, Type, Func<object>, string, TagHelperOutputContent> TestDataSet
20+
{
21+
get
22+
{
23+
var modelWithNull = new Model
24+
{
25+
NestedModel = new NestedModel
26+
{
27+
Text = null,
28+
},
29+
Text = null,
30+
};
31+
var modelWithText = new Model
32+
{
33+
NestedModel = new NestedModel
34+
{
35+
Text = "inner text",
36+
},
37+
Text = "outer text",
38+
};
39+
var models = new List<Model>
40+
{
41+
modelWithNull,
42+
modelWithText,
43+
};
44+
45+
return new TheoryData<object, Type, Func<object>, string, TagHelperOutputContent>
46+
{
47+
{ null, typeof(Model), () => null, "Text",
48+
new TagHelperOutputContent(Environment.NewLine, "Text") },
49+
50+
{ modelWithNull, typeof(Model), () => modelWithNull.Text, "Text",
51+
new TagHelperOutputContent(Environment.NewLine, "Text") },
52+
{ modelWithText, typeof(Model), () => modelWithText.Text, "Text",
53+
new TagHelperOutputContent(Environment.NewLine, "Text") },
54+
{ modelWithText, typeof(Model), () => modelWithNull.Text, "Text",
55+
new TagHelperOutputContent("Hello World", "Hello World") },
56+
{ modelWithText, typeof(Model), () => modelWithText.Text, "Text",
57+
new TagHelperOutputContent("Hello World", "Hello World") },
58+
59+
{ modelWithNull, typeof(NestedModel), () => modelWithNull.NestedModel.Text, "NestedModel.Text",
60+
new TagHelperOutputContent(Environment.NewLine, "Text") },
61+
{ modelWithText, typeof(NestedModel), () => modelWithText.NestedModel.Text, "NestedModel.Text",
62+
new TagHelperOutputContent(Environment.NewLine, "Text") },
63+
{ modelWithNull, typeof(NestedModel), () => modelWithNull.NestedModel.Text, "NestedModel.Text",
64+
new TagHelperOutputContent("Hello World", "Hello World") },
65+
{ modelWithText, typeof(NestedModel), () => modelWithText.NestedModel.Text, "NestedModel.Text",
66+
new TagHelperOutputContent("Hello World", "Hello World") },
67+
68+
// Note: Tests cases below here will not work in practice due to current limitations on indexing
69+
// into ModelExpressions. Will be fixed in https://github.com/aspnet/Mvc/issues/1345.
70+
{ models, typeof(Model), () => models[0].Text, "[0].Text",
71+
new TagHelperOutputContent(Environment.NewLine, "Text") },
72+
{ models, typeof(Model), () => models[1].Text, "[1].Text",
73+
new TagHelperOutputContent(Environment.NewLine, "Text") },
74+
{ models, typeof(Model), () => models[0].Text, "[0].Text",
75+
new TagHelperOutputContent("Hello World", "Hello World") },
76+
{ models, typeof(Model), () => models[1].Text, "[1].Text",
77+
new TagHelperOutputContent("Hello World", "Hello World") },
78+
79+
{ models, typeof(NestedModel), () => models[0].NestedModel.Text, "[0].NestedModel.Text",
80+
new TagHelperOutputContent(Environment.NewLine, "Text") },
81+
{ models, typeof(NestedModel), () => models[1].NestedModel.Text, "[1].NestedModel.Text",
82+
new TagHelperOutputContent(Environment.NewLine, "Text") },
83+
{ models, typeof(NestedModel), () => models[0].NestedModel.Text, "[0].NestedModel.Text",
84+
new TagHelperOutputContent("Hello World", "Hello World") },
85+
{ models, typeof(NestedModel), () => models[1].NestedModel.Text, "[1].NestedModel.Text",
86+
new TagHelperOutputContent("Hello World", "Hello World") },
87+
};
88+
}
89+
}
90+
91+
[Theory]
92+
[MemberData(nameof(TestDataSet))]
93+
public async Task ProcessAsync_GeneratesExpectedOutput(
94+
object model,
95+
Type containerType,
96+
Func<object> modelAccessor,
97+
string propertyPath,
98+
TagHelperOutputContent tagHelperOutputContent)
99+
{
100+
// Arrange
101+
var expectedAttributes = new Dictionary<string, string>
102+
{
103+
{ "class", "form-control" },
104+
{ "for", propertyPath }
105+
};
106+
var metadataProvider = new DataAnnotationsModelMetadataProvider();
107+
108+
// Property name is either nameof(Model.Text) or nameof(NestedModel.Text).
109+
var metadata = metadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName: "Text");
110+
var modelExpression = new ModelExpression(propertyPath, metadata);
111+
var tagHelper = new LabelTagHelper
112+
{
113+
For = modelExpression,
114+
};
115+
116+
var tagHelperContext = new TagHelperContext(allAttributes: new Dictionary<string, object>());
117+
var htmlAttributes = new Dictionary<string, string>
118+
{
119+
{ "class", "form-control" },
120+
};
121+
var output = new TagHelperOutput("A random tag name", htmlAttributes, tagHelperOutputContent.OriginalContent);
122+
var expectedTagName = "label";
123+
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
124+
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
125+
tagHelper.ViewContext = viewContext;
126+
tagHelper.Generator = htmlGenerator;
127+
128+
// Act
129+
await tagHelper.ProcessAsync(tagHelperContext, output);
130+
131+
// Assert
132+
Assert.Equal(expectedAttributes, output.Attributes);
133+
Assert.Equal(tagHelperOutputContent.ExpectedContent, output.Content);
134+
Assert.False(output.SelfClosing);
135+
Assert.Equal(expectedTagName, output.TagName);
136+
}
137+
138+
[Fact]
139+
public async Task TagHelper_LeavesOutputUnchanged_IfForNotBound2()
140+
{
141+
// Arrange
142+
var expectedAttributes = new Dictionary<string, string>
143+
{
144+
{ "class", "form-control" },
145+
};
146+
var expectedContent = "original content";
147+
var expectedTagName = "original tag name";
148+
149+
var metadataProvider = new DataAnnotationsModelMetadataProvider();
150+
var metadata = metadataProvider.GetMetadataForProperty(
151+
modelAccessor: () => null,
152+
containerType: typeof(Model),
153+
propertyName: nameof(Model.Text));
154+
var modelExpression = new ModelExpression(nameof(Model.Text), metadata);
155+
var tagHelper = new LabelTagHelper();
156+
157+
var tagHelperContext = new TagHelperContext(allAttributes: new Dictionary<string, object>());
158+
var output = new TagHelperOutput(expectedTagName, expectedAttributes, expectedContent);
159+
160+
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
161+
Model model = null;
162+
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
163+
var activator = new DefaultTagHelperActivator();
164+
activator.Activate(tagHelper, viewContext);
165+
166+
// Act
167+
await tagHelper.ProcessAsync(tagHelperContext, output);
168+
169+
// Assert
170+
Assert.Equal(expectedAttributes, output.Attributes);
171+
Assert.Equal(expectedContent, output.Content);
172+
Assert.Equal(expectedTagName, output.TagName);
173+
}
174+
175+
public class TagHelperOutputContent
176+
{
177+
public TagHelperOutputContent(string outputContent, string expectedContent)
178+
{
179+
OriginalContent = outputContent;
180+
ExpectedContent = expectedContent;
181+
}
182+
183+
public string OriginalContent { get; set; }
184+
public string ExpectedContent { get; set; }
185+
}
186+
187+
private class Model
188+
{
189+
public string Text { get; set; }
190+
191+
public NestedModel NestedModel { get; set; }
192+
}
193+
194+
private class NestedModel
195+
{
196+
public string Text { get; set; }
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)