diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs
index f1264d5050..ab1715cf17 100644
--- a/src/Umbraco.Core/Constants-PropertyEditors.cs
+++ b/src/Umbraco.Core/Constants-PropertyEditors.cs
@@ -90,6 +90,11 @@ public static partial class Constants
///
public const string DateOnly = "Umbraco.DateOnly";
+ ///
+ /// Entity Data Picker
+ ///
+ public const string EntityDataPicker = "Umbraco.EntityDataPicker";
+
///
/// Time Only.
///
diff --git a/src/Umbraco.Core/Models/EntityDataPickerValue.cs b/src/Umbraco.Core/Models/EntityDataPickerValue.cs
new file mode 100644
index 0000000000..2b4274c3ef
--- /dev/null
+++ b/src/Umbraco.Core/Models/EntityDataPickerValue.cs
@@ -0,0 +1,8 @@
+namespace Umbraco.Cms.Core.Models;
+
+public sealed class EntityDataPickerValue
+{
+ public required IEnumerable Ids { get; set; }
+
+ public required string DataSource { get; set; }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/EntityDataPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/EntityDataPickerConfiguration.cs
new file mode 100644
index 0000000000..bdbaa636dd
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/EntityDataPickerConfiguration.cs
@@ -0,0 +1,17 @@
+namespace Umbraco.Cms.Core.PropertyEditors;
+
+public sealed class EntityDataPickerConfiguration
+{
+ [ConfigurationField("validationLimit")]
+ public NumberRange ValidationLimit { get; set; } = new();
+
+ [ConfigurationField("umbEditorDataSource")]
+ public string DataSource { get; set; } = string.Empty;
+
+ public class NumberRange
+ {
+ public int? Min { get; set; }
+
+ public int? Max { get; set; }
+ }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/EntityDataPickerConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/EntityDataPickerConfigurationEditor.cs
new file mode 100644
index 0000000000..16f7d644fa
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/EntityDataPickerConfigurationEditor.cs
@@ -0,0 +1,11 @@
+using Umbraco.Cms.Core.IO;
+
+namespace Umbraco.Cms.Core.PropertyEditors;
+
+internal sealed class EntityDataPickerConfigurationEditor : ConfigurationEditor
+{
+ public EntityDataPickerConfigurationEditor(IIOHelper ioHelper)
+ : base(ioHelper)
+ {
+ }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/EntityDataPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/EntityDataPickerPropertyEditor.cs
new file mode 100644
index 0000000000..29604e24ef
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/EntityDataPickerPropertyEditor.cs
@@ -0,0 +1,126 @@
+using System.ComponentModel.DataAnnotations;
+using Umbraco.Cms.Core.IO;
+using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Validation;
+using Umbraco.Cms.Core.PropertyEditors.Validation;
+using Umbraco.Cms.Core.Serialization;
+using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Core.Strings;
+using Umbraco.Extensions;
+
+namespace Umbraco.Cms.Core.PropertyEditors;
+
+///
+/// Represents an entity data picker property editor.
+///
+[DataEditor(
+ Constants.PropertyEditors.Aliases.EntityDataPicker,
+ ValueType = ValueTypes.Json,
+ ValueEditorIsReusable = true)]
+internal sealed class EntityDataPickerPropertyEditor : DataEditor
+{
+ private readonly IIOHelper _ioHelper;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EntityDataPickerPropertyEditor(IDataValueEditorFactory dataValueEditorFactory, IIOHelper ioHelper)
+ : base(dataValueEditorFactory)
+ {
+ _ioHelper = ioHelper;
+ SupportsReadOnly = true;
+ }
+
+ ///
+ public override IPropertyIndexValueFactory PropertyIndexValueFactory { get; } = new NoopPropertyIndexValueFactory();
+
+ ///
+ protected override IDataValueEditor CreateValueEditor()
+ => DataValueEditorFactory.Create(Attribute!);
+
+ ///
+ protected override IConfigurationEditor CreateConfigurationEditor() => new EntityDataPickerConfigurationEditor(_ioHelper);
+
+ ///
+ /// Defines the value editor for the entity data picker property editor.
+ ///
+ internal sealed class EntityDataPickerPropertyValueEditor : DataValueEditor
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EntityDataPickerPropertyValueEditor(
+ IShortStringHelper shortStringHelper,
+ IJsonSerializer jsonSerializer,
+ IIOHelper ioHelper,
+ DataEditorAttribute attribute,
+ ILocalizedTextService localizedTextService)
+ : base(shortStringHelper, jsonSerializer, ioHelper, attribute)
+ {
+ var validators = new TypedJsonValidatorRunner(
+ jsonSerializer,
+ new MinMaxValidator(localizedTextService));
+
+ Validators.Add(validators);
+ }
+
+ ///
+ /// Validates the min/max configuration for the entity data picker property editor.
+ ///
+ internal sealed class MinMaxValidator : ITypedJsonValidator
+ {
+ private readonly ILocalizedTextService _localizedTextService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MinMaxValidator(ILocalizedTextService localizedTextService) =>
+ _localizedTextService = localizedTextService;
+
+ ///
+ public IEnumerable Validate(
+ EntityDataPickerDto? data,
+ EntityDataPickerConfiguration? configuration,
+ string? valueType,
+ PropertyValidationContext validationContext)
+ {
+ var validationResults = new List();
+
+ if (data is null || configuration is null)
+ {
+ return validationResults;
+ }
+
+ if (configuration.ValidationLimit.Min is not null
+ && data.Ids.Length < configuration.ValidationLimit.Min)
+ {
+ validationResults.Add(new ValidationResult(
+ _localizedTextService.Localize(
+ "validation",
+ "entriesShort",
+ [configuration.ValidationLimit.Min.ToString(), (configuration.ValidationLimit.Min - data.Ids.Length).ToString()]),
+ ["value"]));
+ }
+
+ if (configuration.ValidationLimit.Max is not null
+ && data.Ids.Length > configuration.ValidationLimit.Max)
+ {
+ validationResults.Add(new ValidationResult(
+ _localizedTextService.Localize(
+ "validation",
+ "entriesExceed",
+ [configuration.ValidationLimit.Max.ToString(), (data.Ids.Length - configuration.ValidationLimit.Max).ToString()
+ ]),
+ ["value"]));
+ }
+
+ return validationResults;
+ }
+ }
+ }
+
+ internal sealed class EntityDataPickerDto
+ {
+ public string[] Ids { get; set; } = [];
+ }
+}
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/EntityDataPickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/EntityDataPickerValueConverter.cs
new file mode 100644
index 0000000000..811ce10544
--- /dev/null
+++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/EntityDataPickerValueConverter.cs
@@ -0,0 +1,43 @@
+using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.PublishedContent;
+using Umbraco.Cms.Core.Serialization;
+
+namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters;
+
+// NOTE: this class is made public on purpose because all value converters should be public
+[DefaultPropertyValueConverter(typeof(JsonValueConverter))]
+public sealed class EntityDataPickerValueConverter : PropertyValueConverterBase
+{
+ private readonly IJsonSerializer _jsonSerializer;
+
+ public EntityDataPickerValueConverter(IJsonSerializer jsonSerializer)
+ => _jsonSerializer = jsonSerializer;
+
+ public override bool IsConverter(IPublishedPropertyType propertyType)
+ => Constants.PropertyEditors.Aliases.EntityDataPicker.Equals(propertyType.EditorAlias);
+
+ public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
+ => typeof(EntityDataPickerValue);
+
+ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
+ => PropertyCacheLevel.Element;
+
+ public override object? ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object? source, bool preview)
+ {
+ if (source is not string sourceString
+ || propertyType.DataType.ConfigurationObject is not EntityDataPickerConfiguration dataTypeConfiguration)
+ {
+ return null;
+ }
+
+ EntityDataPickerDto? dto = _jsonSerializer.Deserialize(sourceString);
+ return dto is not null
+ ? new EntityDataPickerValue { Ids = dto.Ids, DataSource = dataTypeConfiguration.DataSource }
+ : null;
+ }
+
+ private class EntityDataPickerDto
+ {
+ public required string[] Ids { get; init; }
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/examples/picker-data-source/index.ts b/src/Umbraco.Web.UI.Client/examples/picker-data-source/index.ts
index fd4d9f3a2f..1d5a4aa2f6 100644
--- a/src/Umbraco.Web.UI.Client/examples/picker-data-source/index.ts
+++ b/src/Umbraco.Web.UI.Client/examples/picker-data-source/index.ts
@@ -1,7 +1,9 @@
+import { UMB_PICKER_DATA_SOURCE_TYPE } from '@umbraco-cms/backoffice/picker-data-source';
+
export const manifests: Array = [
{
type: 'propertyEditorDataSource',
- dataSourceType: 'picker',
+ dataSourceType: UMB_PICKER_DATA_SOURCE_TYPE,
alias: 'Umb.PropertyEditorDataSource.CustomPickerCollection',
name: 'Custom Picker Collection Data Source',
api: () => import('./example-custom-picker-collection-data-source.js'),
@@ -13,7 +15,7 @@ export const manifests: Array = [
},
{
type: 'propertyEditorDataSource',
- dataSourceType: 'picker',
+ dataSourceType: UMB_PICKER_DATA_SOURCE_TYPE,
alias: 'Umb.PropertyEditorDataSource.CustomPickerTree',
name: 'Custom Picker Tree Data Source',
api: () => import('./example-custom-picker-tree-data-source.js'),
@@ -25,7 +27,7 @@ export const manifests: Array = [
},
{
type: 'propertyEditorDataSource',
- dataSourceType: 'picker',
+ dataSourceType: UMB_PICKER_DATA_SOURCE_TYPE,
alias: 'Umb.PropertyEditorDataSource.DocumentPicker',
name: 'Document Picker Data Source',
api: () => import('./example-document-picker-data-source.js'),
@@ -53,7 +55,7 @@ export const manifests: Array = [
},
{
type: 'propertyEditorDataSource',
- dataSourceType: 'picker',
+ dataSourceType: UMB_PICKER_DATA_SOURCE_TYPE,
alias: 'Umb.PropertyEditorDataSource.MediaPicker',
name: 'Media Picker Data Source',
api: () => import('./example-media-picker-data-source.js'),
@@ -65,7 +67,7 @@ export const manifests: Array = [
},
{
type: 'propertyEditorDataSource',
- dataSourceType: 'picker',
+ dataSourceType: UMB_PICKER_DATA_SOURCE_TYPE,
alias: 'Umb.PropertyEditorDataSource.LanguagePicker',
name: 'Language Picker Data Source',
api: () => import('./example-language-picker-data-source.js'),
@@ -77,7 +79,7 @@ export const manifests: Array = [
},
{
type: 'propertyEditorDataSource',
- dataSourceType: 'picker',
+ dataSourceType: UMB_PICKER_DATA_SOURCE_TYPE,
alias: 'Umb.PropertyEditorDataSource.WebhookPicker',
name: 'Webhook Picker Data Source',
api: () => import('./example-webhook-picker-data-source.js'),
@@ -89,7 +91,7 @@ export const manifests: Array = [
},
{
type: 'propertyEditorDataSource',
- dataSourceType: 'picker',
+ dataSourceType: UMB_PICKER_DATA_SOURCE_TYPE,
alias: 'Umb.PropertyEditorDataSource.UserPicker',
name: 'User Picker Data Source',
api: () => import('./example-user-picker-data-source.js'),
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/constant.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/constant.ts
new file mode 100644
index 0000000000..0cf82c236e
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/constant.ts
@@ -0,0 +1 @@
+export const UMB_PICKER_DATA_SOURCE_TYPE = 'Umb.DataSourceType.Picker';
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/index.ts
index ca0e12ded8..6203532024 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/index.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/index.ts
@@ -1,4 +1,5 @@
export * from './collection-data-source/is-picker-collection-data-source.guard.js';
+export * from './constant.js';
export * from './searchable-data-source/is-picker-searchable.data-source.guard.js';
export * from './tree-data-source/is-picker-tree-data-source.guard.js';
export type * from './types.js';
diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/entry-point.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/entry-point.ts
index 5632c01f5a..839c9927a5 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/entry-point.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/entry-point.ts
@@ -1,5 +1,6 @@
import { manifests as entityDataPickerManifests } from './manifests.js';
import type { UmbEntryPointOnInit } from '@umbraco-cms/backoffice/extension-api';
+import { UMB_PICKER_DATA_SOURCE_TYPE } from '@umbraco-cms/backoffice/picker-data-source';
import type { ManifestPropertyEditorDataSource } from '@umbraco-cms/backoffice/property-editor-data-source';
export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => {
@@ -11,7 +12,7 @@ export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => {
extensionRegistry
.byTypeAndFilter<'propertyEditorDataSource', ManifestPropertyEditorDataSource>(
'propertyEditorDataSource',
- (manifest) => manifest.dataSourceType === 'picker',
+ (manifest) => manifest.dataSourceType === UMB_PICKER_DATA_SOURCE_TYPE,
)
.subscribe((pickerPropertyEditorDataSource) => {
if (pickerPropertyEditorDataSource.length > 0 && !initialized) {
diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/Umbraco.EntityDataPicker.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/Umbraco.EntityDataPicker.ts
new file mode 100644
index 0000000000..68c7a708a2
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/Umbraco.EntityDataPicker.ts
@@ -0,0 +1,21 @@
+export const manifests: Array = [
+ {
+ type: 'propertyEditorSchema',
+ name: 'Entity Data Picker',
+ alias: 'Umbraco.EntityDataPicker',
+ meta: {
+ defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.EntityDataPicker',
+ settings: {
+ properties: [
+ {
+ alias: 'validationLimit',
+ label: 'Amount',
+ propertyEditorUiAlias: 'Umb.PropertyEditorUi.NumberRange',
+ config: [{ alias: 'validationRange', value: { min: 0, max: Infinity } }],
+ weight: 100,
+ },
+ ],
+ },
+ },
+ },
+];
diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/entity-data-picker-property-editor-ui.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/entity-data-picker-property-editor-ui.element.ts
index 64a7ef4820..a8611f0f9f 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/entity-data-picker-property-editor-ui.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/entity-data-picker-property-editor-ui.element.ts
@@ -128,7 +128,7 @@ export class UmbEntityDataPickerPropertyEditorUIElement
// Ensure the value is of the correct type before setting it.
if (Array.isArray(selection)) {
- this.value = selection;
+ this.value = { ids: selection };
this.dispatchEvent(new UmbChangeEvent());
} else {
throw new Error('Selection is not of type array. Cannot set property value.');
@@ -137,7 +137,7 @@ export class UmbEntityDataPickerPropertyEditorUIElement
override render() {
return html` import('./entity-data-picker-property-editor-ui.element.js'),
- meta: {
- label: 'Entity Data Picker',
- icon: 'icon-page-add',
- group: 'pickers',
- propertyEditorSchemaAlias: 'Umbraco.Plain.Json',
- supportsReadOnly: true,
- supportsDataSource: {
- enabled: true,
- forDataSourceTypes: ['picker'],
- },
- settings: {
- properties: [
- // TODO: Move this to schema manifest when server can validate it
- {
- alias: 'validationLimit',
- label: 'Amount',
- propertyEditorUiAlias: 'Umb.PropertyEditorUi.NumberRange',
- config: [{ alias: 'validationRange', value: { min: 0, max: Infinity } }],
- weight: 100,
- },
- ],
+export const manifests: Array = [
+ {
+ type: 'propertyEditorUi',
+ alias: 'Umb.PropertyEditorUi.EntityDataPicker',
+ name: 'Entity Data Picker Property Editor UI',
+ element: () => import('./entity-data-picker-property-editor-ui.element.js'),
+ meta: {
+ label: 'Entity Data Picker',
+ icon: 'icon-page-add',
+ group: 'pickers',
+ propertyEditorSchemaAlias: 'Umbraco.EntityDataPicker',
+ supportsReadOnly: true,
+ supportsDataSource: {
+ enabled: true,
+ forDataSourceTypes: [UMB_PICKER_DATA_SOURCE_TYPE],
+ },
},
},
-};
-
-export const manifests: Array = [manifest];
+ ...schemaManifests,
+];
diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/types.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/types.ts
index fa9bbf6564..fd72143a5a 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/types.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/property-editor/types.ts
@@ -1 +1,3 @@
-export type UmbEntityDataPickerPropertyEditorValue = Array;
+export type UmbEntityDataPickerPropertyEditorValue = {
+ ids: Array;
+};
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs
index 253fcc2b4b..0940c10533 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs
@@ -132,7 +132,7 @@ public class TypeLoaderTests
public void GetDataEditors()
{
var types = _typeLoader.GetDataEditors();
- Assert.AreEqual(41, types.Count());
+ Assert.AreEqual(42, types.Count());
}
///
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/EntityDataPickerPropertyValueEditorTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/EntityDataPickerPropertyValueEditorTests.cs
new file mode 100644
index 0000000000..7dbb0b2d49
--- /dev/null
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/EntityDataPickerPropertyValueEditorTests.cs
@@ -0,0 +1,95 @@
+using System.Globalization;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Cms.Core.IO;
+using Umbraco.Cms.Core.Models.Validation;
+using Umbraco.Cms.Core.PropertyEditors;
+using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Core.Strings;
+using Umbraco.Cms.Infrastructure.Serialization;
+
+namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors;
+
+[TestFixture]
+public class EntityDataPickerPropertyValueEditorTests
+{
+ [TestCase(1, false)]
+ [TestCase(2, true)]
+ [TestCase(3, true)]
+ public void Validates_Is_Greater_Than_Or_Equal_To_Configured_Min(int numberOfSelections, bool expectedSuccess)
+ {
+ var editor = CreateValueEditor();
+ var value = new EntityDataPickerPropertyEditor.EntityDataPickerDto
+ {
+ Ids = [.. Enumerable.Range(1, numberOfSelections).Select(i => i.ToString())],
+ };
+ var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
+ var serializedValue = serializer.Serialize(value);
+ var result = editor.Validate(serializedValue, false, null, PropertyValidationContext.Empty());
+ if (expectedSuccess)
+ {
+ Assert.IsEmpty(result);
+ }
+ else
+ {
+ Assert.AreEqual(1, result.Count());
+
+ var validationResult = result.First();
+ Assert.AreEqual("validation_entriesShort", validationResult.ErrorMessage);
+ }
+ }
+
+ [TestCase(3, true)]
+ [TestCase(4, true)]
+ [TestCase(5, false)]
+ public void Validates_Is_Less_Than_Or_Equal_To_Configured_Max(int numberOfSelections, bool expectedSuccess)
+ {
+ var editor = CreateValueEditor();
+ var value = new EntityDataPickerPropertyEditor.EntityDataPickerDto
+ {
+ Ids = [.. Enumerable.Range(1, numberOfSelections).Select(i => i.ToString())],
+ };
+ var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
+ var serializedValue = serializer.Serialize(value);
+ var result = editor.Validate(serializedValue, false, null, PropertyValidationContext.Empty());
+ if (expectedSuccess)
+ {
+ Assert.IsEmpty(result);
+ }
+ else
+ {
+ Assert.AreEqual(1, result.Count());
+
+ var validationResult = result.First();
+ Assert.AreEqual("validation_entriesExceed", validationResult.ErrorMessage);
+ }
+ }
+
+ private static EntityDataPickerPropertyEditor.EntityDataPickerPropertyValueEditor CreateValueEditor()
+ {
+ var localizedTextServiceMock = new Mock();
+ localizedTextServiceMock.Setup(x => x.Localize(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny>()))
+ .Returns((string key, string alias, CultureInfo culture, IDictionary args) => $"{key}_{alias}");
+ return new EntityDataPickerPropertyEditor.EntityDataPickerPropertyValueEditor(
+ Mock.Of(),
+ new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory()),
+ Mock.Of(),
+ new DataEditorAttribute("alias"),
+ localizedTextServiceMock.Object)
+ {
+ ConfigurationObject = new EntityDataPickerConfiguration
+ {
+ DataSource = "testDataSource",
+ ValidationLimit = new EntityDataPickerConfiguration.NumberRange
+ {
+ Min = 2,
+ Max = 4
+ }
+ },
+ };
+ }
+}