diff --git a/.changeset/friendly-ties-beg.md b/.changeset/friendly-ties-beg.md new file mode 100644 index 00000000000..ce870c98a11 --- /dev/null +++ b/.changeset/friendly-ties-beg.md @@ -0,0 +1,5 @@ +--- +'@sap-ux/ui-prompting': minor +--- + +Enhance the translation input to allow passing i18n annotation information. diff --git a/.changeset/late-suits-repeat.md b/.changeset/late-suits-repeat.md new file mode 100644 index 00000000000..8cc37934e1d --- /dev/null +++ b/.changeset/late-suits-repeat.md @@ -0,0 +1,5 @@ +--- +'@sap-ux-private/ui-prompting-examples': patch +--- + +Implement changes to handle i18n entry creation with i18n annotation information. diff --git a/.changeset/sixty-chairs-crash.md b/.changeset/sixty-chairs-crash.md new file mode 100644 index 00000000000..c1375dba001 --- /dev/null +++ b/.changeset/sixty-chairs-crash.md @@ -0,0 +1,5 @@ +--- +'@sap-ux/fe-fpm-writer': patch +--- + +Update the "header" question in the Table building block prompt to include i18n annotation information for the translation entry. diff --git a/examples/ui-prompting-examples/src/BuildingBlock.tsx b/examples/ui-prompting-examples/src/BuildingBlock.tsx index 1a3a32ecb7d..b924c47b4c6 100644 --- a/examples/ui-prompting-examples/src/BuildingBlock.tsx +++ b/examples/ui-prompting-examples/src/BuildingBlock.tsx @@ -184,9 +184,8 @@ export const BuildingBlockQuestions = (props: { translationProps={{ bundle: i18nBundle, onEvent: (name, event) => { - // ToDo if (event.name === 'update') { - updateBundle(name, event.entry); + updateBundle(name, event.entry, event.properties); } else { console.log( `Show entry: key:"${event.entry.key.value}" -> value:"${event.entry.value.value}"` diff --git a/examples/ui-prompting-examples/src/backend/connection.ts b/examples/ui-prompting-examples/src/backend/connection.ts index 0b808bd894d..5f3b6d7733c 100644 --- a/examples/ui-prompting-examples/src/backend/connection.ts +++ b/examples/ui-prompting-examples/src/backend/connection.ts @@ -230,7 +230,18 @@ async function handleAction(action: Actions): Promise { case CREATE_I18N_ENTRY: { if (currentApp?.projectPath) { await createI18nEntry( - [{ key: action.key, value: action.value }], + [ + { + key: action.key, + value: action.value, + annotation: action.properties + ? { + textType: action.properties.type, + note: action.properties.annotation + } + : undefined + } + ], currentApp?.projectPath, currentApp?.appId ); diff --git a/examples/ui-prompting-examples/src/utils/communication.ts b/examples/ui-prompting-examples/src/utils/communication.ts index d54023a3676..8577de6e258 100644 --- a/examples/ui-prompting-examples/src/utils/communication.ts +++ b/examples/ui-prompting-examples/src/utils/communication.ts @@ -13,7 +13,13 @@ import type { UpdateProjectPathResultPayload, ValidateAnswers } from '../addons/project/types'; -import type { DynamicChoices, PromptQuestion, ValidationResults, PromptsGroup } from '@sap-ux/ui-prompting'; +import type { + DynamicChoices, + PromptQuestion, + ValidationResults, + PromptsGroup, + TranslationProperties +} from '@sap-ux/ui-prompting'; import type { Actions, CreateI18n, GetChoices, GetQuestions, RequestI18n } from './types'; import { APPLY_ANSWERS, @@ -337,14 +343,16 @@ export function getI18nBundle(): Promise { * * @param key Key of new i18n entry * @param value Value of new i18n entry + * @param properties i18n properties * @returns Returns updated i18n bundle. */ -export function createI18n(key: string, value: string): Promise { +export function createI18n(key: string, value: string, properties?: TranslationProperties): Promise { return new Promise((resolve) => { const createAction: CreateI18n = { type: CREATE_I18N_ENTRY, key, - value + value, + properties }; sendMessage(createAction); const handleMessage = (action: Actions) => { diff --git a/examples/ui-prompting-examples/src/utils/hooks.ts b/examples/ui-prompting-examples/src/utils/hooks.ts index 9bdf6d3cbdd..4ee781e452a 100644 --- a/examples/ui-prompting-examples/src/utils/hooks.ts +++ b/examples/ui-prompting-examples/src/utils/hooks.ts @@ -6,7 +6,7 @@ import { subscribeOnChoicesUpdate, unsubscribeOnChoicesUpdate } from './communication'; -import type { DynamicChoices, PromptQuestion } from '@sap-ux/ui-prompting'; +import type { DynamicChoices, PromptQuestion, TranslationProperties } from '@sap-ux/ui-prompting'; import type { PromptsType } from './types'; import type { PromptsGroup } from '@sap-ux/ui-prompting'; import type { Answers } from 'inquirer'; @@ -105,14 +105,18 @@ function removePendingQuestions(pendingQuestions: string[], question: string): s * * @returns I18n bundle with creation function. */ -export function useI18nBundle(): [I18nBundle, (question: string, entry: TranslationEntry) => void, string[]] { +export function useI18nBundle(): [ + I18nBundle, + (question: string, entry: TranslationEntry, properties?: TranslationProperties) => void, + string[] +] { const [i18nBundle, setI18nBundle] = useState({}); const [pendingQuestions, setPendingQuestions] = useState([]); - const updateBundle = (question: string, entry: TranslationEntry) => { + const updateBundle = (question: string, entry: TranslationEntry, properties?: TranslationProperties) => { // Update pending creations pendingQuestions.push(question); setPendingQuestions([...pendingQuestions]); - createI18n(entry.key.value, entry.value.value) + createI18n(entry.key.value, entry.value.value, properties) .then((bundle: I18nBundle) => { setI18nBundle(bundle); const newPendingQuestions = removePendingQuestions(pendingQuestions, question); diff --git a/examples/ui-prompting-examples/src/utils/types.ts b/examples/ui-prompting-examples/src/utils/types.ts index 74130985880..3b79bbd4526 100644 --- a/examples/ui-prompting-examples/src/utils/types.ts +++ b/examples/ui-prompting-examples/src/utils/types.ts @@ -6,7 +6,7 @@ import { } from '@sap-ux/fe-fpm-writer'; import { PromptsType } from '@sap-ux/fe-fpm-writer/dist/prompts/types'; import type { AddonActions } from '../addons/types'; -import type { DynamicChoices } from '@sap-ux/ui-prompting'; +import type { DynamicChoices, TranslationProperties } from '@sap-ux/ui-prompting'; import type { Answers, CodeSnippet, SupportedGeneratorAnswers } from '@sap-ux/fe-fpm-writer'; import type { I18nBundle } from '@sap-ux/i18n'; @@ -117,4 +117,5 @@ export interface CreateI18n { type: typeof CREATE_I18N_ENTRY; key: string; value: string; + properties?: TranslationProperties; } diff --git a/packages/fe-fpm-writer/package.json b/packages/fe-fpm-writer/package.json index 947a166bc3b..8a0d74bd2fc 100644 --- a/packages/fe-fpm-writer/package.json +++ b/packages/fe-fpm-writer/package.json @@ -44,6 +44,7 @@ "xpath": "0.0.33" }, "devDependencies": { + "@sap-ux/i18n": "workspace:*", "@types/inquirer": "8.2.6", "@sap-ux/ui-prompting": "workspace:*", "@types/ejs": "3.1.2", diff --git a/packages/fe-fpm-writer/src/building-block/prompts/questions/table.ts b/packages/fe-fpm-writer/src/building-block/prompts/questions/table.ts index 3879ce43f43..112cdc25847 100644 --- a/packages/fe-fpm-writer/src/building-block/prompts/questions/table.ts +++ b/packages/fe-fpm-writer/src/building-block/prompts/questions/table.ts @@ -18,6 +18,7 @@ import type { PromptContext, Prompts, PromptsGroup } from '../../../prompts/type import { BuildingBlockType } from '../../types'; import type { BuildingBlockConfig, Table } from '../../types'; import { getManifestPromptsGroup } from './building-blocks'; +import { SapShortTextType } from '@sap-ux/i18n'; const MANIFEST_LIBRARIES_GROUP = getManifestPromptsGroup(); @@ -189,7 +190,10 @@ export async function getTableBuildingBlockPrompts(context: PromptContext): Prom message: t('header.message'), guiOptions: { groupId: groupIds.visualisationProperties, - translatable: true + translationProperties: { + type: SapShortTextType.TableTitle, + annotation: t('header.translationAnnotation') + } } }, { diff --git a/packages/fe-fpm-writer/src/prompts/translations/i18n.en.json b/packages/fe-fpm-writer/src/prompts/translations/i18n.en.json index 11b04c021e5..1b7958cf1b5 100644 --- a/packages/fe-fpm-writer/src/prompts/translations/i18n.en.json +++ b/packages/fe-fpm-writer/src/prompts/translations/i18n.en.json @@ -179,7 +179,11 @@ } }, "headerVisible": "Display Header", - "header": { "message": "Table Header Text", "validation": "Enter a Table Header Text" }, + "header": { + "message": "Table Header Text", + "validation": "Enter a Table Header Text", + "translationAnnotation": "Header of the table" + }, "personalization": { "message": "Table Personalization", "choices": { diff --git a/packages/fe-fpm-writer/test/unit/prompts/__snapshots__/prompts.test.ts.snap b/packages/fe-fpm-writer/test/unit/prompts/__snapshots__/prompts.test.ts.snap index 79fae17adb7..18eb89845e7 100644 --- a/packages/fe-fpm-writer/test/unit/prompts/__snapshots__/prompts.test.ts.snap +++ b/packages/fe-fpm-writer/test/unit/prompts/__snapshots__/prompts.test.ts.snap @@ -629,7 +629,10 @@ Object { Object { "guiOptions": Object { "groupId": "tableVisualizationProperties", - "translatable": true, + "translationProperties": Object { + "annotation": "Header of the table", + "type": "XTIT", + }, }, "message": "Table Header Text", "name": "buildingBlockData.header", @@ -1799,7 +1802,10 @@ Object { Object { "guiOptions": Object { "groupId": "tableVisualizationProperties", - "translatable": true, + "translationProperties": Object { + "annotation": "Header of the table", + "type": "XTIT", + }, }, "message": "Table Header Text", "name": "buildingBlockData.header", diff --git a/packages/fe-fpm-writer/tsconfig.json b/packages/fe-fpm-writer/tsconfig.json index 5cb299b10f3..53c0327c645 100644 --- a/packages/fe-fpm-writer/tsconfig.json +++ b/packages/fe-fpm-writer/tsconfig.json @@ -13,6 +13,9 @@ { "path": "../fiori-annotation-api" }, + { + "path": "../i18n" + }, { "path": "../project-access" }, diff --git a/packages/ui-prompting/package.json b/packages/ui-prompting/package.json index e553cc6b817..e42647e5e69 100644 --- a/packages/ui-prompting/package.json +++ b/packages/ui-prompting/package.json @@ -46,6 +46,7 @@ "@babel/preset-env": "7.22.20", "@babel/preset-react": "7.22.15", "@babel/preset-typescript": "7.22.15", + "@sap-ux/i18n": "workspace:*", "@sap-ux/inquirer-common": "workspace:*", "@storybook/addons": "7.6.17", "@storybook/components": "8.1.11", diff --git a/packages/ui-prompting/src/components/Inputs/Input/TranslationInput.tsx b/packages/ui-prompting/src/components/Inputs/Input/TranslationInput.tsx index 830cd88807c..2e43078b859 100644 --- a/packages/ui-prompting/src/components/Inputs/Input/TranslationInput.tsx +++ b/packages/ui-prompting/src/components/Inputs/Input/TranslationInput.tsx @@ -5,10 +5,15 @@ import { useValue, getLabelRenderer } from '../../../utilities'; import { useTranslation } from '../../../context/TranslationContext'; import type { InputProps } from './Input'; import { TRANSLATE_EVENT_UPDATE, TRANSLATE_EVENT_SHOW } from '../../../types'; +import type { TranslationProperties } from '../../../types'; -export const TranslationInput = (props: InputProps) => { +export interface TranslationInputProps extends InputProps { + properties: TranslationProperties; +} + +export const TranslationInput = (props: TranslationInputProps) => { const { bundle, onEvent: triggerEvent, pendingQuestions } = useTranslation(); - const { name, onChange, guiOptions = {}, message, errorMessage, id } = props; + const { name, onChange, guiOptions = {}, message, errorMessage, id, properties } = props; const { mandatory, hint, placeholder } = guiOptions; const [value, setValue] = useValue('', props.value); const onLiveChange = (_event: React.FormEvent, newValue?: string | undefined) => { @@ -20,7 +25,8 @@ export const TranslationInput = (props: InputProps) => { (entry: TranslationEntry): void => { triggerEvent?.(name, { name: TRANSLATE_EVENT_UPDATE, - entry + entry, + properties }); }, [name] diff --git a/packages/ui-prompting/src/components/Question/Question.tsx b/packages/ui-prompting/src/components/Question/Question.tsx index 549f3fc57a8..0569f3a86fe 100644 --- a/packages/ui-prompting/src/components/Question/Question.tsx +++ b/packages/ui-prompting/src/components/Question/Question.tsx @@ -28,8 +28,8 @@ export const Question = (props: QuestionProps) => { const inputId = id ? `${id}--input` : undefined; switch (question?.type) { case 'input': { - const { translatable } = question.guiOptions ?? {}; - if (isI18nInputSupported && translatable) { + const { translationProperties } = question.guiOptions ?? {}; + if (isI18nInputSupported && translationProperties) { questionInput = ( { onChange={onChange} errorMessage={errorMessage} id={inputId} + properties={translationProperties} /> ); } else { diff --git a/packages/ui-prompting/src/types.ts b/packages/ui-prompting/src/types.ts index 64a4f828d8a..157befb9b48 100644 --- a/packages/ui-prompting/src/types.ts +++ b/packages/ui-prompting/src/types.ts @@ -6,6 +6,7 @@ import type { GuiOptions as BaseGuiOptions } from '@sap-ux/inquirer-common'; import type { I18nBundle, TranslationEntry } from '@sap-ux/ui-components'; +import type { SapTextType } from '@sap-ux/i18n'; export { Answers }; @@ -60,16 +61,29 @@ export interface ListPromptQuestionCreationProps { placeholder?: string; } +/** + * Translation properties for translatable entry. + */ +export interface TranslationProperties { + /** + * Text types for translatable entry. + */ + type: SapTextType; + + /** + * Description of the annotation for a new entry in the translation file. + */ + annotation?: string; +} + /** * Extended GUI interface for list question. */ export interface InputGuiOptions extends GuiOptions { /** - * Renders the input field as translatable, allowing the creation of i18n entries with i18n binding. - * - * @default false + * Translation properties for translatable input. If this is defined, the input is considered translatable. */ - translatable?: boolean; + translationProperties?: TranslationProperties; } /** @@ -155,6 +169,7 @@ export const TRANSLATE_EVENT_SHOW = 'show'; export interface TranlateUpdateEvent { name: typeof TRANSLATE_EVENT_UPDATE; entry: T; + properties?: TranslationProperties; } export interface TranlateShowEvent { name: typeof TRANSLATE_EVENT_SHOW; diff --git a/packages/ui-prompting/test/unit/components/Inputs/TranslationInput.test.tsx b/packages/ui-prompting/test/unit/components/Inputs/TranslationInput.test.tsx index fde5a986799..f271c7713a0 100644 --- a/packages/ui-prompting/test/unit/components/Inputs/TranslationInput.test.tsx +++ b/packages/ui-prompting/test/unit/components/Inputs/TranslationInput.test.tsx @@ -2,13 +2,18 @@ import * as React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { initIcons } from '@sap-ux/ui-components'; import { TranslationInput } from '../../../../src/components'; -import type { InputProps } from '../../../../src/components'; +import type { TranslationInputProps } from '../../../../src/components'; import * as TranslationContext from '../../../../src/context/TranslationContext'; import { acceptI18nCallout, clickI18nButton, isI18nLoading } from '../../utils'; import { TRANSLATE_EVENT_SHOW, TRANSLATE_EVENT_UPDATE } from '../../../../src/types'; +import { SapShortTextType } from '@sap-ux/i18n'; const id = 'test'; -const props: InputProps = { +const annotationProps = { + type: SapShortTextType.GeneralText, + annotation: 'Dummy text' +}; +const props: TranslationInputProps = { id, value: 'dummy', name: 'testInput', @@ -18,7 +23,8 @@ const props: InputProps = { hint: '', placeholder: undefined }, - errorMessage: undefined + errorMessage: undefined, + properties: annotationProps }; const selectors = { @@ -143,7 +149,8 @@ describe('TranslationInput', () => { expect(triggerEventMock).toBeCalledTimes(1); expect(triggerEventMock).toBeCalledWith('testInput', { entry: { key: { value: 'dummy' }, value: { value: 'dummy' } }, - name: TRANSLATE_EVENT_UPDATE + name: TRANSLATE_EVENT_UPDATE, + properties: annotationProps }); }); diff --git a/packages/ui-prompting/test/unit/components/Questions.test.tsx b/packages/ui-prompting/test/unit/components/Questions.test.tsx index 23ffac0da88..9863dbb8601 100644 --- a/packages/ui-prompting/test/unit/components/Questions.test.tsx +++ b/packages/ui-prompting/test/unit/components/Questions.test.tsx @@ -8,6 +8,7 @@ import type { QuestionsProps } from '../../../src'; import { questions } from '../../mock-data/questions'; import { getDependantQuestions } from '../../../src/utilities'; import { acceptI18nCallout, clickI18nButton, isI18nLoading, translationInputSelectors } from '../utils'; +import { SapShortTextType } from '@sap-ux/i18n'; describe('Questions', () => { initIcons(); @@ -284,13 +285,17 @@ describe('Questions', () => { }); describe('Translation input', () => { + const translationAnnotation = { + type: SapShortTextType.GeneralText, + annotation: 'Dummy text' + }; const question: PromptQuestion = { message: 'Translatable empty', name: 'testInput', type: 'input', default: 'dummy value', guiOptions: { - translatable: true + translationProperties: translationAnnotation } }; @@ -316,7 +321,8 @@ describe('Questions', () => { expect(onTranslateEvent).toBeCalledTimes(1); expect(onTranslateEvent).toBeCalledWith('testInput', { entry: { key: { value: 'dummyValue' }, value: { value: 'dummy value' } }, - name: TRANSLATE_EVENT_UPDATE + name: TRANSLATE_EVENT_UPDATE, + properties: translationAnnotation }); }); diff --git a/packages/ui-prompting/test/unit/context/TranslationContext.test.tsx b/packages/ui-prompting/test/unit/context/TranslationContext.test.tsx index 9cb473f0401..618127d39c3 100644 --- a/packages/ui-prompting/test/unit/context/TranslationContext.test.tsx +++ b/packages/ui-prompting/test/unit/context/TranslationContext.test.tsx @@ -1,14 +1,19 @@ import * as React from 'react'; import { render } from '@testing-library/react'; import { TranslationInput } from '../../../src/components'; -import type { InputProps } from '../../../src/components'; +import type { TranslationInputProps } from '../../../src/components'; import { TranslationProvider } from '../../../src/context/TranslationContext'; import type { TranslationProviderProps } from '../../../src/context/TranslationContext'; import { acceptI18nCallout, clickI18nButton, isI18nLoading } from '../utils'; import { TRANSLATE_EVENT_SHOW, TRANSLATE_EVENT_UPDATE } from '../../../src/types'; +import { SapShortTextType } from '@sap-ux/i18n'; const id = 'test'; -const props: InputProps = { +const annotationProps = { + type: SapShortTextType.GeneralText, + annotation: 'Dummy text' +}; +const props: TranslationInputProps = { id, value: 'dummy', name: 'testInput', @@ -18,7 +23,8 @@ const props: InputProps = { hint: '', placeholder: undefined }, - errorMessage: undefined + errorMessage: undefined, + properties: annotationProps }; describe('TranslationProvider', () => { @@ -47,7 +53,8 @@ describe('TranslationProvider', () => { expect(translationProps.onEvent).toBeCalledTimes(1); expect(translationProps.onEvent).toBeCalledWith('testInput', { entry: { key: { value: 'dummyValue' }, value: { value: 'dummy value' } }, - name: TRANSLATE_EVENT_UPDATE + name: TRANSLATE_EVENT_UPDATE, + properties: annotationProps }); }); diff --git a/packages/ui-prompting/tsconfig.json b/packages/ui-prompting/tsconfig.json index 8b8f6558fd4..d721db8bf4f 100644 --- a/packages/ui-prompting/tsconfig.json +++ b/packages/ui-prompting/tsconfig.json @@ -21,6 +21,9 @@ "importsNotUsedAsValues": "remove" }, "references": [ + { + "path": "../i18n" + }, { "path": "../inquirer-common" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79f020b023f..dfe0fc15885 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1378,6 +1378,9 @@ importers: specifier: 0.0.33 version: 0.0.33 devDependencies: + '@sap-ux/i18n': + specifier: workspace:* + version: link:../i18n '@sap-ux/ui-prompting': specifier: workspace:* version: link:../ui-prompting @@ -2695,6 +2698,9 @@ importers: '@babel/preset-typescript': specifier: 7.22.15 version: 7.22.15(@babel/core@7.22.20) + '@sap-ux/i18n': + specifier: workspace:* + version: link:../i18n '@sap-ux/inquirer-common': specifier: workspace:* version: link:../inquirer-common