Skip to content

Commit

Permalink
fix(i18n): allow locale plugins to translate "All fields" field group (
Browse files Browse the repository at this point in the history
…#7117)

* fix(i18n): allow translating field group titles

* chore(test-studio): add field group translation debug

* chore(e2e): simplify e2e studio config

* test(i18n): add test for field group translations
  • Loading branch information
rexxars committed Jul 11, 2024
1 parent ff31556 commit 24f9936
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 39 deletions.
12 changes: 12 additions & 0 deletions dev/studio-e2e-testing/i18n/bundles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {defineLocaleResourceBundle} from 'sanity'
import {testStudioLocaleNamespace} from 'sanity-test-studio/locales'

export const e2eI18nBundles = [
defineLocaleResourceBundle({
locale: 'en-US',
namespace: testStudioLocaleNamespace,
resources: {
'field-groups.group-1': '🇺🇸 Group 1',
},
}),
]
29 changes: 15 additions & 14 deletions dev/studio-e2e-testing/sanity.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {googleMapsInput} from '@sanity/google-maps-input'
import {BookIcon} from '@sanity/icons'
import {visionTool} from '@sanity/vision'
import {defineConfig, definePlugin} from 'sanity'
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {muxInput} from 'sanity-plugin-mux-input'
import {imageAssetSource} from 'sanity-test-studio/assetSources'
Expand All @@ -16,10 +16,18 @@ import {presenceTool} from 'sanity-test-studio/plugins/presence'
import {defaultDocumentNode, newDocumentOptions, structure} from 'sanity-test-studio/structure'

import {customComponents} from './components-api'
import {e2eI18nBundles} from './i18n/bundles'
import {schemaTypes} from './schemaTypes'

const sharedSettings = definePlugin({
name: 'sharedSettings',
export default defineConfig({
name: 'default',
title: 'studio-e2e-testing',

projectId: process.env.SANITY_E2E_PROJECT_ID!,
dataset: process.env.SANITY_E2E_DATASET!,

basePath: '/test',

schema: {
types: schemaTypes,
templates: resolveInitialValueTemplates,
Expand All @@ -30,6 +38,10 @@ const sharedSettings = definePlugin({
},
},

i18n: {
bundles: e2eI18nBundles,
},

document: {
actions: documentActions,
inspectors: (prev, ctx) => {
Expand Down Expand Up @@ -90,14 +102,3 @@ const sharedSettings = definePlugin({
},
},
})

export default defineConfig({
name: 'default',
title: 'studio-e2e-testing',

projectId: process.env.SANITY_E2E_PROJECT_ID!,
dataset: process.env.SANITY_E2E_DATASET!,

plugins: [sharedSettings()],
basePath: '/test',
})
18 changes: 17 additions & 1 deletion dev/test-studio/locales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const enUSStrings = {
'translate.with-formatter': 'This value has a list-formatter: {{countries, list}}',
'use-translation.with-html': 'Apparently, <code>code</code> is an HTML element?',
'use-translation.interpolation-example': 'This has {{ spaces }} around it, this one {{doesNot}}',

// Used by `fieldGroupsWithI18n` debug schema type
'field-groups.group-1': '🇺🇸 Group 1',
'field-groups.group-2': '🇺🇸 Group 2',
}

const enUS = defineLocaleResourceBundle({
Expand All @@ -29,6 +33,10 @@ const nbNO = defineLocaleResourceBundle({
'translate.with-xml-in-value':
'Denne verdien har XML i en interpolert verdi: <strong>{{value}}</strong>',
'use-translation.with-html': 'Faktisk er <code>code</code> et HTML-element?',

// Used by `fieldGroupsWithI18n` debug schema type
'field-groups.group-1': '🇳🇴 Gruppe 1',
'field-groups.group-2': '🇳🇴 Gruppe 2',
},
})

Expand All @@ -40,6 +48,14 @@ const nbNOBStructureOverrides = defineLocaleResourceBundle({
},
})

const enUSStudioOverrides = defineLocaleResourceBundle({
locale: 'en-US',
namespace: 'studio',
resources: {
//'inputs.object.field-group-tabs.all-fields-title': 'அனைத்து துறைகள்',
},
})

export type TestStudioLocaleResourceKeys = keyof typeof enUSStrings

export const testStudioLocaleBundles = [enUS, nbNO, nbNOBStructureOverrides]
export const testStudioLocaleBundles = [enUS, nbNO, nbNOBStructureOverrides, enUSStudioOverrides]
32 changes: 32 additions & 0 deletions dev/test-studio/schema/debug/fieldGroupsWithI18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {defineType} from 'sanity'

import {testStudioLocaleNamespace} from '../../locales'

export default defineType({
name: 'fieldGroupsWithI18n',
title: 'With i18n',
type: 'document',
groups: [
{
name: 'i18n-group1',
title: 'I18N-MISSING (1)',
i18n: {title: {key: 'field-groups.group-1', ns: testStudioLocaleNamespace}},
},
{
name: 'i18n-group2',
title: 'I18N-MISSING (2)',
i18n: {title: {key: 'intentionally-missing-key', ns: testStudioLocaleNamespace}},
},
{
name: 'non-i18n-group3',
title: '🌐 Non-i18n group',
},
],
fields: [
{name: 'field1', type: 'string', group: 'i18n-group1'},
{name: 'field2', type: 'string', group: 'i18n-group2'},
{name: 'field3', type: 'string', group: 'i18n-group1'},
{name: 'field4', type: 'string', group: ['i18n-group1', 'i18n-group2']},
{name: 'field5', type: 'string', group: 'non-i18n-group3'},
],
})
2 changes: 2 additions & 0 deletions dev/test-studio/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import fieldGroupsDefault from './debug/fieldGroupsDefault'
import fieldGroupsMany from './debug/fieldGroupsMany'
import fieldGroupsWithFieldsets from './debug/fieldGroupsWithFieldsets'
import fieldGroupsWithFieldsetsAndValidation from './debug/fieldGroupsWithFieldsetsAndValidation'
import fieldGroupsWithI18n from './debug/fieldGroupsWithI18n'
import fieldGroupsWithValidation from './debug/fieldGroupsWithValidation'
import fieldsets from './debug/fieldsets'
import {
Expand Down Expand Up @@ -249,6 +250,7 @@ export const schemaTypes = [
fieldGroups,
fieldGroupsDefault,
fieldGroupsMany,
fieldGroupsWithI18n,
fieldGroupsWithValidation,
fieldGroupsWithFieldsetsAndValidation,
virtualizationInObject,
Expand Down
1 change: 1 addition & 0 deletions dev/test-studio/structure/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const DEBUG_FIELD_GROUP_TYPES = [
'fieldGroups',
'fieldGroupsDefault',
'fieldGroupsMany',
'fieldGroupsWithI18n',
'fieldGroupsWithValidation',
'fieldGroupsWithFieldsets',
'fieldGroupsWithFieldsetsAndValidation',
Expand Down
3 changes: 2 additions & 1 deletion packages/@sanity/types/src/schema/definition/type/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {type ComponentType, type ReactElement, type ReactNode} from 'react'

import {type ConditionalProperty, type DeprecatedProperty} from '../../types'
import {type ConditionalProperty, type DeprecatedProperty, type I18nTextRecord} from '../../types'
import {type ObjectOptions} from './object'

/** @public */
Expand All @@ -21,6 +21,7 @@ export type FieldGroupDefinition = {
hidden?: ConditionalProperty
icon?: ComponentType
default?: boolean
i18n?: I18nTextRecord<'title'>
}

/** @public */
Expand Down
3 changes: 2 additions & 1 deletion packages/@sanity/types/src/schema/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export interface SortOrderingItem {
direction: 'asc' | 'desc'
}

/** @beta */
/** @public */
export type I18nTextRecord<K extends string> = {[P in K]?: {key: string; ns: string}}

/** @beta */
Expand Down Expand Up @@ -388,6 +388,7 @@ export interface FieldGroup {
icon?: ComponentType
title?: string
description?: string
i18n?: I18nTextRecord<'title'>
hidden?: ConditionalProperty
default?: boolean
fields?: ObjectField[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,32 @@ const GroupTabs = ({
onClick,
shouldAutoFocus = true,
disabled,
}: FieldGroupTabsProps) => (
<TabList space={2} data-testid="field-group-tabs">
{groups.map((group) => {
return (
<GroupTab
aria-controls={`${inputId}-field-group-fields`}
autoFocus={shouldAutoFocus && group.selected}
disabled={disabled || group.disabled}
icon={group?.icon}
key={`${inputId}-${group.name}-tab`}
name={group.name}
onClick={onClick}
selected={Boolean(group.selected)}
title={group.title || group.name}
/>
)
})}
</TabList>
)
}: FieldGroupTabsProps) => {
const {t} = useTranslation()
return (
<TabList space={2} data-testid="field-group-tabs">
{groups.map((group) => {
const title = group.i18n?.title
? t(group.i18n.title.key, {ns: group.i18n.title.ns})
: group.title || group.name

return (
<GroupTab
aria-controls={`${inputId}-field-group-fields`}
autoFocus={shouldAutoFocus && group.selected}
disabled={disabled || group.disabled}
icon={group?.icon}
key={`${inputId}-${group.name}-tab`}
name={group.name}
onClick={onClick}
selected={Boolean(group.selected)}
title={title}
/>
)
})}
</TabList>
)
}

/* For small screens, use Select from Sanity UI */
const GroupSelect = ({
Expand Down Expand Up @@ -87,15 +94,18 @@ const GroupSelect = ({
value={groups.find((g) => g.selected)?.name}
>
{groups.map((group) => {
// Separate hidden in order to resolve it to a boolean type
const title = group.i18n?.title
? t(group.i18n.title.key, {ns: group.i18n.title.ns})
: group.title || group.name

return (
<GroupOption
aria-controls={`${inputId}-field-group-fields`}
disabled={group.disabled}
key={`${inputId}-${group.name}-tab`}
name={group.name}
selected={Boolean(group.selected)}
title={group.title || group.name}
title={title}
/>
)
})}
Expand Down
8 changes: 8 additions & 0 deletions packages/sanity/src/core/form/store/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {type FieldGroup} from '@sanity/types'

import {studioLocaleNamespace} from '../../i18n/localeNamespaces'

/**
* Max supported field depth. Fields deeper than this will be considered hidden.
*/
Expand All @@ -14,4 +16,10 @@ export const ALL_FIELDS_GROUP: FieldGroup = {
name: 'all-fields',
title: 'All fields',
hidden: false,
i18n: {
title: {
key: 'inputs.object.field-group-tabs.all-fields-title',
ns: studioLocaleNamespace,
},
},
}
1 change: 1 addition & 0 deletions packages/sanity/src/core/form/store/formState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ function prepareObjectInputState<T>(
name: group.name,
selected,
title: group.title,
i18n: group.i18n,
},
]
})
Expand Down
2 changes: 2 additions & 0 deletions packages/sanity/src/core/form/store/types/fieldGroup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {type I18nTextRecord} from '@sanity/types'
import {type ComponentType} from 'react'

/**
Expand All @@ -8,5 +9,6 @@ export interface FormFieldGroup {
selected?: boolean
disabled?: boolean
title?: string
i18n?: I18nTextRecord<'title'>
icon?: ComponentType
}
4 changes: 3 additions & 1 deletion packages/sanity/src/core/i18n/bundles/studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,9 @@ export const studioLocaleStrings = defineLocalesResources('studio', {
'inputs.invalid-value.reset-button.text': 'Reset value',
/** Invalid property value */
'inputs.invalid-value.title': 'Invalid property value',
/** Field groups */
/** Title for the "All fields" field group */
'inputs.object.field-group-tabs.all-fields-title': 'All fields',
/** Aria label for the "Field groups" select control on smaller screens */
'inputs.object.field-group-tabs.aria-label': 'Field groups',
/** Read-only field description */
'inputs.object.unknown-fields.read-only.description':
Expand Down
15 changes: 15 additions & 0 deletions test/e2e/tests/inputs/object.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {expect} from '@playwright/test'
import {test} from '@sanity/test'

test('fields groups can use/not use i18n titles', async ({page, createDraftDocument}) => {
await createDraftDocument('/test/content/input-debug;field-groups;fieldGroupsWithI18n')

await expect(await page.getByTestId(`group-tab-i18n-group1`)).toBeVisible()

// Should be translated (see e2e studio `i18n/bundles`)
await expect(page.getByTestId('group-tab-i18n-group1')).toHaveText('🇺🇸 Group 1')
// Should intentionally not be translated, eg show the missing key
await expect(page.getByTestId('group-tab-i18n-group2')).toHaveText('intentionally-missing-key')
// Should show defined title if no `i18n` key is defined
await expect(page.getByTestId('group-tab-non-i18n-group3')).toHaveText('🌐 Non-i18n group')
})

0 comments on commit 24f9936

Please sign in to comment.