Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into issue/LMBQ-334
Browse files Browse the repository at this point in the history
  • Loading branch information
barthamark committed May 27, 2024
2 parents 8eeab66 + 4b603e6 commit 765f3fa
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 42 deletions.
6 changes: 3 additions & 3 deletions Lombiq.BaseTheme.Samples/Lombiq.BaseTheme.Samples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Admin" Version="1.8.2" />
<PackageReference Include="OrchardCore.Theme.Targets" Version="1.8.2" />
<PackageReference Include="OrchardCore.Admin" Version="1.8.0" />
<PackageReference Include="OrchardCore.Theme.Targets" Version="1.8.0" />
</ItemGroup>

<ItemGroup>
Expand All @@ -39,7 +39,7 @@
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' == 'true'">
<PackageReference Include="Lombiq.NodeJs.Extensions" Version="1.3.3-alpha.0.osoe-751" />
<PackageReference Include="Lombiq.NodeJs.Extensions" Version="2.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static async Task TestAddingMenuItemToBlogMainMenuAsync(this UITestContex
By byIcon = null)
{
await context.GoToAdminRelativeUrlAsync("/Lombiq.BaseTheme/Admin/Index");
await context.SetCheckboxValueAsync(By.Id("HideMenu"), isChecked: true);
await context.SetCheckboxValueAsync(By.Id("HideMenu"));

await context.ClickReliablyOnAsync(By.XPath("//div[contains(@class, 'thumb-container')]"));
await context.ClickReliablyOnAsync(By.CssSelector("#Editor .delete-button").OfAnyVisibility());
Expand Down
2 changes: 1 addition & 1 deletion Lombiq.BaseTheme.Tests.UI/Lombiq.BaseTheme.Tests.UI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' == 'true'">
<PackageReference Include="Lombiq.Tests.UI" Version="9.0.0" />
<PackageReference Include="Lombiq.Tests.UI" Version="10.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
33 changes: 16 additions & 17 deletions Lombiq.BaseTheme/Lombiq.BaseTheme.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
<PackageLicenseFile>License.md</PackageLicenseFile>
</PropertyGroup>

<PropertyGroup>
<PropertyGroup Condition="'$(NuGetBuild)' != 'true'">
<SourceGeneratorLocation>$(SolutionDir)src\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.SourceGenerators\bin\Debug\netstandard2.0\Lombiq.HelpfulLibraries.SourceGenerators.dll</SourceGeneratorLocation>
<SourceGeneratorLocation Condition=" '$(Configuration)' != 'Debug' ">
$(SolutionDir)src\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.SourceGenerators\bin\Release\netstandard2.0\Lombiq.HelpfulLibraries.SourceGenerators.dll
</SourceGeneratorLocation>
</PropertyGroup>
</PropertyGroup>

<Target Name="CustomBeforeCompile" BeforeTargets="Compile">
<Target Name="CustomBeforeCompile" BeforeTargets="Compile" Condition="'$(NuGetBuild)' != 'true'">
<MSBuild Condition="!Exists('$(SourceGeneratorLocation)')" Projects="..\..\..\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.SourceGenerators\Lombiq.HelpfulLibraries.SourceGenerators.csproj" />
</Target>

Expand All @@ -43,29 +43,28 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Theme.Targets" Version="1.8.2" />
<PackageReference Include="OrchardCore.Menu" Version="1.8.2" />
<PackageReference Include="OrchardCore.Media" Version="1.8.2" />
<PackageReference Include="OrchardCore.ContentManagement" Version="1.8.2" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.8.2" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="1.8.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.Attributes\Lombiq.HelpfulLibraries.Attributes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
<ProjectReference Include="..\..\..\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.SourceGenerators\Lombiq.HelpfulLibraries.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<PackageReference Include="OrchardCore.Theme.Targets" Version="1.8.0" />
<PackageReference Include="OrchardCore.Menu" Version="1.8.0" />
<PackageReference Include="OrchardCore.Media" Version="1.8.0" />
<PackageReference Include="OrchardCore.ContentManagement" Version="1.8.0" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.8.0" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="1.8.0" />
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' != 'true'">
<ProjectReference Include="..\..\..\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.csproj" />
<ProjectReference Include="..\..\..\Modules\Lombiq.HelpfulExtensions\Lombiq.HelpfulExtensions\Lombiq.HelpfulExtensions.csproj" />
<ProjectReference Include="..\..\..\Utilities\Lombiq.NodeJs.Extensions\Lombiq.NodeJs.Extensions\Lombiq.NodeJs.Extensions.csproj" />
<ProjectReference Include="..\..\..\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.Attributes\Lombiq.HelpfulLibraries.Attributes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
<ProjectReference Include="..\..\..\Libraries\Lombiq.HelpfulLibraries\Lombiq.HelpfulLibraries.SourceGenerators\Lombiq.HelpfulLibraries.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup Condition="'$(NuGetBuild)' == 'true'">
<PackageReference Include="Lombiq.HelpfulLibraries" Version="9.0.0" />
<PackageReference Include="Lombiq.HelpfulExtensions" Version="8.0.0" />
<PackageReference Include="Lombiq.NodeJs.Extensions" Version="1.3.3-alpha.0.osoe-751" />
<PackageReference Include="Lombiq.HelpfulLibraries" Version="10.0.0" />
<PackageReference Include="Lombiq.HelpfulExtensions" Version="8.0.1" />
<PackageReference Include="Lombiq.NodeJs.Extensions" Version="2.1.0" />
<PackageReference Include="Lombiq.HelpfulLibraries.SourceGenerators" Version="10.0.0" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<PackageReference Include="Lombiq.HelpfulLibraries.Attributes" Version="10.0.0" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />

<Content Include="Assets/Styles/**/*.*">
<IncludeInPackage>true</IncludeInPackage>
Expand Down
54 changes: 47 additions & 7 deletions Lombiq.BaseTheme/Models/ZoneDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
using Lombiq.BaseTheme.Services;
using Lombiq.HelpfulLibraries.Common.Utilities;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Localization;
using OrchardCore.DisplayManagement.Razor;
using OrchardCore.DisplayManagement.Zones;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static AngleSharp.Dom.TagNames;

namespace Lombiq.BaseTheme.Models;

Expand All @@ -16,18 +18,30 @@ public class ZoneDescriptor
public const string LayoutElementClassName = "layoutElement";
public const string LeafClassName = LayoutElementClassName + "_leaf";

// Elements that may be zones and are landmarks, see https://html-validate.org/rules/unique-landmark.html.
private static readonly string[] _landmarkElements = [Aside, Footer, Form, Header, Main, Nav, Section];

public string ZoneName { get; set; }
public string ElementName { get; set; }
public bool WrapBody { get; set; }
public LocalizedHtmlString AriaLabel { get; set; }
public IDictionary<string, string> Attributes { get; private set; }

public IEnumerable<ZoneDescriptor> ChildrenBefore { get; set; }
public IEnumerable<ZoneDescriptor> ChildrenAfter { get; set; }

public ZoneDescriptor(string zoneName = null, string elementName = null, bool wrapBody = false)
public ZoneDescriptor(
string zoneName,
string elementName = null,
bool wrapBody = false,
LocalizedHtmlString ariaLabel = null,
IReadOnlyDictionary<string, string> attributes = null)
{
ZoneName = zoneName;
ElementName = elementName;
WrapBody = wrapBody;
AriaLabel = ariaLabel;
Attributes = attributes?.ToDictionary() ?? [];
}

public async Task<IHtmlContent> DisplayZoneAsync<TModel>(
Expand All @@ -41,7 +55,7 @@ public ZoneDescriptor(string zoneName = null, string elementName = null, bool wr
return new HtmlString(string.Empty);
}

ElementName ??= "div";
ElementName ??= Div;

// The zone name should already be PascalCase.
var id = ZoneName.ToCamelCase();
Expand All @@ -56,6 +70,9 @@ public ZoneDescriptor(string zoneName = null, string elementName = null, bool wr
ChildrenBefore?.Any() != true || ChildrenAfter?.Any() != true ? LeafClassName : null);

var body = await page.DisplayAsync(zone);

var attributesFlattened = Attributes.Select(attribute => $"{attribute.Key}=\"{attribute.Value}\"").Join();

if (WrapBody)
{
// If there is no parent then "body" becomes the BEM element, otherwise there already is an element so
Expand All @@ -64,18 +81,29 @@ public ZoneDescriptor(string zoneName = null, string elementName = null, bool wr
? layoutClassName + "__body"
: layoutClassName + "Body";

// This improves accessibility by providing a main landmark, see:
// https://dequeuniversity.com/rules/axe/4.2/bypass?application=axeAPI
var elementName = ZoneName == ZoneNames.Content ? "main" : "div";
var bodyAttributes = $"class=\"{bodyWrapperClass} {LeafClassName}\" " + attributesFlattened;

var elementName = Div;

if (ZoneName == ZoneNames.Content)
{
// This improves accessibility by providing a main landmark, see:
// https://dequeuniversity.com/rules/axe/4.2/bypass?application=axeAPI
elementName = Main;

bodyAttributes += GetAriaLabelAttribute(elementName);
}

body = new HtmlContentBuilder()
.AppendHtml(StringHelper.CreateInvariant($"<{elementName} class=\"{bodyWrapperClass} {LeafClassName}\">"))
.AppendHtml(StringHelper.CreateInvariant($"<{elementName} {bodyAttributes}>"))
.AppendHtml(body)
.AppendHtml(StringHelper.CreateInvariant($"</{elementName}>"));
}

attributesFlattened += GetAriaLabelAttribute(ElementName);

return new HtmlContentBuilder()
.AppendHtml(StringHelper.CreateInvariant($"<{ElementName} id=\"{id}\" class=\"{classNames}\">"))
.AppendHtml(StringHelper.CreateInvariant($"<{ElementName} id=\"{id}\" class=\"{classNames}\" {attributesFlattened}>"))
.AppendHtml(await ConcatenateAsync(classHolder, page, ChildrenBefore, parent))
.AppendHtml(body)
.AppendHtml(await ConcatenateAsync(classHolder, page, ChildrenAfter, parent))
Expand All @@ -91,6 +119,18 @@ public ZoneDescriptor(string zoneName = null, string elementName = null, bool wr
? Task.FromResult<IHtmlContent>(new HtmlString(string.Empty))
: ConcatenateInnerAsync(classHolder, page, zoneDescriptors, ZoneName, parent);

private string GetAriaLabelAttribute(string elementName)
{
// Intentionally no CamelCase word-splitting the ZoneName by default, since that would involve regex for every
// single page view, for values that one only ever set once.
if (AriaLabel != null || _landmarkElements.Contains(elementName))
{
return $"aria-label=\"{(AriaLabel ?? new LocalizedHtmlString(ZoneName, ZoneName)).Value}\" ";
}

return string.Empty;
}

private static async Task<IHtmlContent> ConcatenateInnerAsync<TModel>(
ICssClassHolder classHolder,
RazorPage<TModel> page,
Expand Down
28 changes: 15 additions & 13 deletions Lombiq.BaseTheme/Views/Layout.cshtml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@using TagNames = AngleSharp.Dom.TagNames;

<!DOCTYPE html>

@inject ICssClassHolder CssClassHolder
Expand All @@ -22,32 +24,32 @@
page: this,
new[]
{
new ZoneDescriptor(Header, wrapBody: true, elementName: "header")
new ZoneDescriptor(Header, wrapBody: true, elementName: TagNames.Header, ariaLabel: T["Site"])
{
ChildrenBefore = new [] { new ZoneDescriptor("Banner", elementName: "section") },
ChildrenBefore = new [] { new ZoneDescriptor("Banner", elementName: TagNames.Section) },
ChildrenAfter = new []
{
new ZoneDescriptor(Navigation, elementName: "nav"),
new ZoneDescriptor(Navigation, elementName: TagNames.Nav, ariaLabel: T["Main"]),
},
},
new ZoneDescriptor(BeforeMain, elementName: "section"),
new ZoneDescriptor(Featured, elementName: "section"),
new ZoneDescriptor(Content, wrapBody: true, elementName: "section")
new ZoneDescriptor(BeforeMain, elementName: TagNames.Section, ariaLabel: T["Before Main"]),
new ZoneDescriptor(Featured, elementName: TagNames.Section),
new ZoneDescriptor(Content, wrapBody: true, elementName: TagNames.Section)
{
ChildrenBefore = new []
{
new ZoneDescriptor(AsideFirst, elementName: "aside"),
new ZoneDescriptor(Messages, elementName: "section"),
new ZoneDescriptor(BeforeContent, elementName: "section"),
new ZoneDescriptor(AsideFirst, elementName: TagNames.Aside, ariaLabel: T["Aside First"]),
new ZoneDescriptor(Messages, elementName: TagNames.Section),
new ZoneDescriptor(BeforeContent, elementName: TagNames.Section, ariaLabel: T["Before Content"]),
},
ChildrenAfter = new []
{
new ZoneDescriptor(AfterContent, elementName: "section"),
new ZoneDescriptor(AsideSecond, elementName: "aside"),
new ZoneDescriptor(AfterContent, elementName: TagNames.Section, ariaLabel: T["After Content"]),
new ZoneDescriptor(AsideSecond, elementName: TagNames.Aside, ariaLabel: T["Aside Second"]),
},
},
new ZoneDescriptor(AfterMain, elementName: "section"),
new ZoneDescriptor(Footer, "footer"),
new ZoneDescriptor(AfterMain, elementName: TagNames.Section, ariaLabel: T["After Main"]),
new ZoneDescriptor(Footer, elementName: TagNames.Footer, ariaLabel: T["Site"]),
})

<script>
Expand Down

0 comments on commit 765f3fa

Please sign in to comment.