Skip to content

Commit

Permalink
Allow specifying subset of locales with required field option
Browse files Browse the repository at this point in the history
  • Loading branch information
kyoshino committed Dec 27, 2024
1 parent 8df9468 commit 83d1adf
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import { _ } from 'svelte-i18n';
import { writable } from 'svelte/store';
import { defaultI18nConfig } from '$lib/services/contents/i18n';
import { isFieldRequired } from '$lib/services/contents/entry/fields';
import { revertChanges } from '$lib/services/contents/draft/update';
import { entryDraft } from '$lib/services/contents/draft';
import { editors } from '$lib/components/contents/details/widgets';
Expand Down Expand Up @@ -55,10 +56,10 @@
comment = '',
hint = '',
widget: widgetName = 'string',
required = true,
i18n = false,
pattern = /** @type {string[]} */ ([]),
} = $derived(fieldConfig);
const required = $derived(isFieldRequired({ fieldConfig, locale }));
const {
field: subField,
fields: subFields,
Expand Down
7 changes: 4 additions & 3 deletions src/lib/services/contents/draft/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getCollection } from '$lib/services/contents/collection';
import { entryDraft, i18nAutoDupEnabled } from '$lib/services/contents/draft';
import { restoreBackupIfNeeded } from '$lib/services/contents/draft/backup';
import { showDuplicateToast } from '$lib/services/contents/draft/editor';
import { getFieldConfig } from '$lib/services/contents/entry/fields';
import { getFieldConfig, isFieldRequired } from '$lib/services/contents/entry/fields';
import { getDefaultValue as getDefaultDateTimeValue } from '$lib/services/contents/widgets/date-time/helper';
import { getDefaultValue as getDefaultHiddenValue } from '$lib/services/contents/widgets/hidden/helper';
import { getDefaultValue as getDefaultUuidValue } from '$lib/services/contents/widgets/uuid/helper';
Expand Down Expand Up @@ -113,7 +113,8 @@ export const getDefaultValues = (fields, locale, dynamicValues = {}) => {
return;
}

const { widget: widgetName = 'string', default: defaultValue, required = true } = fieldConfig;
const { widget: widgetName = 'string', default: defaultValue } = fieldConfig;
const required = isFieldRequired({ fieldConfig, locale });
const isArray = Array.isArray(defaultValue) && !!defaultValue.length;

if (widgetName === 'list') {
Expand Down Expand Up @@ -264,7 +265,7 @@ export const createProxy = ({
// Update validity in real time if validation has already been performed
if (validity) {
// @todo Perform all the field validations, not just `valueMissing` for string fields
if (typeof value === 'string' && fieldConfig.required !== false) {
if (typeof value === 'string' && isFieldRequired({ fieldConfig, locale: sourceLocale })) {
validity.valueMissing = !value;
}
}
Expand Down
10 changes: 3 additions & 7 deletions src/lib/services/contents/draft/validate.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { escapeRegExp } from '@sveltia/utils/string';
import { get } from 'svelte/store';
import { entryDraft } from '$lib/services/contents/draft';
import { getFieldConfig } from '$lib/services/contents/entry/fields';
import { getFieldConfig, isFieldRequired } from '$lib/services/contents/entry/fields';
import { validateStringField } from '$lib/services/contents/widgets/string/helper';

// cspell:disable-next-line
Expand Down Expand Up @@ -53,12 +53,8 @@ export const validateEntry = () => {
return;
}

const {
widget: widgetName = 'string',
required = true,
i18n = false,
pattern: validation,
} = fieldConfig;
const { widget: widgetName = 'string', i18n = false, pattern: validation } = fieldConfig;
const required = isFieldRequired({ fieldConfig, locale });

// Skip validation on non-editable fields
if (
Expand Down
10 changes: 10 additions & 0 deletions src/lib/services/contents/entry/fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ export const getFieldConfig = ({
return field;
};

/**
* Check if the field requires user input.
* @param {object} args - Arguments.
* @param {Field} args.fieldConfig - Field configuration.
* @param {LocaleCode} args.locale - Current pane’s locale.
* @returns {boolean} Result.
*/
export const isFieldRequired = ({ fieldConfig: { required = true }, locale }) =>
Array.isArray(required) ? required.includes(locale) : !!required;

/**
* Get a field’s display value that matches the given field name (key path).
* @param {object} args - Arguments.
Expand Down
23 changes: 23 additions & 0 deletions src/lib/services/contents/entry/fields.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, expect, test } from 'vitest';
import { isFieldRequired } from '$lib/services/contents/entry/fields';

describe('Test isFieldRequired()', () => {
const name = 'title';
const locale = 'en';

test('required: undefined', () => {
expect(isFieldRequired({ fieldConfig: { name }, locale })).toBe(true);
});

test('required: boolean', () => {
expect(isFieldRequired({ fieldConfig: { name, required: true }, locale })).toBe(true);
expect(isFieldRequired({ fieldConfig: { name, required: false }, locale })).toBe(false);
});

test('required: array', () => {
expect(isFieldRequired({ fieldConfig: { name, required: ['en'] }, locale })).toBe(true);
expect(isFieldRequired({ fieldConfig: { name, required: ['ja'] }, locale })).toBe(false);
expect(isFieldRequired({ fieldConfig: { name, required: ['en', 'ja'] }, locale })).toBe(true);
expect(isFieldRequired({ fieldConfig: { name, required: [] }, locale })).toBe(false);
});
});
4 changes: 3 additions & 1 deletion src/lib/typedefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,9 @@
* @property {string} [label] - Field label.
* @property {string} [comment] - Field description.
* @property {string} [widget] - Widget name.
* @property {boolean} [required] - Whether to require input.
* @property {boolean | LocaleCode[]} [required] - Whether to require user input for the field. If
* i18n is enabled and the field doesn’t require input for every locale, a subset of locales can be
* passed as an array.
* @property {string[]} [pattern] - Validation format.
* @property {string} [hint] - Value hint to be displayed below the input.
* @property {boolean} [preview] - Whether to show the preview of the field. Default: `true`.
Expand Down

0 comments on commit 83d1adf

Please sign in to comment.