feat(admin): follow presets on mltext fields per locale
- formState: changedLocales and presetUpdates hold the preset-follow rules for text and mltext pairs - FormView applies presetUpdates on create, tracks per-locale hand edits of ML fields and follows the active ML locale - rebuilt modules/boardwalk/dist
This commit is contained in:
@@ -92,6 +92,91 @@ export function presetValue(type: string, source: unknown): string {
|
|||||||
.replace(/^-+|-+$/g, '')
|
.replace(/^-+|-+$/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The locales whose text differs between two locale maps: every locale on
|
||||||
|
* either side, a missing key counting as ''. A value that is not a map counts
|
||||||
|
* as an empty map. */
|
||||||
|
export function changedLocales(previous: unknown, next: unknown): string[] {
|
||||||
|
const before = localeRecord(previous)
|
||||||
|
const after = localeRecord(next)
|
||||||
|
const out: string[] = []
|
||||||
|
for (const locale of new Set([...Object.keys(before), ...Object.keys(after)])) {
|
||||||
|
if ((before[locale] ?? '') !== (after[locale] ?? '')) {
|
||||||
|
out.push(locale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reports whether the administrator edited a preset target by hand: the
|
||||||
|
* whole field when no locale is given, one locale of an ML target otherwise. */
|
||||||
|
export type PresetEdited = (target: string, locale?: string) => boolean
|
||||||
|
|
||||||
|
function isMLType(type: string | undefined): boolean {
|
||||||
|
return type === 'mltext' || type === 'mlmarkdown'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preset targets of `source` (fields.yaml `preset`) to rewrite after the
|
||||||
|
* source changed from `previous` to `next`: target name -> new value. Targets
|
||||||
|
* are text and mltext fields; a preset on any other type is ignored.
|
||||||
|
*
|
||||||
|
* - text -> text: the target takes presetValue of the source, until edited.
|
||||||
|
* - ML -> text: the target takes presetValue of the source's active-locale text.
|
||||||
|
* - text -> ML: only the target's active locale is written; its other locales stay.
|
||||||
|
* - ML -> ML: per locale. Each locale whose source text changed rewrites the
|
||||||
|
* same locale of the target, unless the administrator edited that locale by
|
||||||
|
* hand; the target's other locales stay.
|
||||||
|
*
|
||||||
|
* A preset never writes a locale the administrator neither typed in (source)
|
||||||
|
* nor sees (target), so untouched locales keep the server's fallback.
|
||||||
|
*/
|
||||||
|
export function presetUpdates(
|
||||||
|
fields: FormField[],
|
||||||
|
source: string,
|
||||||
|
previous: unknown,
|
||||||
|
next: unknown,
|
||||||
|
values: AdminRecord,
|
||||||
|
activeLocale: string,
|
||||||
|
isEdited: PresetEdited,
|
||||||
|
): AdminRecord {
|
||||||
|
const out: AdminRecord = {}
|
||||||
|
const sourceML = isMLType(fields.find((field) => field.name === source)?.type)
|
||||||
|
for (const field of fields) {
|
||||||
|
const preset = field.preset
|
||||||
|
if (!preset || preset.field !== source || (field.type !== 'text' && field.type !== 'mltext')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (field.type === 'text') {
|
||||||
|
if (isEdited(field.name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[field.name] = presetValue(preset.type, sourceML ? (localeRecord(next)[activeLocale] ?? '') : next)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!sourceML) {
|
||||||
|
if (isEdited(field.name, activeLocale)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[field.name] = { ...localeRecord(values[field.name]), [activeLocale]: presetValue(preset.type, next) }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const texts = localeRecord(next)
|
||||||
|
const target = { ...localeRecord(values[field.name]) }
|
||||||
|
let written = false
|
||||||
|
for (const locale of changedLocales(previous, next)) {
|
||||||
|
if (isEdited(field.name, locale)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
target[locale] = presetValue(preset.type, texts[locale] ?? '')
|
||||||
|
written = true
|
||||||
|
}
|
||||||
|
if (written) {
|
||||||
|
out[field.name] = target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
/** Initial values of a new record: schema defaults, toggles off, no ids.
|
/** Initial values of a new record: schema defaults, toggles off, no ids.
|
||||||
* ML fields seed an empty string for every enabled locale. */
|
* ML fields seed an empty string for every enabled locale. */
|
||||||
export function initialValues(fields: FormField[], enabledLocales: readonly string[] = []): AdminRecord {
|
export function initialValues(fields: FormField[], enabledLocales: readonly string[] = []): AdminRecord {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onBeforeUnmount, onMounted, provide, readonly, ref, watchEffect } from 'vue'
|
import { computed, nextTick, onBeforeUnmount, onMounted, provide, readonly, ref, watch, watchEffect } from 'vue'
|
||||||
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
|
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
|
||||||
import { ArrowLeft, Trash2 } from '@lucide/vue'
|
import { ArrowLeft, Trash2 } from '@lucide/vue'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
@@ -17,6 +17,8 @@ import { FORM_ASSETS, FORM_ENABLED_LOCALES, FORM_LOCALE, FORM_PATCH, FORM_SESSIO
|
|||||||
import { RELATION_MANAGER, needsRecord } from '../components/form/registry'
|
import { RELATION_MANAGER, needsRecord } from '../components/form/registry'
|
||||||
import {
|
import {
|
||||||
DEFAULT_TAB,
|
DEFAULT_TAB,
|
||||||
|
ML_LOCALE_CHANGE,
|
||||||
|
changedLocales,
|
||||||
contextAllows,
|
contextAllows,
|
||||||
editablePayload,
|
editablePayload,
|
||||||
fieldErrors,
|
fieldErrors,
|
||||||
@@ -24,7 +26,7 @@ import {
|
|||||||
initialValues,
|
initialValues,
|
||||||
mergeMLValue,
|
mergeMLValue,
|
||||||
panelDomId,
|
panelDomId,
|
||||||
presetValue,
|
presetUpdates,
|
||||||
schemaEnabledLocales,
|
schemaEnabledLocales,
|
||||||
snapshot,
|
snapshot,
|
||||||
tabDomId,
|
tabDomId,
|
||||||
@@ -205,24 +207,60 @@ async function load(): Promise<void> {
|
|||||||
values.value = initialValues(fields.value, schemaEnabledLocales(schema.value.meta))
|
values.value = initialValues(fields.value, schemaEnabledLocales(schema.value.meta))
|
||||||
}
|
}
|
||||||
adopt(recordResult?.data)
|
adopt(recordResult?.data)
|
||||||
|
mlLocale.value = firstMLLocale()
|
||||||
activeTab.value = tabs.value[0]?.key ?? DEFAULT_TAB
|
activeTab.value = tabs.value[0]?.key ?? DEFAULT_TAB
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preset fields (fields.yaml `preset`, UI-SPEC S7): on create, a text field
|
// Preset fields (fields.yaml `preset`, UI-SPEC S7): on create, a text or
|
||||||
// follows its source field until the administrator edits it by hand; that
|
// mltext field follows its text or mltext source until the administrator
|
||||||
// stops it for as long as the form is open. On update nothing follows.
|
// edits it by hand; that stops it for as long as the form is open. An mltext
|
||||||
|
// pair follows per locale and a hand edit of one locale stops only that
|
||||||
|
// locale; a mixed pair follows the active ML locale (presetUpdates). On
|
||||||
|
// update nothing follows.
|
||||||
const edited = new Set<string>()
|
const edited = new Set<string>()
|
||||||
|
// The locales of each ML field the administrator edited by hand.
|
||||||
|
const editedLocales = new Map<string, Set<string>>()
|
||||||
|
// The locale every ML control shows (MLTextField broadcasts the choice).
|
||||||
|
const mlLocale = ref('en')
|
||||||
|
|
||||||
|
function firstMLLocale(): string {
|
||||||
|
return schemaEnabledLocales(schema.value?.meta)[0] ?? 'en'
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMLLocale(event: Event): void {
|
||||||
|
const locale = (event as CustomEvent<unknown>).detail
|
||||||
|
if (typeof locale === 'string' && locale !== '') {
|
||||||
|
mlLocale.value = locale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormGrid remounts the new panel's ML controls on the first enabled locale.
|
||||||
|
watch(activeTab, () => {
|
||||||
|
mlLocale.value = firstMLLocale()
|
||||||
|
})
|
||||||
|
|
||||||
|
function isEdited(target: string, locale?: string): boolean {
|
||||||
|
return locale === undefined ? edited.has(target) : (editedLocales.get(target)?.has(locale) ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
function update(name: string, value: unknown): void {
|
function update(name: string, value: unknown): void {
|
||||||
|
const previous = values.value[name]
|
||||||
const next = { ...values.value, [name]: value }
|
const next = { ...values.value, [name]: value }
|
||||||
edited.add(name)
|
edited.add(name)
|
||||||
|
const type = fields.value.find((field) => field.name === name)?.type
|
||||||
|
if (type === 'mltext' || type === 'mlmarkdown') {
|
||||||
|
let locales = editedLocales.get(name)
|
||||||
|
if (!locales) {
|
||||||
|
locales = new Set<string>()
|
||||||
|
editedLocales.set(name, locales)
|
||||||
|
}
|
||||||
|
for (const locale of changedLocales(previous, value)) {
|
||||||
|
locales.add(locale)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (mode === 'create') {
|
if (mode === 'create') {
|
||||||
for (const field of fields.value) {
|
Object.assign(next, presetUpdates(fields.value, name, previous, value, values.value, mlLocale.value, isEdited))
|
||||||
if (field.preset?.field === name && field.type === 'text' && !edited.has(field.name)) {
|
|
||||||
next[field.name] = presetValue(field.preset.type, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
values.value = next
|
values.value = next
|
||||||
if (errors.value[name]) {
|
if (errors.value[name]) {
|
||||||
@@ -411,8 +449,14 @@ function onBeforeUnload(event: BeforeUnloadEvent): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => window.addEventListener('beforeunload', onBeforeUnload))
|
onMounted(() => {
|
||||||
onBeforeUnmount(() => window.removeEventListener('beforeunload', onBeforeUnload))
|
window.addEventListener('beforeunload', onBeforeUnload)
|
||||||
|
window.addEventListener(ML_LOCALE_CHANGE, onMLLocale)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('beforeunload', onBeforeUnload)
|
||||||
|
window.removeEventListener(ML_LOCALE_CHANGE, onMLLocale)
|
||||||
|
})
|
||||||
|
|
||||||
// The header's last breadcrumb is this record's title (design: Top header).
|
// The header's last breadcrumb is this record's title (design: Top header).
|
||||||
watchEffect(() => setRecordCrumb(loading.value ? '' : title.value))
|
watchEffect(() => setRecordCrumb(loading.value ? '' : title.value))
|
||||||
|
|||||||
@@ -553,4 +553,81 @@ describe('password and preset on the form (UI-SPEC S7, D-19, D-27 G7)', () => {
|
|||||||
await field(update.wrapper, 'name').setValue('Ada King')
|
await field(update.wrapper, 'name').setValue('Ada King')
|
||||||
expect(field(update.wrapper, 'slug').element.value).toBe('ada-lovelace')
|
expect(field(update.wrapper, 'slug').element.value).toBe('ada-lovelace')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** The roster schema with en/pl content locales and name/slug retyped. */
|
||||||
|
function mlSchema(nameType: string, slugType: string) {
|
||||||
|
const envelope = clone(rosterFormSchemaFixture)
|
||||||
|
envelope.data.meta.enabledLocales = ['en', 'pl']
|
||||||
|
for (const item of envelope.data.fields) {
|
||||||
|
if (item.name === 'name') {
|
||||||
|
item.type = nameType
|
||||||
|
} else if (item.name === 'slug') {
|
||||||
|
item.type = slugType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { body: envelope }
|
||||||
|
}
|
||||||
|
const editor = (wrapper: Mounted, name: string) => wrapper.find<HTMLInputElement>(`#field-${name}-editor`)
|
||||||
|
const localeSelect = (wrapper: Mounted, name: string) => wrapper.find<HTMLSelectElement>(`[data-field="${name}"] [data-ml-locale]`)
|
||||||
|
const invalidCreate = { status: 422, body: { error: { code: 'validation_failed', message: 'Validation failed', details: {} } } }
|
||||||
|
|
||||||
|
it('follows an mltext source per locale on create, and stops only a locale edited by hand', async () => {
|
||||||
|
const { wrapper, calls } = await mountApp(
|
||||||
|
'/acme/roster/people/create',
|
||||||
|
people({ [`GET ${PEOPLE}/schema/form`]: mlSchema('mltext', 'mltext'), [`POST ${PEOPLE}`]: invalidCreate }),
|
||||||
|
)
|
||||||
|
await editor(wrapper, 'name').setValue('Zażółć gęślą')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('za-g-l')
|
||||||
|
|
||||||
|
await localeSelect(wrapper, 'name').setValue('pl')
|
||||||
|
await editor(wrapper, 'name').setValue('Witaj Świecie')
|
||||||
|
expect(localeSelect(wrapper, 'slug').element.value).toBe('pl')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('witaj-wiecie')
|
||||||
|
|
||||||
|
await localeSelect(wrapper, 'name').setValue('en')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('za-g-l')
|
||||||
|
|
||||||
|
// A hand edit of the pl slug stops pl only.
|
||||||
|
await localeSelect(wrapper, 'slug').setValue('pl')
|
||||||
|
await editor(wrapper, 'slug').setValue('reczny')
|
||||||
|
await editor(wrapper, 'name').setValue('Inny Tytuł')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('reczny')
|
||||||
|
await localeSelect(wrapper, 'name').setValue('en')
|
||||||
|
await editor(wrapper, 'name').setValue('Hello World')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('hello-world')
|
||||||
|
|
||||||
|
await save(wrapper)
|
||||||
|
const body = (await requestsTo(calls, 'POST', PEOPLE)[0]!.clone().json()) as Record<string, unknown>
|
||||||
|
expect(body.name).toEqual({ en: 'Hello World', pl: 'Inny Tytuł' })
|
||||||
|
expect(body.slug).toEqual({ en: 'hello-world', pl: 'reczny' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not follow an mltext source on update', async () => {
|
||||||
|
const record = clone(rosterRecordFixture)
|
||||||
|
record.data.name = { en: 'Ada Lovelace', pl: '' }
|
||||||
|
record.data.slug = { en: 'ada-lovelace', pl: '' }
|
||||||
|
const { wrapper } = await mountApp('/acme/roster/people/1', people({ [`GET ${PEOPLE}/schema/form`]: mlSchema('mltext', 'mltext'), [`GET ${PERSON}`]: { body: record } }))
|
||||||
|
await editor(wrapper, 'name').setValue('Ada King')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('ada-lovelace')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('drives a text slug from the active locale of an mltext name, until edited', async () => {
|
||||||
|
const { wrapper } = await mountApp('/acme/roster/people/create', people({ [`GET ${PEOPLE}/schema/form`]: mlSchema('mltext', 'text') }))
|
||||||
|
await editor(wrapper, 'name').setValue('Hello World')
|
||||||
|
expect(field(wrapper, 'slug').element.value).toBe('hello-world')
|
||||||
|
await localeSelect(wrapper, 'name').setValue('pl')
|
||||||
|
await editor(wrapper, 'name').setValue('Witaj Świecie')
|
||||||
|
expect(field(wrapper, 'slug').element.value).toBe('witaj-wiecie')
|
||||||
|
await field(wrapper, 'slug').setValue('by-hand')
|
||||||
|
await editor(wrapper, 'name').setValue('Inny')
|
||||||
|
expect(field(wrapper, 'slug').element.value).toBe('by-hand')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fills only the active locale of an mltext slug from a text name', async () => {
|
||||||
|
const { wrapper } = await mountApp('/acme/roster/people/create', people({ [`GET ${PEOPLE}/schema/form`]: mlSchema('text', 'mltext') }))
|
||||||
|
await field(wrapper, 'name').setValue('Hello World')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('hello-world')
|
||||||
|
await localeSelect(wrapper, 'slug').setValue('pl')
|
||||||
|
expect(editor(wrapper, 'slug').element.value).toBe('')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { afterEach, describe, expect, it } from 'vitest'
|
import { afterEach, describe, expect, it } from 'vitest'
|
||||||
import {
|
import {
|
||||||
|
changedLocales,
|
||||||
contextAllows,
|
contextAllows,
|
||||||
DEFAULT_TAB,
|
DEFAULT_TAB,
|
||||||
editablePayload,
|
editablePayload,
|
||||||
@@ -8,12 +9,14 @@ import {
|
|||||||
initialValues,
|
initialValues,
|
||||||
mergeMLValue,
|
mergeMLValue,
|
||||||
panelDomId,
|
panelDomId,
|
||||||
|
presetUpdates,
|
||||||
presetValue,
|
presetValue,
|
||||||
snapshot,
|
snapshot,
|
||||||
tabDomId,
|
tabDomId,
|
||||||
tabOf,
|
tabOf,
|
||||||
} from '../../src/components/form/formState'
|
} from '../../src/components/form/formState'
|
||||||
import { extensionFormSchemaFixture, formField, formSchemaFixture, rosterFormSchemaFixture } from '../fixtures/typed'
|
import { extensionFormSchemaFixture, formField, formSchemaFixture, rosterFormSchemaFixture } from '../fixtures/typed'
|
||||||
|
import type { FormField } from '../../src/api/types'
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
document.body.innerHTML = ''
|
document.body.innerHTML = ''
|
||||||
@@ -180,6 +183,76 @@ describe('preset values (D-27 G7)', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('presets on mltext fields', () => {
|
||||||
|
function presetFields(sourceType: string, targetType: string, presetType = 'slug'): FormField[] {
|
||||||
|
const source = formField('name')
|
||||||
|
source.type = sourceType
|
||||||
|
const target = formField('name')
|
||||||
|
target.name = 'slug'
|
||||||
|
target.type = targetType
|
||||||
|
target.preset = { field: 'name', type: presetType }
|
||||||
|
return [source, target]
|
||||||
|
}
|
||||||
|
const never = () => false
|
||||||
|
|
||||||
|
it('lists the locales whose text changed', () => {
|
||||||
|
expect(changedLocales({ en: '', pl: '' }, { en: 'A', pl: '' })).toEqual(['en'])
|
||||||
|
expect(changedLocales({ en: 'A', pl: 'B' }, { en: 'A', pl: 'B' })).toEqual([])
|
||||||
|
expect(changedLocales({ en: '' }, { en: '', pl: 'B' })).toEqual(['pl'])
|
||||||
|
expect(changedLocales({ en: 'A', pl: '' }, { en: 'A' })).toEqual([])
|
||||||
|
expect(changedLocales(undefined, { en: 'A' })).toEqual(['en'])
|
||||||
|
expect(changedLocales('text', { en: 'A', pl: '' })).toEqual(['en'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps text -> text as before, and leaves an edited target out', () => {
|
||||||
|
const fields = presetFields('text', 'text')
|
||||||
|
expect(presetUpdates(fields, 'name', '', 'Hello World', {}, 'en', never)).toEqual({ slug: 'hello-world' })
|
||||||
|
expect(presetUpdates(fields, 'name', '', 'Hello World', {}, 'en', (target) => target === 'slug')).toEqual({})
|
||||||
|
expect(presetUpdates(fields, 'slug', '', 'x', {}, 'en', never)).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('follows ML -> ML per locale and stops only an edited locale', () => {
|
||||||
|
const fields = presetFields('mltext', 'mltext')
|
||||||
|
const values = { name: { en: '', pl: '' }, slug: { en: '', pl: 'kept' } }
|
||||||
|
expect(presetUpdates(fields, 'name', { en: '', pl: '' }, { en: 'Hello World', pl: '' }, values, 'en', never)).toEqual({
|
||||||
|
slug: { en: 'hello-world', pl: 'kept' },
|
||||||
|
})
|
||||||
|
const later = { name: { en: 'Hello World', pl: '' }, slug: { en: 'hello-world', pl: '' } }
|
||||||
|
expect(presetUpdates(fields, 'name', later.name, { en: 'Hello World', pl: 'Witaj Świecie' }, later, 'pl', never)).toEqual({
|
||||||
|
slug: { en: 'hello-world', pl: 'witaj-wiecie' },
|
||||||
|
})
|
||||||
|
const plEdited = (target: string, locale?: string) => target === 'slug' && locale === 'pl'
|
||||||
|
expect(presetUpdates(fields, 'name', later.name, { en: 'Hello World', pl: 'Witaj' }, later, 'pl', plEdited)).toEqual({})
|
||||||
|
expect(presetUpdates(fields, 'name', later.name, { en: 'Other', pl: '' }, later, 'pl', plEdited)).toEqual({
|
||||||
|
slug: { en: 'other', pl: '' },
|
||||||
|
})
|
||||||
|
const exact = presetFields('mltext', 'mltext', 'exact')
|
||||||
|
expect(presetUpdates(exact, 'name', {}, { en: 'Hello World' }, {}, 'en', never)).toEqual({ slug: { en: 'Hello World' } })
|
||||||
|
expect(presetUpdates(fields, 'name', { en: 'A' }, { en: 'A' }, values, 'en', never)).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('drives a text target from the ML source active locale', () => {
|
||||||
|
const fields = presetFields('mltext', 'text')
|
||||||
|
expect(presetUpdates(fields, 'name', {}, { en: 'Hello World', pl: 'Witaj' }, {}, 'en', never)).toEqual({ slug: 'hello-world' })
|
||||||
|
expect(presetUpdates(fields, 'name', {}, { en: 'Hello World', pl: 'Witaj' }, {}, 'pl', never)).toEqual({ slug: 'witaj' })
|
||||||
|
expect(presetUpdates(fields, 'name', {}, { en: 'Hello World' }, {}, 'de', never)).toEqual({ slug: '' })
|
||||||
|
expect(presetUpdates(fields, 'name', {}, { en: 'Hello World' }, {}, 'en', (target) => target === 'slug')).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('writes only the active locale of an ML target from a text source', () => {
|
||||||
|
const fields = presetFields('text', 'mltext')
|
||||||
|
const values = { slug: { en: '', pl: 'kept' } }
|
||||||
|
expect(presetUpdates(fields, 'name', '', 'Hello World', values, 'en', never)).toEqual({ slug: { en: 'hello-world', pl: 'kept' } })
|
||||||
|
expect(presetUpdates(fields, 'name', '', 'Hello World', values, 'en', (target, locale) => target === 'slug' && locale === 'en')).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores a preset on any other target type', () => {
|
||||||
|
for (const type of ['mlmarkdown', 'checkbox', 'markdown']) {
|
||||||
|
expect(presetUpdates(presetFields('mltext', type), 'name', {}, { en: 'A' }, {}, 'en', never)).toEqual({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('ML create seed and adopt merge', () => {
|
describe('ML create seed and adopt merge', () => {
|
||||||
it('seeds every enabled locale as an empty string', () => {
|
it('seeds every enabled locale as an empty string', () => {
|
||||||
const fields = [
|
const fields = [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
9
modules/boardwalk/dist/assets/index-DPCIeFlT.js
vendored
Normal file
9
modules/boardwalk/dist/assets/index-DPCIeFlT.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
modules/boardwalk/dist/index.html
vendored
2
modules/boardwalk/dist/index.html
vendored
@@ -6,7 +6,7 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="summer-admin-base" content="__SUMMER_ADMIN_BASE__" />
|
<meta name="summer-admin-base" content="__SUMMER_ADMIN_BASE__" />
|
||||||
<title>SummerCMS</title>
|
<title>SummerCMS</title>
|
||||||
<script type="module" crossorigin src="./assets/index-7UEAlsFO.js"></script>
|
<script type="module" crossorigin src="./assets/index-DPCIeFlT.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="./assets/index-BUCXRqcT.css">
|
<link rel="stylesheet" crossorigin href="./assets/index-BUCXRqcT.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Reference in New Issue
Block a user