feat: add min and max fields to relation widget (resolves #5026) (#5238)

This commit is contained in:
Ned Zimmerman 2021-04-12 13:54:51 -03:00 committed by GitHub
parent 6561939d42
commit fe117e472e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 67 additions and 44 deletions

View File

@ -84,8 +84,8 @@ const ca = {
max: '%{fieldLabel} ha de ser %{maxValue} o més.',
rangeCount: '%{fieldLabel} ha de tenir entre %{minCount} i %{maxCount} element(s).',
rangeCountExact: '%{fieldLabel} ha de tenir exactament %{count} element(s).',
minCount: '%{fieldLabel} ha de tenir com a mínim %{minCount} element(s).',
maxCount: '%{fieldLabel} ha de ser %{maxCount} o inferior.',
rangeMin: '%{fieldLabel} ha de tenir com a mínim %{minCount} element(s).',
rangeMax: '%{fieldLabel} ha de ser %{maxCount} o inferior.',
invalidPath: `'%{path}' no és un camí vàlid`,
pathExists: `'%{path}' ja existeix`,
},

View File

@ -85,8 +85,8 @@ const da = {
max: '%{fieldLabel} være være %{maxValue} eller mindre.',
rangeCount: '%{fieldLabel} skal have mellem %{minCount} og %{maxCount} element(er).',
rangeCountExact: '%{fieldLabel} skal have præcis %{count} element(er).',
minCount: '%{fieldLabel} skal have mindst %{minCount} element(er).',
maxCount: '%{fieldLabel} skal have %{maxCount} eller færre element(er).',
rangeMin: '%{fieldLabel} skal have mindst %{minCount} element(er).',
rangeMax: '%{fieldLabel} skal have %{maxCount} eller færre element(er).',
invalidPath: `'%{path}' er ikke en gyldig sti`,
pathExists: `Stien '%{path}' findes allerede`,
},

View File

@ -79,8 +79,8 @@ const de = {
max: '%{fieldLabel} darf nicht größer als %{maxValue} sein.',
rangeCount: '%{fieldLabel} muss %{minCount} bis %{maxCount} Element(e) enthalten.',
rangeCountExact: '%{fieldLabel} muss exakt %{count} Element(e) enthalten.',
minCount: '%{fieldLabel} muss mindestens %{minCount} Element(e) enthalten.',
maxCount: '%{fieldLabel} darf maximal %{maxCount} Element(e) enthalten.',
rangeMin: '%{fieldLabel} muss mindestens %{minCount} Element(e) enthalten.',
rangeMax: '%{fieldLabel} darf maximal %{maxCount} Element(e) enthalten.',
invalidPath: `'%{path}' ist kein gültiger Pfad`,
pathExists: `Pfad '%{path}' existiert bereits`,
},

View File

@ -85,8 +85,8 @@ const en = {
max: '%{fieldLabel} must be %{maxValue} or less.',
rangeCount: '%{fieldLabel} must have between %{minCount} and %{maxCount} item(s).',
rangeCountExact: '%{fieldLabel} must have exactly %{count} item(s).',
minCount: '%{fieldLabel} must be at least %{minCount} item(s).',
maxCount: '%{fieldLabel} must be %{maxCount} or less item(s).',
rangeMin: '%{fieldLabel} must be at least %{minCount} item(s).',
rangeMax: '%{fieldLabel} must be %{maxCount} or less item(s).',
invalidPath: `'%{path}' is not a valid path`,
pathExists: `Path '%{path}' already exists`,
},

View File

@ -74,8 +74,8 @@ const es = {
max: '%{fieldLabel} debe ser %{maxValue} o menos.',
rangeCount: '%{fieldLabel} debe tener entre %{minCount} y %{maxCount} elemento(s).',
rangeCountExact: '%{fieldLabel} debe tener exactamente %{count} elemento(s).',
minCount: '%{fieldLabel} debe ser por lo menos %{minCount} elemento(s).',
maxCount: '%{fieldLabel} debe ser %{maxCount} o menos elemento(s).',
rangeMin: '%{fieldLabel} debe ser por lo menos %{minCount} elemento(s).',
rangeMax: '%{fieldLabel} debe ser %{maxCount} o menos elemento(s).',
},
},
editor: {

View File

@ -86,8 +86,8 @@ const fr = {
max: 'Le champ %{fieldLabel} doit avoir une valeur de %{maxValue} ou moins.',
rangeCount: '%{fieldLabel} doit avoir entre %{minCount} et %{maxCount} élément(s).',
rangeCountExact: '%{fieldLabel} doit avoir exactement %{count} éléments(s).',
minCount: '%{fieldLabel} doit avoir au moins %{minCount} éléments(s).',
maxCount: '%{fieldLabel} doit avoir %{maxCount} éléments(s) ou moins.',
rangeMin: '%{fieldLabel} doit avoir au moins %{minCount} éléments(s).',
rangeMax: '%{fieldLabel} doit avoir %{maxCount} éléments(s) ou moins.',
invalidPath: `'%{path}' n'est pas un chemin valide`,
pathExists: `Le chemin '%{path}' existe déjà`,
},

View File

@ -85,8 +85,8 @@ const hr = {
max: '%{fieldLabel} mora biti %{maxValue} ili manje.',
rangeCount: '%{fieldLabel} mora imati izeđu %{minCount} i %{maxCount} predmeta.',
rangeCountExact: '%{fieldLabel} mora imati točno %{count} predmeta.',
minCount: '%{fieldLabel} mora imati najmanje %{minCount} predmet(a).',
maxCount: '%{fieldLabel} mora imate %{maxCount} ili manje predmeta.',
rangeMin: '%{fieldLabel} mora imati najmanje %{minCount} predmet(a).',
rangeMax: '%{fieldLabel} mora imate %{maxCount} ili manje predmeta.',
invalidPath: `'%{path}' nije valjana putanja`,
pathExists: `Putanja '%{path}' već postoji`,
},

View File

@ -85,8 +85,8 @@ const ja = {
max: '%{fieldLabel}の最大値は%{maxValue}です。',
rangeCount: '%{fieldLabel}は%{minCount}個から%{maxCount}個まで選択してください。',
rangeCountExact: '%{fieldLabel}はちょうど%{count}個選択してください。',
minCount: '%{fieldLabel}は%{minCount}個以上選択してください。',
maxCount: '%{fieldLabel}は%{maxCount}個以下選択してください。',
rangeMin: '%{fieldLabel}は%{minCount}個以上選択してください。',
rangeMax: '%{fieldLabel}は%{maxCount}個以下選択してください。',
invalidPath: `'%{path}'は有効なパスではありません。`,
pathExists: `'%{path}'というパスはすでに存在しています。`,
},

View File

@ -79,8 +79,8 @@ const ko = {
max: '%{fieldLabel} 은(는) 최대 %{maxValue} 여야 합니다.',
rangeCount: '%{fieldLabel} 개수는 %{minCount} 개 에서 %{maxCount} 개 사이여야 합니다.',
rangeCountExact: '%{fieldLabel} 개수는 정확히 %{count} 개 여야 합니다.',
minCount: '%{fieldLabel} 개수는 적어도 %{minCount} 개 이상 이여야 합니다.',
maxCount: '%{fieldLabel} 개수는 최대 %{maxCount} 개 여야 합니다.',
rangeMin: '%{fieldLabel} 개수는 적어도 %{minCount} 개 이상 이여야 합니다.',
rangeMax: '%{fieldLabel} 개수는 최대 %{maxCount} 개 여야 합니다.',
invalidPath: `'%{path}' 은(는) 올바른 경로가 아닙니다.`,
pathExists: `'%{path}' 경로가 이미 존재합니다.`,
},

View File

@ -86,8 +86,8 @@ const lt = {
max: '%{fieldLabel} turi būti %{maxValue} arba mažiau.',
rangeCount: '%{fieldLabel} turi būti tarp %{minCount} ir %{maxCount} elementų/-o.',
rangeCountExact: '%{fieldLabel} turi turėti būtent tik %{count} elementų/-us.',
minCount: '%{fieldLabel} turi būti bent %{minCount} elementų.',
maxCount: '%{fieldLabel} turi būti %{maxCount} arba mažiau elementų.',
rangeMin: '%{fieldLabel} turi būti bent %{minCount} elementų.',
rangeMax: '%{fieldLabel} turi būti %{maxCount} arba mažiau elementų.',
invalidPath: `'%{path}' nėra taisyklinga nuoroda/adresas į resursą/-us`,
pathExists: `Adresas '%{path}' jau egzistuoja`,
},

View File

@ -74,8 +74,8 @@ const nb_no = {
max: '%{fieldLabel} må være %{maxValue} eller mindre.',
rangeCount: '%{fieldLabel} må ha mellom %{minCount} og %{maxCount} element(er).',
rangeCountExact: '%{fieldLabel} må ha nøyaktig %{count} element(er).',
minCount: '%{fieldLabel} må minst ha %{minCount} element(er).',
maxCount: '%{fieldLabel} må ha %{maxCount} eller færre element(er).',
rangeMin: '%{fieldLabel} må minst ha %{minCount} element(er).',
rangeMax: '%{fieldLabel} må ha %{maxCount} eller færre element(er).',
},
},
editor: {

View File

@ -85,8 +85,8 @@ const nl = {
max: '%{fieldLabel} moet hoogstens %{maxValue} bevatten.',
rangeCount: '%{fieldLabel} moet tussen %{minCount} en %{maxCount} item(s) bevatten.',
rangeCountExact: '%{fieldLabel} moet exact %{count} item(s) bevatten.',
minCount: '%{fieldLabel} moet tenminste %{minCount} item(s) bevatten.',
maxCount: '%{fieldLabel} moet hoogstens %{maxCount} item(s) bevatten.',
rangeMin: '%{fieldLabel} moet tenminste %{minCount} item(s) bevatten.',
rangeMax: '%{fieldLabel} moet hoogstens %{maxCount} item(s) bevatten.',
},
i18n: {
writingInLocale: '%{locale} aan het bewerken',

View File

@ -74,8 +74,8 @@ const nn_no = {
max: '%{fieldLabel} må vera %{maxValue} eller mindre.',
rangeCount: '%{fieldLabel} må ha mellom %{minCount} og %{maxCount} element.',
rangeCountExact: '%{fieldLabel} må ha nøyaktig %{count} element.',
minCount: '%{fieldLabel} må minst ha %{minCount} element.',
maxCount: '%{fieldLabel} må ha %{maxCount} eller færre element.',
rangeMin: '%{fieldLabel} må minst ha %{minCount} element.',
rangeMax: '%{fieldLabel} må ha %{maxCount} eller færre element.',
},
},
editor: {

View File

@ -74,8 +74,8 @@ const pl = {
max: '%{fieldLabel} musi być %{maxValue} lub mniej.',
rangeCount: '%{fieldLabel} musi mieć od %{minCount} do %{maxCount} elementów',
rangeCountExact: '%{fieldLabel} musi mieć %{count} elementów',
minCount: '%{fieldLabel} musi mieć przynajmniej %{minCount} elementów',
maxCount: '%{fieldLabel} może mieć maksymalnie %{maxCount} elementów',
rangeMin: '%{fieldLabel} musi mieć przynajmniej %{minCount} elementów',
rangeMax: '%{fieldLabel} może mieć maksymalnie %{maxCount} elementów',
},
},
editor: {

View File

@ -79,8 +79,8 @@ const pt = {
max: '%{fieldLabel} deve ser igual ou menor que %{maxValue}.',
rangeCount: '%{fieldLabel} deve ser entre %{minCount} e %{maxCount}.',
rangeCountExact: '%{fieldLabel} deve ser exatamente %{count}.',
minCount: '%{fieldLabel} deve ter, pelo menos, %{minCount}.',
maxCount: '%{fieldLabel} deve ter %{maxCount} ou menos.',
rangeMin: '%{fieldLabel} deve ter, pelo menos, %{minCount}.',
rangeMax: '%{fieldLabel} deve ter %{maxCount} ou menos.',
invalidPath: `'%{path}' não é um caminho válido`,
pathExists: `O caminho '%{path}' já existe`,
},

View File

@ -85,8 +85,8 @@ const ru = {
max: 'Значение поля %{fieldLabel} должно быть %{maxValue} или менее.',
rangeCount: '%{fieldLabel} должно содержать от %{minCount} до %{maxCount} элементов.',
rangeCountExact: '%{fieldLabel} должно содержать строго %{count} элементов.',
minCount: '%{fieldLabel} должно содержать не менее %{minCount} элементов.',
maxCount: '%{fieldLabel} должно содержать %{maxCount} или менее элементов.',
rangeMin: '%{fieldLabel} должно содержать не менее %{minCount} элементов.',
rangeMax: '%{fieldLabel} должно содержать %{maxCount} или менее элементов.',
invalidPath: `Путь '%{path}' содежрит ошибки`,
pathExists: `Путь '%{path}' уже существует`,
},

View File

@ -74,8 +74,8 @@ const sv = {
max: '%{fieldLabel} måste vara %{maxValue} eller mindre.',
rangeCount: '%{fieldLabel} måste ha mellan %{minCount} och %{maxCount} element.',
rangeCountExact: '%{fieldLabel} måste ha exakt %{count} element.',
minCount: '%{fieldLabel} måste ha åtminstone %{minCount} element.',
maxCount: '%{fieldLabel} måste ha %{maxCount} eller färre element.',
rangeMin: '%{fieldLabel} måste ha åtminstone %{minCount} element.',
rangeMax: '%{fieldLabel} måste ha %{maxCount} eller färre element.',
},
},
editor: {

View File

@ -79,8 +79,8 @@ const th = {
max: '%{fieldLabel} จะต้องมีค่าไม่มากกว่า %{maxValue}',
rangeCount: '%{fieldLabel} จะต้องอยู่ระหว่าง %{minCount} และ %{maxCount} รายการ',
rangeCountExact: '%{fieldLabel} จะต้องมี %{count} รายการ',
minCount: '%{fieldLabel} จะต้องมีไม่ต่ำกว่า %{minCount} รายการ',
maxCount: '%{fieldLabel} จะต้องมีไม่มากกว่า %{maxCount} รายการ',
rangeMin: '%{fieldLabel} จะต้องมีไม่ต่ำกว่า %{minCount} รายการ',
rangeMax: '%{fieldLabel} จะต้องมีไม่มากกว่า %{maxCount} รายการ',
invalidPath: `'%{path}' พาทไม่ถูกต้อง`,
pathExists: `พาท '%{path}' มีอยู่แล้ว`,
},

View File

@ -63,8 +63,8 @@ const tr = {
max: '%{fieldLabel}, %{maxValue} veya daha az olmalı.',
rangeCount: '%{fieldLabel}, %{minCount} ve %{maxCount} öğeleri arasında olmalı.',
rangeCountExact: '%{fieldLabel}, %{count} öğe olmalıdır.',
minCount: '%{fieldLabel}, en az %{minCount} öğe olmalıdır.',
maxCount: '%{fieldLabel}, %{maxCount} veya daha az öğe olmalıdır.',
rangeMin: '%{fieldLabel}, en az %{minCount} öğe olmalıdır.',
rangeMax: '%{fieldLabel}, %{maxCount} veya daha az öğe olmalıdır.',
},
},
editor: {

View File

@ -79,8 +79,8 @@ const vi = {
max: '%{fieldLabel} tối đa %{maxValue} hoặc ít hơn.',
rangeCount: '%{fieldLabel} phải nằm trong khoảng từ %{minCount} đến %{maxCount} mục.',
rangeCountExact: '%{fieldLabel} phải có %{count} mục.',
minCount: '%{fieldLabel} phải có ít nhất %{minCount} mục.',
maxCount: '%{fieldLabel} phải có tối đa %{maxCount} mục hoặc ít hơn.',
rangeMin: '%{fieldLabel} phải có ít nhất %{minCount} mục.',
rangeMax: '%{fieldLabel} phải có tối đa %{maxCount} mục hoặc ít hơn.',
invalidPath: `Đường dẫn '%{path}' không hợp lệ`,
pathExists: `Đường dẫn '%{path}' đã tồn tại`,
},

View File

@ -79,8 +79,8 @@ const zh_Hant = {
max: '%{fieldLabel} 必須小於或等於 %{maxValue}',
rangeCount: '%{fieldLabel} 必須有 %{minCount} 到 %{maxCount} 個項目。',
rangeCountExact: '%{fieldLabel} 必須正好有 %{count} 個項目。',
minCount: '%{fieldLabel} 必須至少有 %{minCount} 個項目。',
maxCount: '%{fieldLabel} 最多只能有 %{maxCount} 個項目。',
rangeMin: '%{fieldLabel} 必須至少有 %{minCount} 個項目。',
rangeMax: '%{fieldLabel} 最多只能有 %{maxCount} 個項目。',
invalidPath: `'%{path}' 不是有效的路徑`,
pathExists: `路徑 '%{path}' 已經存在`,
},

View File

@ -5,7 +5,7 @@ import { Async as AsyncSelect } from 'react-select';
import { find, isEmpty, last, debounce, get, uniqBy } from 'lodash';
import { List, Map, fromJS } from 'immutable';
import { reactSelectStyles } from 'netlify-cms-ui-default';
import { stringTemplate } from 'netlify-cms-lib-widgets';
import { stringTemplate, validations } from 'netlify-cms-lib-widgets';
import { FixedSizeList } from 'react-window';
function Option({ index, style, data }) {
@ -97,6 +97,26 @@ export default class RelationControl extends React.Component {
setInactiveStyle: PropTypes.func.isRequired,
};
isValid = () => {
const { field, value, t } = this.props;
const min = field.get('min');
const max = field.get('max');
if (!this.isMultiple()) {
return { error: false };
}
const error = validations.validateMinMax(
t,
field.get('label', field.get('name')),
value,
min,
max,
);
return error ? { error } : { error: false };
};
shouldComponentUpdate(nextProps) {
return (
this.props.value !== nextProps.value ||

View File

@ -133,7 +133,6 @@ const numberFieldsHits = [
},
},
];
class RelationController extends React.Component {
state = {
value: this.props.value,

View File

@ -5,6 +5,8 @@ export default {
search_fields: { type: 'array', minItems: 1, items: { type: 'string' } },
file: { type: 'string' },
multiple: { type: 'boolean' },
min: { type: 'integer' },
max: { type: 'integer' },
display_fields: { type: 'array', minItems: 1, items: { type: 'string' } },
options_length: { type: 'integer' },
},

View File

@ -16,6 +16,8 @@ The relation widget allows you to reference items from another collection. It pr
* `display_fields`: list of one or more names of fields in the referenced collection that will render in the autocomplete menu of the control. Defaults to `value_field`. Syntax to reference nested fields is similar to that of *value_field*.
* `default`: accepts any widget data type; defaults to an empty string
* `multiple` : accepts a boolean, defaults to `false`
* `min`: minimum number of items; ignored if **multiple** is `false`
* `max`: maximum number of items; ignored if **multiple** is `false`
* `options_length`: accepts integer to override number of options presented to user. Defaults to `20`.
* **Referencing a folder collection example** (assuming a separate "authors" collection with "name" and "twitterHandle" fields with subfields "first" and "last" for the "name" field):