Skip to content

Commit 767894b

Browse files
Color Picker: Validate uniqueness of selected colors (#20431)
* Added unique color checker to color picker. * Added Unittest for duplicates * optimized for codescene * removed the bump and simplified the function * Fixed behaviour for duplicate checks so unit test passes. A little refactoring. * Adds continue so invalid colors aren't checked for duplicates. --------- Co-authored-by: Andy Butland <abutland73@gmail.com>
1 parent 1fe7931 commit 767894b

File tree

2 files changed

+68
-5
lines changed

2 files changed

+68
-5
lines changed

src/Umbraco.Infrastructure/PropertyEditors/ColorPickerConfigurationEditor.cs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,29 @@
1010

1111
namespace Umbraco.Cms.Core.PropertyEditors;
1212

13-
internal sealed class ColorPickerConfigurationEditor : ConfigurationEditor<ColorPickerConfiguration>
13+
internal sealed partial class ColorPickerConfigurationEditor : ConfigurationEditor<ColorPickerConfiguration>
1414
{
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="ColorPickerConfigurationEditor"/> class.
17+
/// </summary>
1518
public ColorPickerConfigurationEditor(IIOHelper ioHelper, IConfigurationEditorJsonSerializer configurationEditorJsonSerializer)
1619
: base(ioHelper)
1720
{
1821
ConfigurationField items = Fields.First(x => x.Key == "items");
1922
items.Validators.Add(new ColorListValidator(configurationEditorJsonSerializer));
2023
}
2124

22-
internal sealed class ColorListValidator : IValueValidator
25+
internal sealed partial class ColorListValidator : IValueValidator
2326
{
2427
private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer;
2528

29+
/// <summary>
30+
/// Initializes a new instance of the <see cref="ColorListValidator"/> class.
31+
/// </summary>
2632
public ColorListValidator(IConfigurationEditorJsonSerializer configurationEditorJsonSerializer)
2733
=> _configurationEditorJsonSerializer = configurationEditorJsonSerializer;
2834

35+
/// <inheritdoc/>
2936
public IEnumerable<ValidationResult> Validate(object? value, string? valueType, object? dataTypeConfiguration, PropertyValidationContext validationContext)
3037
{
3138
var stringValue = value?.ToString();
@@ -46,17 +53,53 @@ public IEnumerable<ValidationResult> Validate(object? value, string? valueType,
4653

4754
if (items is null)
4855
{
49-
yield return new ValidationResult($"The configuration value {stringValue} is not a valid color picker configuration", new[] { "items" });
56+
yield return new ValidationResult($"The configuration value {stringValue} is not a valid color picker configuration", ["items"]);
5057
yield break;
5158
}
5259

60+
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
61+
var duplicates = new List<string>();
5362
foreach (ColorPickerConfiguration.ColorPickerItem item in items)
5463
{
55-
if (Regex.IsMatch(item.Value, "^([0-9a-f]{3}|[0-9a-f]{6})$", RegexOptions.IgnoreCase) == false)
64+
if (ColorPattern().IsMatch(item.Value) == false)
5665
{
57-
yield return new ValidationResult($"The value {item.Value} is not a valid hex color", new[] { "items" });
66+
yield return new ValidationResult($"The value {item.Value} is not a valid hex color", ["items"]);
67+
continue;
68+
}
69+
70+
var normalized = Normalize(item.Value);
71+
if (seen.Add(normalized) is false)
72+
{
73+
duplicates.Add(normalized);
5874
}
5975
}
76+
77+
if (duplicates.Count > 0)
78+
{
79+
yield return new ValidationResult(
80+
$"Duplicate color values are not allowed: {string.Join(", ", duplicates)}",
81+
["items"]);
82+
}
6083
}
84+
85+
private static string Normalize(string? value)
86+
{
87+
if (string.IsNullOrWhiteSpace(value))
88+
{
89+
return string.Empty;
90+
}
91+
92+
var normalizedValue = value.Trim().ToLowerInvariant();
93+
94+
if (normalizedValue.Length == 3)
95+
{
96+
normalizedValue = $"{normalizedValue[0]}{normalizedValue[0]}{normalizedValue[1]}{normalizedValue[1]}{normalizedValue[2]}{normalizedValue[2]}";
97+
}
98+
99+
return normalizedValue;
100+
}
101+
102+
[GeneratedRegex("^([0-9a-f]{3}|[0-9a-f]{6})$", RegexOptions.IgnoreCase, "en-GB")]
103+
private static partial Regex ColorPattern();
61104
}
62105
}

tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/ColorListValidatorTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,24 @@ public void Validates_Color_Vals()
5858
PropertyValidationContext.Empty());
5959
Assert.AreEqual(2, result.Count());
6060
}
61+
62+
[Test]
63+
public void Validates_Color_Vals_Are_Unique()
64+
{
65+
var validator = new ColorPickerConfigurationEditor.ColorListValidator(ConfigurationEditorJsonSerializer());
66+
var result =
67+
validator.Validate(
68+
new JsonArray(
69+
JsonNode.Parse("""{"value": "FFFFFF", "label": "One"}"""),
70+
JsonNode.Parse("""{"value": "000000", "label": "Two"}"""),
71+
JsonNode.Parse("""{"value": "FF00AA", "label": "Three"}"""),
72+
JsonNode.Parse("""{"value": "fff", "label": "Four"}"""),
73+
JsonNode.Parse("""{"value": "000000", "label": "Five"}"""),
74+
JsonNode.Parse("""{"value": "F0A", "label": "Six"}""")),
75+
null,
76+
null,
77+
PropertyValidationContext.Empty());
78+
Assert.AreEqual(1, result.Count());
79+
Assert.IsTrue(result.First().ErrorMessage.Contains("ffffff, 000000, ff00aa"));
80+
}
6181
}

0 commit comments

Comments
 (0)