Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(i18n): allow locale plugins to translate "All fields" field group #7117

Merged
merged 4 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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')
})
Loading