Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static class ComponentScaffold
["FormRow"] = ApplyFormRow,
["FormCell"] = ApplyFormCell,
["FormControl"] = ApplyFormControl,
["FormColumn"] = ApplyFormColumn,
};

public static IReadOnlyCollection<string> SupportedComponentTypes => Appliers.Keys;
Expand Down Expand Up @@ -102,6 +103,18 @@ private static ScaffoldResult ApplyFormControl(ComponentScaffoldRequest request)
ColumnSpan = OptionalKnown(request.Parameters, "col-span"),
});

// Files: column (required). Parameters: entity, form-type, form-id, tab targeting, all optional.
private static ScaffoldResult ApplyFormColumn(ComponentScaffoldRequest request) =>
FormColumnScaffold.Apply(new FormColumnScaffoldRequest
{
SolutionRootPath = request.SolutionRootPath,
EntitySchemaName = OptionalKnown(request.Parameters, "entity"),
FormType = OptionalKnown(request.Parameters, "form-type"),
FormId = OptionalId(request.Parameters, "form-id"),
Placement = PlacementFrom(request),
ColumnFilePath = RequiredFile(request, "column"),
});

private static FormPlacement PlacementFrom(ComponentScaffoldRequest request) => new()
{
TabId = OptionalId(request.Parameters, "tab-id"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Xml;

namespace TALXIS.Platform.Metadata.Serialization.Xml.Scaffolding;

/// <summary>
/// Appends the rendered column(s) into the columns container of the resolved tab
/// (or tab footer), creating the container when the tab has none.
/// </summary>
public static class FormColumnScaffold
{
public static ScaffoldResult Apply(FormColumnScaffoldRequest request)
{
if (!File.Exists(request.ColumnFilePath))
throw new FileNotFoundException($"Column fragment file not found: {request.ColumnFilePath}");

var formFilePath = FormXmlLocator.Locate(request.SolutionRootPath, request.EntitySchemaName, request.FormType, request.FormId);
var formDoc = ScaffoldXmlFile.Load(formFilePath);
var container = FormPlacementResolver.ResolveTab(formDoc, request.Placement);

// Resolves the footer document-wide using //tabfooter.
if (request.Placement.SetToTabFooter)
{
var footers = container.SelectNodes("//tabfooter")!;
if (footers.Count == 0) throw new InvalidOperationException("Target tab footer not found.");
container = footers[footers.Count - 1]!;
}

var columnsNode = container.SelectSingleNode("./columns");
if (columnsNode == null)
{
columnsNode = formDoc.CreateElement("columns");
container.AppendChild(columnsNode);
}

var fragmentDoc = new XmlDocument();
fragmentDoc.LoadXml("<columns>" + File.ReadAllText(request.ColumnFilePath) + "</columns>");
foreach (XmlNode column in fragmentDoc.DocumentElement!.ChildNodes)
{
columnsNode.AppendChild(formDoc.ImportNode(column, deep: true));
}

ScaffoldXmlFile.Save(formDoc, formFilePath);
return new ScaffoldResult();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace TALXIS.Platform.Metadata.Serialization.Xml.Scaffolding;

/// <summary>
/// Input for <see cref="FormColumnScaffold"/>: the form to patch, the tab (or
/// footer) targeting and the rendered column fragment to append.
/// </summary>
public sealed class FormColumnScaffoldRequest
{
/// <summary>
/// Folder containing the unpacked solution files (Other/, Entities/, OptionSets/).
/// </summary>
public string SolutionRootPath { get; set; } = "";

/// <summary>
/// Schema name of the entity that owns the form; null lets the locator search.
/// </summary>
public string? EntitySchemaName { get; set; }

/// <summary>
/// Form type ("main", "quick", "dialog"); null lets the locator search.
/// </summary>
public string? FormType { get; set; }

/// <summary>
/// GUID of the form to patch, without braces; null lets the locator search.
/// </summary>
public string? FormId { get; set; }

/// <summary>
/// Target tab inside the form (only tab and footer targeting applies).
/// </summary>
public FormPlacement Placement { get; set; } = new();

/// <summary>
/// Rendered column fragment file whose column elements are appended.
/// </summary>
public string ColumnFilePath { get; set; } = "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ internal static class FormPlacementResolver
{
public static XmlNode ResolveTargetSection(XmlDocument formDoc, FormPlacement placement)
{
var tab = ResolveByIdOrIndex(formDoc, "//tab", placement.TabId, placement.TabIndex)
?? throw new InvalidOperationException("Target tab not found.");
var tab = ResolveTab(formDoc, placement);

if (placement.SetToTabFooter)
{
Expand All @@ -26,6 +25,10 @@ public static XmlNode ResolveTargetSection(XmlDocument formDoc, FormPlacement pl
?? throw new InvalidOperationException("Target section not found in the selected column.");
}

public static XmlNode ResolveTab(XmlDocument formDoc, FormPlacement placement) =>
ResolveByIdOrIndex(formDoc, "//tab", placement.TabId, placement.TabIndex)
?? throw new InvalidOperationException("Target tab not found.");

public static XmlNode ResolveTargetRow(XmlNode section, string? rowIndex) =>
ResolveByIndexOrLast(section, "./rows/row", rowIndex)
?? throw new InvalidOperationException("Target row not found in the selected section.");
Expand Down
113 changes: 113 additions & 0 deletions tests/TALXIS.Platform.Metadata.Tests/FormColumnScaffoldTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System.Xml.Linq;
using TALXIS.Platform.Metadata.Serialization.Xml.Scaffolding;

namespace TALXIS.Platform.Metadata.Tests;

public class FormColumnScaffoldTests : IDisposable
{
private const string EntityName = "udpp_warehouseitem";
private const string FormId = "a1b2c3d4-e5f6-4a1b-8c2d-000000000001";

private readonly string _root = Directory.CreateTempSubdirectory("metadata-form-column-scaffold").FullName;
private readonly string _formFilePath;
private readonly string _columnFilePath;

public FormColumnScaffoldTests()
{
var formDirectory = Path.Combine(_root, "Entities", EntityName, "FormXml", "main");
Directory.CreateDirectory(formDirectory);
_formFilePath = Path.Combine(formDirectory, $"{{{FormId}}}.xml");
File.WriteAllText(_formFilePath, """
<form>
<tabs>
<tab id="{00000000-0000-0000-0000-0000000000a1}">
<tabfooter />
<columns>
<column width="50%" />
</columns>
</tab>
<tab id="{00000000-0000-0000-0000-0000000000a2}" />
</tabs>
</form>
""");
_columnFilePath = Path.Combine(_root, "column.xml");
File.WriteAllText(_columnFilePath, """
<column width="100%">
<sections>

</sections>
</column>
""");
}

public void Dispose() => Directory.Delete(_root, recursive: true);

private FormColumnScaffoldRequest Request() => new()
{
SolutionRootPath = _root,
EntitySchemaName = EntityName,
FormType = "main",
FormId = FormId,
ColumnFilePath = _columnFilePath,
};

[Fact]
public void Apply_LastTabWithoutColumns_CreatesContainerAndAppends()
{
var result = FormColumnScaffold.Apply(Request());

Assert.Empty(result.Warnings);
var doc = XDocument.Load(_formFilePath);
var lastTab = doc.Descendants("tab").Single(t => t.Attribute("id")?.Value == "{00000000-0000-0000-0000-0000000000a2}");
Assert.Equal("100%", lastTab.Element("columns")!.Elements("column").Single().Attribute("width")?.Value);
}

[Fact]
public void Apply_TargetedTab_AppendsToExistingColumns()
{
var request = Request();
request.Placement.TabId = "00000000-0000-0000-0000-0000000000a1";
FormColumnScaffold.Apply(request);

var doc = XDocument.Load(_formFilePath);
var tab = doc.Descendants("tab").Single(t => t.Attribute("id")?.Value == "{00000000-0000-0000-0000-0000000000a1}");
Assert.Equal(2, tab.Element("columns")!.Elements("column").Count());
}

[Fact]
public void Apply_TabFooter_UsesDocumentWideFooter()
{
var request = Request();
request.Placement.TabIndex = "2";
request.Placement.SetToTabFooter = true;
FormColumnScaffold.Apply(request);

var doc = XDocument.Load(_formFilePath);
Assert.Single(doc.Descendants("tabfooter").Single().Element("columns")!.Elements("column"));
}

[Fact]
public void Apply_ViaComponentScaffold_DispatchesFormColumn()
{
var result = ComponentScaffold.Apply(new ComponentScaffoldRequest
{
ComponentType = "FormColumn",
SolutionRootPath = _root,
Files = new Dictionary<string, string> { ["column"] = _columnFilePath },
Parameters = new Dictionary<string, string>
{
["entity"] = EntityName,
["form-type"] = "main",
["form-id"] = FormId,
["tab-id"] = "unknown",
["tab-index"] = "1",
["set-to-tab-footer"] = "False",
},
});

Assert.Empty(result.Warnings);
var doc = XDocument.Load(_formFilePath);
var tab = doc.Descendants("tab").Single(t => t.Attribute("id")?.Value == "{00000000-0000-0000-0000-0000000000a1}");
Assert.Equal(2, tab.Element("columns")!.Elements("column").Count());
}
}