* chore: ensures relationship fields react to locale changes in the admin panel - fixes #1870 * chore: patches in default values for fields, and localized fields using fallbacks - fixes #1859 * chore: organizes field localization and sanitizing * Revert "Feat/1180 loading UI enhancements" * Feat/1180 loading UI enhancements * chore: safely sets tab if name field, only sets fallback value if it exists * chore: adds test to ensure text fields use fallback locale value when empty
This commit is contained in:
@@ -26,6 +26,7 @@ import { GetFilterOptions } from '../../../utilities/GetFilterOptions';
|
||||
import { SingleValue } from './select-components/SingleValue';
|
||||
import { MultiValueLabel } from './select-components/MultiValueLabel';
|
||||
import { DocumentDrawerProps } from '../../../elements/DocumentDrawer/types';
|
||||
import { useLocale } from '../../../utilities/Locale';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
@@ -64,6 +65,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
|
||||
const { t, i18n } = useTranslation('fields');
|
||||
const { permissions } = useAuth();
|
||||
const locale = useLocale();
|
||||
const formProcessing = useFormProcessing();
|
||||
const hasMultipleRelations = Array.isArray(relationTo);
|
||||
const [options, dispatchOptions] = useReducer(optionsReducer, []);
|
||||
@@ -140,7 +142,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
limit: maxResultsPerRequest,
|
||||
page: lastLoadedPageToUse,
|
||||
sort: fieldToSearch,
|
||||
locale: i18n.language,
|
||||
locale,
|
||||
depth: 0,
|
||||
};
|
||||
|
||||
@@ -203,6 +205,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
api,
|
||||
t,
|
||||
i18n,
|
||||
locale,
|
||||
]);
|
||||
|
||||
const updateSearch = useDebouncedCallback((searchArg: string, valueArg: unknown) => {
|
||||
@@ -242,7 +245,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
},
|
||||
},
|
||||
depth: 0,
|
||||
locale: i18n.language,
|
||||
locale,
|
||||
limit: idsToLoad.length,
|
||||
};
|
||||
|
||||
@@ -274,6 +277,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
api,
|
||||
i18n,
|
||||
relationTo,
|
||||
locale,
|
||||
]);
|
||||
|
||||
// Determine if we should switch to word boundary search
|
||||
@@ -287,7 +291,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
setEnableWordBoundarySearch(!isIdOnly);
|
||||
}, [relationTo, collections]);
|
||||
|
||||
// When relationTo or filterOptionsResult changes, reset component
|
||||
// When (`relationTo` || `filterOptionsResult` || `locale`) changes, reset component
|
||||
// Note - effect should not run on first run
|
||||
useEffect(() => {
|
||||
if (firstRun.current) {
|
||||
@@ -299,7 +303,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
setLastFullyLoadedRelation(-1);
|
||||
setLastLoadedPage(1);
|
||||
setHasLoadedFirstPage(false);
|
||||
}, [relationTo, filterOptionsResult]);
|
||||
}, [relationTo, filterOptionsResult, locale]);
|
||||
|
||||
const onSave = useCallback<DocumentDrawerProps['onSave']>((args) => {
|
||||
dispatchOptions({ type: 'UPDATE', doc: args.doc, collection: args.collectionConfig, i18n });
|
||||
@@ -373,7 +377,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
sort: false,
|
||||
});
|
||||
}}
|
||||
value={valueToRender}
|
||||
value={valueToRender ?? null}
|
||||
showError={showError}
|
||||
disabled={formProcessing}
|
||||
options={options}
|
||||
|
||||
@@ -46,22 +46,49 @@ export const promise = async ({
|
||||
delete siblingDoc[field.name];
|
||||
}
|
||||
|
||||
const hasLocalizedValue = flattenLocales
|
||||
const shouldHoistLocalizedValue = flattenLocales
|
||||
&& fieldAffectsData(field)
|
||||
&& (typeof siblingDoc[field.name] === 'object' && siblingDoc[field.name] !== null)
|
||||
&& field.localized
|
||||
&& req.locale !== 'all';
|
||||
|
||||
if (hasLocalizedValue) {
|
||||
let localizedValue = siblingDoc[field.name][req.locale];
|
||||
if (typeof localizedValue === 'undefined' && req.fallbackLocale) localizedValue = siblingDoc[field.name][req.fallbackLocale];
|
||||
if (localizedValue === null && (field.type === 'array' || field.type === 'blocks')) localizedValue = siblingDoc[field.name][req.fallbackLocale];
|
||||
if (typeof localizedValue === 'undefined' && (field.type === 'group' || field.type === 'tab')) localizedValue = {};
|
||||
if (typeof localizedValue === 'undefined') localizedValue = null;
|
||||
siblingDoc[field.name] = localizedValue;
|
||||
if (shouldHoistLocalizedValue) {
|
||||
// replace actual value with localized value before sanitizing
|
||||
// { [locale]: fields } -> fields
|
||||
const { locale } = req;
|
||||
const value = siblingDoc[field.name][locale];
|
||||
const fallbackLocale = req.payload.config.localization && req.payload.config.localization?.fallback && req.fallbackLocale;
|
||||
|
||||
let hoistedValue = value;
|
||||
|
||||
if (fallbackLocale && fallbackLocale !== locale) {
|
||||
const fallbackValue = siblingDoc[field.name][fallbackLocale];
|
||||
const isNullOrUndefined = typeof value === 'undefined' || value === null;
|
||||
|
||||
if (fallbackValue) {
|
||||
switch (field.type) {
|
||||
case 'text':
|
||||
case 'textarea': {
|
||||
if (value === '' || isNullOrUndefined) {
|
||||
hoistedValue = fallbackValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (isNullOrUndefined) {
|
||||
hoistedValue = fallbackValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
siblingDoc[field.name] = hoistedValue;
|
||||
}
|
||||
|
||||
// Sanitize outgoing data
|
||||
// Sanitize outgoing field value
|
||||
switch (field.type) {
|
||||
case 'group': {
|
||||
// Fill groups with empty objects so fields with hooks within groups can populate
|
||||
@@ -74,7 +101,7 @@ export const promise = async ({
|
||||
}
|
||||
case 'tabs': {
|
||||
field.tabs.forEach((tab) => {
|
||||
if (tabHasName(tab) && typeof siblingDoc[tab.name] === 'undefined') {
|
||||
if (tabHasName(tab) && (typeof siblingDoc[tab.name] === 'undefined' || siblingDoc[tab.name] === null)) {
|
||||
siblingDoc[tab.name] = {};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -98,6 +98,28 @@ describe('Localization', () => {
|
||||
expect(localized.title.es).toEqual(spanishTitle);
|
||||
});
|
||||
|
||||
it('should fallback to english translation when empty', async () => {
|
||||
const updated = await payload.update<LocalizedPost>({
|
||||
collection,
|
||||
id: post1.id,
|
||||
locale: spanishLocale,
|
||||
data: {
|
||||
title: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(updated.title).toEqual(englishTitle);
|
||||
|
||||
const localizedFallback = await payload.findByID<LocalizedPostAllLocale>({
|
||||
collection,
|
||||
id: post1.id,
|
||||
locale: 'all',
|
||||
});
|
||||
|
||||
expect(localizedFallback.title.en).toEqual(englishTitle);
|
||||
expect(localizedFallback.title.es).toEqual('');
|
||||
});
|
||||
|
||||
describe('querying', () => {
|
||||
let localizedPost: LocalizedPost;
|
||||
beforeEach(async () => {
|
||||
|
||||
Reference in New Issue
Block a user