fix(ui): should not show publish specific locale button when no localized fields exist (#13459)
### What? Hides the `Publish in [specific locale]` button on collections and globals when no localized fields are present. ### Why? Currently, the `Publish in [specific locale]` shows on every collection/global as long as `localization` is enabled in the Payload config, however this is not necessary when the collection/global has no localized fields. ### How? Checks that localized fields exist prior to rendering the `Publish in [specific locale]` button. Fixes #13447 --------- Co-authored-by: German Jablonski <43938777+GermanJablo@users.noreply.github.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import type { PublishButtonClientProps } from 'payload'
|
|||||||
|
|
||||||
import { useModal } from '@faceless-ui/modal'
|
import { useModal } from '@faceless-ui/modal'
|
||||||
import * as qs from 'qs-esm'
|
import * as qs from 'qs-esm'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { useForm, useFormModified } from '../../forms/Form/context.js'
|
import { useForm, useFormModified } from '../../forms/Form/context.js'
|
||||||
import { FormSubmit } from '../../forms/Submit/index.js'
|
import { FormSubmit } from '../../forms/Submit/index.js'
|
||||||
@@ -15,6 +15,7 @@ import { useEditDepth } from '../../providers/EditDepth/index.js'
|
|||||||
import { useLocale } from '../../providers/Locale/index.js'
|
import { useLocale } from '../../providers/Locale/index.js'
|
||||||
import { useOperation } from '../../providers/Operation/index.js'
|
import { useOperation } from '../../providers/Operation/index.js'
|
||||||
import { useTranslation } from '../../providers/Translation/index.js'
|
import { useTranslation } from '../../providers/Translation/index.js'
|
||||||
|
import { traverseForLocalizedFields } from '../../utilities/traverseForLocalizedFields.js'
|
||||||
import { PopupList } from '../Popup/index.js'
|
import { PopupList } from '../Popup/index.js'
|
||||||
import { ScheduleDrawer } from './ScheduleDrawer/index.js'
|
import { ScheduleDrawer } from './ScheduleDrawer/index.js'
|
||||||
|
|
||||||
@@ -86,6 +87,15 @@ export function PublishButton({ label: labelProp }: PublishButtonClientProps) {
|
|||||||
(hasAutosave || !modified),
|
(hasAutosave || !modified),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [hasLocalizedFields, setHasLocalizedFields] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const hasLocalizedField = traverseForLocalizedFields(entityConfig?.fields)
|
||||||
|
setHasLocalizedFields(hasLocalizedField)
|
||||||
|
}, [entityConfig?.fields])
|
||||||
|
|
||||||
|
const canPublishSpecificLocale = localization && hasLocalizedFields && hasPublishPermission
|
||||||
|
|
||||||
const operation = useOperation()
|
const operation = useOperation()
|
||||||
|
|
||||||
const disabled = operation === 'update' && !modified
|
const disabled = operation === 'update' && !modified
|
||||||
@@ -213,7 +223,7 @@ export function PublishButton({ label: labelProp }: PublishButtonClientProps) {
|
|||||||
onClick={defaultPublish}
|
onClick={defaultPublish}
|
||||||
size="medium"
|
size="medium"
|
||||||
SubMenuPopupContent={
|
SubMenuPopupContent={
|
||||||
localization || canSchedulePublish
|
canPublishSpecificLocale || canSchedulePublish
|
||||||
? ({ close }) => {
|
? ({ close }) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@@ -227,7 +237,7 @@ export function PublishButton({ label: labelProp }: PublishButtonClientProps) {
|
|||||||
</PopupList.Button>
|
</PopupList.Button>
|
||||||
</PopupList.ButtonGroup>
|
</PopupList.ButtonGroup>
|
||||||
)}
|
)}
|
||||||
{localization && canPublish && (
|
{canPublishSpecificLocale && (
|
||||||
<PopupList.ButtonGroup>
|
<PopupList.ButtonGroup>
|
||||||
<PopupList.Button id="publish-locale" onClick={secondaryPublish}>
|
<PopupList.Button id="publish-locale" onClick={secondaryPublish}>
|
||||||
{secondaryLabel}
|
{secondaryLabel}
|
||||||
|
|||||||
45
packages/ui/src/utilities/traverseForLocalizedFields.ts
Normal file
45
packages/ui/src/utilities/traverseForLocalizedFields.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import type { ClientField } from 'payload'
|
||||||
|
|
||||||
|
export const traverseForLocalizedFields = (fields: ClientField[]): boolean => {
|
||||||
|
for (const field of fields) {
|
||||||
|
if ('localized' in field && field.localized) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field.type) {
|
||||||
|
case 'array':
|
||||||
|
case 'collapsible':
|
||||||
|
case 'group':
|
||||||
|
case 'row':
|
||||||
|
if (field.fields && traverseForLocalizedFields(field.fields)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'blocks':
|
||||||
|
if (field.blocks) {
|
||||||
|
for (const block of field.blocks) {
|
||||||
|
if (block.fields && traverseForLocalizedFields(block.fields)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'tabs':
|
||||||
|
if (field.tabs) {
|
||||||
|
for (const tab of field.tabs) {
|
||||||
|
if ('localized' in tab && tab.localized) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ('fields' in tab && tab.fields && traverseForLocalizedFields(tab.fields)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -4,6 +4,9 @@ export const noLocalizedFieldsCollectionSlug = 'no-localized-fields'
|
|||||||
|
|
||||||
export const NoLocalizedFieldsCollection: CollectionConfig = {
|
export const NoLocalizedFieldsCollection: CollectionConfig = {
|
||||||
slug: noLocalizedFieldsCollectionSlug,
|
slug: noLocalizedFieldsCollectionSlug,
|
||||||
|
versions: {
|
||||||
|
drafts: true,
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'text',
|
name: 'text',
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
|||||||
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||||
import { arrayCollectionSlug } from './collections/Array/index.js'
|
import { arrayCollectionSlug } from './collections/Array/index.js'
|
||||||
import { nestedToArrayAndBlockCollectionSlug } from './collections/NestedToArrayAndBlock/index.js'
|
import { nestedToArrayAndBlockCollectionSlug } from './collections/NestedToArrayAndBlock/index.js'
|
||||||
|
import { noLocalizedFieldsCollectionSlug } from './collections/NoLocalizedFields/index.js'
|
||||||
import { richTextSlug } from './collections/RichText/index.js'
|
import { richTextSlug } from './collections/RichText/index.js'
|
||||||
import {
|
import {
|
||||||
arrayWithFallbackCollectionSlug,
|
arrayWithFallbackCollectionSlug,
|
||||||
@@ -61,6 +62,7 @@ let urlCannotCreateDefaultLocale: AdminUrlUtil
|
|||||||
let urlPostsWithDrafts: AdminUrlUtil
|
let urlPostsWithDrafts: AdminUrlUtil
|
||||||
let urlArray: AdminUrlUtil
|
let urlArray: AdminUrlUtil
|
||||||
let arrayWithFallbackURL: AdminUrlUtil
|
let arrayWithFallbackURL: AdminUrlUtil
|
||||||
|
let noLocalizedFieldsURL: AdminUrlUtil
|
||||||
|
|
||||||
const title = 'english title'
|
const title = 'english title'
|
||||||
const spanishTitle = 'spanish title'
|
const spanishTitle = 'spanish title'
|
||||||
@@ -87,6 +89,7 @@ describe('Localization', () => {
|
|||||||
urlPostsWithDrafts = new AdminUrlUtil(serverURL, localizedDraftsSlug)
|
urlPostsWithDrafts = new AdminUrlUtil(serverURL, localizedDraftsSlug)
|
||||||
urlArray = new AdminUrlUtil(serverURL, arrayCollectionSlug)
|
urlArray = new AdminUrlUtil(serverURL, arrayCollectionSlug)
|
||||||
arrayWithFallbackURL = new AdminUrlUtil(serverURL, arrayWithFallbackCollectionSlug)
|
arrayWithFallbackURL = new AdminUrlUtil(serverURL, arrayWithFallbackCollectionSlug)
|
||||||
|
noLocalizedFieldsURL = new AdminUrlUtil(serverURL, noLocalizedFieldsCollectionSlug)
|
||||||
|
|
||||||
context = await browser.newContext()
|
context = await browser.newContext()
|
||||||
page = await context.newPage()
|
page = await context.newPage()
|
||||||
@@ -671,6 +674,13 @@ describe('Localization', () => {
|
|||||||
await expect(page.locator('#field-title')).toBeEmpty()
|
await expect(page.locator('#field-title')).toBeEmpty()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should not show publish specific locale button when no localized fields exist', async () => {
|
||||||
|
await page.goto(urlPostsWithDrafts.create)
|
||||||
|
await expect(page.locator('#publish-locale')).toHaveCount(1)
|
||||||
|
await page.goto(noLocalizedFieldsURL.create)
|
||||||
|
await expect(page.locator('#publish-locale')).toHaveCount(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function createLocalizedArrayItem(page: Page, url: AdminUrlUtil) {
|
async function createLocalizedArrayItem(page: Page, url: AdminUrlUtil) {
|
||||||
|
|||||||
Reference in New Issue
Block a user