Skip to content

Commit 714704f

Browse files
committed
Merge remote-tracking branch 'origin/issue/OSOE-925' into issue/OSOE-1208
2 parents 7b7c163 + 2277e5b commit 714704f

23 files changed

Lines changed: 445 additions & 43 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
obj/
55
bin/
66
artifacts/
7-
wwwroot/
7+
# Ignore generated vendor assets, keep custom wwwroot source files trackable.
8+
Lombiq.HelpfulExtensions/wwwroot/vendors/*
89
node_modules/
910
*.user
1011
.pnpm-debug.log
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Lombiq.Tests.UI.Extensions;
2+
using Lombiq.Tests.UI.Services;
3+
using OpenQA.Selenium;
4+
using Shouldly;
5+
using System.Threading.Tasks;
6+
7+
namespace Lombiq.HelpfulExtensions.Tests.UI.Extensions;
8+
9+
public static class LucideTestCaseUITestContextExtensions
10+
{
11+
/// <summary>
12+
/// Tests the Lombiq Helpful Extensions - Lucide feature.
13+
/// </summary>
14+
public static async Task TestLucideFeatureAsync(this UITestContext context)
15+
{
16+
const string iconName = "camera";
17+
18+
await context.SignInDirectlyAsync();
19+
20+
await context.EnableFeatureDirectlyAsync(FeatureIds.Lucide);
21+
await context.ExecuteRecipeDirectlyAsync("Lombiq.HelpfulExtensions.Tests.UI.Lucide.Tests");
22+
await context.CreateNewContentItemAsync("LucidePickerTest", onlyIfNotAlreadyThere: false);
23+
24+
await context.ClickReliablyOnAsync(By.CssSelector("[data-lucide-toggle]"));
25+
await context.ClickAndFillInWithRetriesAsync(By.CssSelector("[data-lucide-search]"), iconName);
26+
await context.ClickReliablyOnAsync(By.CssSelector($"[data-lucide-icon='{iconName}']"));
27+
28+
var selectedIcon = context.ExecuteScript(
29+
"return document.querySelector(arguments[0])?.dataset.lucideIcon ?? '';",
30+
$"[data-lucide-icon='{iconName}'].active") as string;
31+
selectedIcon.ShouldBe(iconName);
32+
context.Get(By.CssSelector($"[data-lucide-preview] [data-lucide='{iconName}']"));
33+
}
34+
}

Lombiq.HelpfulExtensions.Tests.UI/Lombiq.HelpfulExtensions.Tests.UI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
</ItemGroup>
2525

2626
<ItemGroup>
27-
<PackageReference Include="OrchardCore.ContentManagement.Abstractions" Version="3.0.0-preview-18960">
27+
<PackageReference Include="OrchardCore.ContentManagement.Abstractions" Version="3.0.0-preview-18975">
2828
<Aliases>OCCMA</Aliases>
2929
</PackageReference>
3030
</ItemGroup>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"name": "Lombiq.HelpfulExtensions.Tests.UI.Lucide.Tests",
3+
"displayName": "Lombiq Helpful Extensions - Lucide test recipe",
4+
"description": "Creates a simple content type for testing the Lucide icon picker editor.",
5+
"author": "Lombiq Technologies",
6+
"website": "https://github.com/Lombiq/Helpful-Extensions",
7+
"version": "1.0",
8+
"issetuprecipe": false,
9+
"categories": [],
10+
"tags": [],
11+
"steps": [
12+
{
13+
"name": "ContentDefinition",
14+
"ContentTypes": [
15+
{
16+
"Name": "LucidePickerTest",
17+
"DisplayName": "Lucide Picker Test",
18+
"Settings": {
19+
"ContentTypeSettings": {
20+
"Creatable": true,
21+
"Listable": true,
22+
"Draftable": true,
23+
"Versionable": true
24+
}
25+
},
26+
"ContentTypePartDefinitionRecords": [
27+
{
28+
"PartName": "LucidePickerTest",
29+
"Name": "LucidePickerTest",
30+
"Settings": {}
31+
}
32+
]
33+
}
34+
],
35+
"ContentParts": [
36+
{
37+
"Name": "LucidePickerTest",
38+
"Settings": {},
39+
"ContentPartFieldDefinitionRecords": [
40+
{
41+
"FieldName": "TextField",
42+
"Name": "Icon",
43+
"Settings": {
44+
"ContentPartFieldSettings": {
45+
"DisplayName": "Icon",
46+
"Editor": "LucideIconPicker"
47+
}
48+
}
49+
}
50+
]
51+
}
52+
]
53+
}
54+
]
55+
}

Lombiq.HelpfulExtensions/Constants/ResourceNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ namespace Lombiq.HelpfulExtensions.Constants;
33
public static class ResourceNames
44
{
55
public const string TargetBlank = nameof(TargetBlank);
6+
public const string Lucide = nameof(Lucide);
7+
public const string LucideIconPicker = nameof(LucideIconPicker);
68
}

Lombiq.HelpfulExtensions/Extensions/Flows/Drivers/AdditionalStylingPartDisplay.cs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,7 @@ public override IDisplayResult Edit(ContentItem model, BuildEditorContext contex
1717

1818
public override async Task<IDisplayResult> UpdateAsync(ContentItem model, UpdateEditorContext context)
1919
{
20-
var additionalStylingPart = model.As<AdditionalStylingPart>();
21-
22-
if (additionalStylingPart == null)
23-
{
24-
return null;
25-
}
20+
if (!model.Has<AdditionalStylingPart>()) return null;
2621

2722
await model.AlterAsync<AdditionalStylingPart>(model => context.Updater.TryUpdateModelAsync(model, Prefix));
2823

@@ -31,12 +26,8 @@ public override async Task<IDisplayResult> UpdateAsync(ContentItem model, Update
3126

3227
private static void PopulateViewModel(ContentItem model, AdditionalStylingPart viewModel)
3328
{
34-
var additionalStylingPart = model.As<AdditionalStylingPart>();
35-
36-
if (additionalStylingPart != null)
37-
{
38-
viewModel.CustomClasses = additionalStylingPart.CustomClasses;
39-
viewModel.RemoveGridExtensionClasses = additionalStylingPart.RemoveGridExtensionClasses;
40-
}
29+
if (!model.TryGet<AdditionalStylingPart>(out var additionalStylingPart)) return;
30+
viewModel.CustomClasses = additionalStylingPart.CustomClasses;
31+
viewModel.RemoveGridExtensionClasses = additionalStylingPart.RemoveGridExtensionClasses;
4132
}
4233
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using Lombiq.HelpfulLibraries.Attributes;
2+
using Microsoft.Extensions.Options;
3+
using OrchardCore.ResourceManagement;
4+
5+
namespace Lombiq.HelpfulExtensions.Extensions.Lucide;
6+
7+
[LibManVersions]
8+
public partial class LucideResourceManagementOptionsConfiguration : IConfigureOptions<ResourceManagementOptions>
9+
{
10+
private const string ModuleRoot = "~/" + FeatureIds.Base + "/";
11+
private const string Css = ModuleRoot + "css/";
12+
private const string Scripts = ModuleRoot + "js/";
13+
private const string Vendors = ModuleRoot + "vendors/";
14+
private static readonly ResourceManifest _manifest = new();
15+
16+
static LucideResourceManagementOptionsConfiguration()
17+
{
18+
_manifest
19+
.DefineScript(Constants.ResourceNames.Lucide)
20+
.SetUrl(
21+
Vendors + "lucide/dist/umd/lucide.min.js",
22+
Vendors + "lucide/dist/umd/lucide.js")
23+
.SetVersion(LibManVersions.Lucide);
24+
25+
_manifest
26+
.DefineScript(Constants.ResourceNames.LucideIconPicker)
27+
.SetUrl(Scripts + "lucide-icon-picker.js")
28+
.SetDependencies(Constants.ResourceNames.Lucide);
29+
30+
_manifest
31+
.DefineStyle(Constants.ResourceNames.LucideIconPicker)
32+
.SetUrl(Css + "lucide-icon-picker.css");
33+
}
34+
35+
public void Configure(ResourceManagementOptions options) => options.ResourceManifests.Add(_manifest);
36+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Lucide
2+
3+
Adds the [Lucide](https://lucide.dev/) icon library integration.
4+
5+
## Lucide resource
6+
7+
The `Lucide` script resource registers the Lucide UMD bundle so themes and modules can render `data-lucide` icons and call `window.lucide.createIcons()`.
8+
9+
## Lucide Icon Picker
10+
11+
`LucideIconPicker` is a `TextField` editor flavor similar to Orchard Core's built-in `IconPicker`, but it selects Lucide icon names instead of Font Awesome names.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Options;
3+
using OrchardCore.Modules;
4+
using OrchardCore.ResourceManagement;
5+
6+
namespace Lombiq.HelpfulExtensions.Extensions.Lucide;
7+
8+
[Feature(FeatureIds.Lucide)]
9+
public sealed class Startup : StartupBase
10+
{
11+
public override void ConfigureServices(IServiceCollection services) =>
12+
services.AddTransient<IConfigureOptions<ResourceManagementOptions>, LucideResourceManagementOptionsConfiguration>();
13+
}

Lombiq.HelpfulExtensions/Extensions/OrchardRecipeMigration/Services/ListPartOrchardExportConverter.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,19 @@ public class ListPartOrchardExportConverter : IOrchardExportConverter
1818
public Task UpdateContentItemsAsync(XDocument document, IList<ContentItem> contentItems)
1919
{
2020
var itemsById = contentItems
21-
.Where(item => !string.IsNullOrEmpty(item.As<OrchardIds>()?.ExportId))
22-
.ToDictionary(item => item.As<OrchardIds>().ExportId);
21+
.SelectWhere(
22+
item => (Item: item, Ids: item.GetOrCreate<OrchardIds>()),
23+
pair => !string.IsNullOrEmpty(pair.Ids.ExportId) && !string.IsNullOrEmpty(pair.Ids.Parent))
24+
.ToDictionary(item => item.Ids.ExportId);
2325

24-
foreach (var item in itemsById.Values.Where(item => !string.IsNullOrEmpty(item.As<OrchardIds>().Parent)))
26+
foreach (var (item, ids) in itemsById.Values)
2527
{
26-
var parentId = item.As<OrchardIds>().Parent;
27-
if (!itemsById.TryGetValue(parentId, out var parent) || !parent.Has<ListPart>()) continue;
28+
if (!itemsById.TryGetValue(ids.Parent, out var parentPair) || !parentPair.Item.Has<ListPart>()) continue;
2829

2930
item.Alter<ContainedPart>(part =>
3031
{
31-
part.ListContentItemId = parent.ContentItemId;
32-
part.ListContentType = parent.ContentType;
32+
part.ListContentItemId = parentPair.Item.ContentItemId;
33+
part.ListContentType = parentPair.Item.ContentType;
3334
});
3435
}
3536

0 commit comments

Comments
 (0)