Add concept of "attributes" to UITree. So far the values are only allowed to be strings, but this will have to change later.

This commit is contained in:
Steve Sanderson 2018-01-05 17:28:26 +00:00
parent f0a78d13bf
commit 0db6f5cc5d
4 changed files with 116 additions and 0 deletions

View File

@ -15,6 +15,7 @@ namespace Microsoft.Blazor.UITree
private UITreeNode[] _entries = new UITreeNode[100];
private int _entriesInUse = 0;
private Stack<int> _openElementIndices = new Stack<int>();
private UITreeNodeType? _lastNonAttributeNodeType;
/// <summary>
/// Appends a node representing an element, i.e., a container for other nodes.
@ -46,6 +47,24 @@ namespace Microsoft.Blazor.UITree
public void AddText(string textContent)
=> Append(UITreeNode.Text(textContent));
/// <summary>
/// Appends a node representing an attribute. The attribute is associated
/// with the most recently added element.
/// </summary>
/// <param name="name">The name of the attribute.</param>
/// <param name="value">The value of the attribute.</param>
public void AddAttribute(string name, string value)
{
if (_lastNonAttributeNodeType == UITreeNodeType.Element)
{
Append(UITreeNode.Attribute(name, value));
}
else
{
throw new InvalidOperationException($"Attributes may only be added immediately after nodes of type {UITreeNodeType.Element}");
}
}
/// <summary>
/// Clears the builder.
/// </summary>
@ -61,6 +80,7 @@ namespace Microsoft.Blazor.UITree
_entriesInUse = 0;
_openElementIndices.Clear();
_lastNonAttributeNodeType = null;
}
/// <summary>
@ -79,6 +99,12 @@ namespace Microsoft.Blazor.UITree
}
_entries[_entriesInUse++] = node;
var nodeType = node.NodeType;
if (nodeType != UITreeNodeType.Attribute)
{
_lastNonAttributeNodeType = node.NodeType;
}
}
}
}

View File

@ -3,6 +3,10 @@
namespace Microsoft.Blazor.UITree
{
// TODO: Consider coalescing properties of compatible types that don't need to be
// used simultaneously. For example, 'ElementName' and 'AttributeName' could be replaced
// by a single 'Name' property.
/// <summary>
/// Represents an entry in a tree of user interface (UI) items.
/// </summary>
@ -32,6 +36,18 @@ namespace Microsoft.Blazor.UITree
/// </summary>
public string TextContent { get; private set; }
/// <summary>
/// If the <see cref="NodeType"/> property equals <see cref="UITreeNodeType.Attribute"/>,
/// gets the attribute name. Otherwise, the value is <see langword="null"/>.
/// </summary>
public string AttributeName { get; private set; }
/// <summary>
/// If the <see cref="NodeType"/> property equals <see cref="UITreeNodeType.Attribute"/>,
/// gets the attribute value. Otherwise, the value is <see langword="null"/>.
/// </summary>
public string AttributeValue { get; private set; }
internal static UITreeNode Element(string elementName) => new UITreeNode
{
NodeType = UITreeNodeType.Element,
@ -44,6 +60,13 @@ namespace Microsoft.Blazor.UITree
TextContent = textContent,
};
internal static UITreeNode Attribute(string name, string value) => new UITreeNode
{
NodeType = UITreeNodeType.Attribute,
AttributeName = name,
AttributeValue = value
};
internal void CloseElement(int descendantsEndIndex)
{
ElementDescendantsEndIndex = descendantsEndIndex;

View File

@ -17,5 +17,10 @@ namespace Microsoft.Blazor.UITree
/// Represents text content.
/// </summary>
Text = 2,
/// <summary>
/// Represents a key-value pair associated with another <see cref="UITreeNode"/>.
/// </summary>
Attribute = 3,
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Blazor.UITree;
using System;
using System.Linq;
using Xunit;
@ -130,6 +131,60 @@ namespace Microsoft.Blazor.Test
node => AssertText(node, "standalone text 2"));
}
[Fact]
public void CanAddAttributes()
{
// Arrange
var builder = new UITreeBuilder();
// Act
builder.OpenElement("myelement"); // 0: <myelement
builder.AddAttribute("attribute1", "value 1"); // 1: attribute1="value 1"
builder.AddAttribute("attribute2", "value 2"); // 2: attribute2="value 2">
builder.OpenElement("child"); // 3: <child
builder.AddAttribute("attribute1", "child value"); // 4: attribute1="child value">
builder.AddText("some text"); // 5: some text
builder.CloseElement(); // </child>
builder.CloseElement(); // </myelement>
// Assert
Assert.Collection(builder.GetNodes(),
node => AssertElement(node, "myelement", 5),
node => AssertAttribute(node, "attribute1", "value 1"),
node => AssertAttribute(node, "attribute2", "value 2"),
node => AssertElement(node, "child", 5),
node => AssertAttribute(node, "attribute1", "child value"),
node => AssertText(node, "some text"));
}
[Fact]
public void CannotAddAttributesAtRoot()
{
// Arrange
var builder = new UITreeBuilder();
// Act/Assert
Assert.Throws<InvalidOperationException>(() =>
{
builder.AddAttribute("name", "value");
});
}
[Fact]
public void CannotAddAttributesToText()
{
// Arrange
var builder = new UITreeBuilder();
// Act/Assert
Assert.Throws<InvalidOperationException>(() =>
{
builder.OpenElement("some element");
builder.AddText("hello");
builder.AddAttribute("name", "value");
});
}
[Fact]
public void CanClear()
{
@ -160,5 +215,12 @@ namespace Microsoft.Blazor.Test
Assert.Equal(elementName, node.ElementName);
Assert.Equal(descendantsEndIndex, node.ElementDescendantsEndIndex);
}
void AssertAttribute(UITreeNode node, string attributeName, string attributeValue)
{
Assert.Equal(UITreeNodeType.Attribute, node.NodeType);
Assert.Equal(attributeName, node.AttributeName);
Assert.Equal(attributeValue, node.AttributeValue);
}
}
}