From f51120a60e001dddb7b36c2e5e475f6946cf59de Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Tue, 21 Jan 2025 16:10:05 +0100 Subject: [PATCH 1/5] update app title in appmetadata and text resource only from settings --- .../SettingsModal/SettingsModal.test.tsx | 14 +- .../Tabs/AboutTab/AboutTab.test.tsx | 297 +++++++++++------- .../components/Tabs/AboutTab/AboutTab.tsx | 53 +++- .../AboutTab/InputFields/InputFields.test.tsx | 52 ++- .../Tabs/AboutTab/InputFields/InputFields.tsx | 152 ++++++--- .../SettingsModal/mocks/appConfigMock.ts | 8 - frontend/app-development/test/mocks.tsx | 1 - frontend/language/src/nb.json | 4 +- 8 files changed, 357 insertions(+), 224 deletions(-) delete mode 100644 frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/mocks/appConfigMock.ts diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/SettingsModal.test.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/SettingsModal.test.tsx index 431bdb49396..9deaf2886d2 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/SettingsModal.test.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/SettingsModal.test.tsx @@ -5,27 +5,15 @@ import userEvent from '@testing-library/user-event'; import { SettingsModal } from './SettingsModal'; import { textMock } from '@studio/testing/mocks/i18nMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import type { QueryClient, UseMutationResult } from '@tanstack/react-query'; +import type { QueryClient } from '@tanstack/react-query'; import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; -import type { AppConfig } from 'app-shared/types/AppConfig'; -import { useAppConfigMutation } from 'app-development/hooks/mutations'; import { MemoryRouter } from 'react-router-dom'; import { SettingsModalContextProvider } from 'app-development/contexts/SettingsModalContext'; import { PreviewContextProvider } from 'app-development/contexts/PreviewContext'; import type { SettingsModalHandle } from 'app-development/types/SettingsModalHandle'; import { typedLocalStorage } from '@studio/pure-functions'; -jest.mock('app-development/hooks/mutations/useAppConfigMutation'); - -const updateAppConfigMutation = jest.fn(); -const mockUpdateAppConfigMutation = useAppConfigMutation as jest.MockedFunction< - typeof useAppConfigMutation ->; -mockUpdateAppConfigMutation.mockReturnValue({ - mutate: updateAppConfigMutation, -} as unknown as UseMutationResult); - describe('SettingsModal', () => { const user = userEvent.setup(); afterEach(jest.clearAllMocks); diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx index 60b4145334a..47d67738174 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx @@ -1,58 +1,35 @@ import React from 'react'; -import { render, screen, waitForElementToBeRemoved } from '@testing-library/react'; -import { AboutTab } from './AboutTab'; +import { screen, waitForElementToBeRemoved } from '@testing-library/react'; +import { AboutTab, getAppTitlesToDisplay } from './AboutTab'; import { textMock } from '@studio/testing/mocks/i18nMock'; -import type { AppConfig } from 'app-shared/types/AppConfig'; import userEvent from '@testing-library/user-event'; -import { useAppConfigMutation } from 'app-development/hooks/mutations'; -import type { UseMutationResult } from '@tanstack/react-query'; import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; -import { ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; -import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { mockRepository1, mockRepository2 } from '../../../mocks/repositoryMock'; -import { mockAppConfig } from '../../../mocks/appConfigMock'; import { formatDateToDateAndTimeString } from 'app-development/utils/dateUtils'; -import { MemoryRouter } from 'react-router-dom'; import type { ApplicationMetadata } from 'app-shared/types/ApplicationMetadata'; import { app, org } from '@studio/testing/testids'; -import { SettingsModalContextProvider } from 'app-development/contexts/SettingsModalContext'; -import { PreviewContext, type PreviewContextProps } from 'app-development/contexts/PreviewContext'; - +import { renderWithProviders } from 'app-development/test/mocks'; +import { APP_NAME, DEFAULT_LANGUAGE } from 'app-shared/constants'; +import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; +import type { PreviewContextProps } from '../../../../../../../../contexts/PreviewContext'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; +import type { RecommendedLanguageFlags, ServiceNames } from './InputFields/InputFields'; + +const nb: string = 'nb'; +const nn: string = 'nn'; +const en: string = 'en'; +const se: string = 'se'; +const da: string = 'da'; const mockNewText: string = 'test'; - +const mockAppTitle = 'mockAppTitle'; const mockAppMetadata: ApplicationMetadata = { id: `${org}/${app}`, org, + title: { [DEFAULT_LANGUAGE]: mockAppTitle }, createdBy: 'Test Testesen', }; -const defaultPreviewContextProps: PreviewContextProps = { - shouldReloadPreview: false, - doReloadPreview: jest.fn(), - previewHasLoaded: jest.fn(), -}; - -jest.mock('app-development/hooks/mutations/useAppConfigMutation'); -const updateAppConfigMutation = jest.fn(); -const mockUpdateAppConfigMutation = useAppConfigMutation as jest.MockedFunction< - typeof useAppConfigMutation ->; -mockUpdateAppConfigMutation.mockReturnValue({ - mutate: updateAppConfigMutation, -} as unknown as UseMutationResult); - -const getAppConfig = jest.fn().mockImplementation(() => Promise.resolve({})); -const getRepoMetadata = jest.fn().mockImplementation(() => Promise.resolve({})); -const getAppMetadata = jest.fn().mockImplementation(() => Promise.resolve({})); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => { - return { org, app }; - }, -})); - describe('AboutTab', () => { afterEach(jest.clearAllMocks); @@ -62,29 +39,27 @@ describe('AboutTab', () => { expect(screen.getByTitle(textMock('settings_modal.loading_content'))).toBeInTheDocument(); }); - it('fetches appConfig on mount', () => { + it('fetches languages on mount', () => { renderAboutTab(); - expect(getAppConfig).toHaveBeenCalledTimes(1); + expect(queriesMock.getTextLanguages).toHaveBeenCalledTimes(1); }); it('fetches repoMetadata on mount', () => { renderAboutTab(); - expect(getRepoMetadata).toHaveBeenCalledTimes(1); + expect(queriesMock.getRepoMetadata).toHaveBeenCalledTimes(1); }); it('fetches applicationMetadata on mount', () => { renderAboutTab(); - expect(getAppMetadata).toHaveBeenCalledTimes(1); + expect(queriesMock.getAppMetadata).toHaveBeenCalledTimes(1); }); - it.each(['getAppConfig', 'getRepoMetadata', 'getAppMetadata'])( + it.each(['getTextLanguages', 'getRepoMetadata', 'getAppMetadata'])( 'shows an error message if an error occured on the %s query', - async (queryName) => { + async (queryName: string) => { const errorMessage = 'error-message-test'; renderAboutTab({ - queries: { - [queryName]: () => Promise.reject({ message: errorMessage }), - }, + [queryName]: jest.fn().mockImplementation(() => Promise.reject({ message: errorMessage })), }); await waitForElementToBeRemoved(() => @@ -101,7 +76,7 @@ describe('AboutTab', () => { await resolveAndWaitForSpinnerToDisappear(); const repoNameInput = screen.getByLabelText(textMock('settings_modal.about_tab_repo_label')); - expect(repoNameInput).toHaveValue(mockAppConfig.repositoryName); + expect(repoNameInput).toHaveValue(mockRepository1.name); expect(repoNameInput).toHaveAttribute('readonly'); }); @@ -109,35 +84,71 @@ describe('AboutTab', () => { const user = userEvent.setup(); await resolveAndWaitForSpinnerToDisappear(); - const appName = screen.getByLabelText(textMock('settings_modal.about_tab_name_label')); - expect(appName).toHaveValue(mockAppConfig.serviceName); - + const appName = screen.getByLabelText(textMock('language.nb')); + expect(appName).toHaveValue(mockAppMetadata.title.nb); + await user.clear(appName); await user.type(appName, mockNewText); - expect(appName).toHaveValue(`${mockAppConfig.serviceName}${mockNewText}`); + expect(appName).toHaveValue(mockNewText); }); - it('displays correct value in "alternative id" input field, and updates the value on change', async () => { - const user = userEvent.setup(); - await resolveAndWaitForSpinnerToDisappear(); + it('displays empty input fields for recommended app title languages even though titles are not set in appMetadata', async () => { + const getAppMetadata = jest + .fn() + .mockImplementation(() => Promise.resolve({ ...mockAppMetadata, title: {} })); + await resolveAndWaitForSpinnerToDisappear({ getAppMetadata }); + + [nb, nn, en].forEach((lang) => { + const appName = screen.getByRole('textbox', { name: textMock(`language.${lang}`) }); + expect(appName).toBeInTheDocument(); + expect(appName).not.toHaveValue(); + }); + }); - const altId = screen.getByLabelText(textMock('settings_modal.about_tab_alt_id_label')); - expect(altId).toHaveValue(mockAppConfig.serviceId); + it('displays input fields for recommended app title languages as readOnly if app does not have translation for them', async () => { + const getTextLanguages = jest.fn().mockImplementation(() => Promise.resolve([])); + const getAppMetadata = jest + .fn() + .mockImplementation(() => Promise.resolve({ ...mockAppMetadata, title: {} })); + await resolveAndWaitForSpinnerToDisappear({ getTextLanguages, getAppMetadata }); + + [nb, nn, en].forEach((lang) => { + const appName = screen.getByRole('textbox', { name: textMock(`language.${lang}`) }); + expect(appName).toHaveAttribute('readonly'); + }); + }); - await user.type(altId, mockNewText); + it('displays input fields for remaining app titles for languages available in app in addition to the recommended', async () => { + const getTextLanguages = jest + .fn() + .mockImplementation(() => Promise.resolve([DEFAULT_LANGUAGE, da])); + await resolveAndWaitForSpinnerToDisappear({ getTextLanguages }); - expect(altId).toHaveValue(`${mockAppConfig.serviceId}${mockNewText}`); + [nb, nb, en, da].forEach((lang) => { + const appName = screen.getByRole('textbox', { name: textMock(`language.${lang}`) }); + expect(appName).toBeInTheDocument(); + }); }); - it('should update app config when saving', async () => { + it('should update appMetadata and text resource for given language when saving', async () => { const user = userEvent.setup(); await resolveAndWaitForSpinnerToDisappear(); - const altId = screen.getByLabelText(textMock('settings_modal.about_tab_alt_id_label')); - await user.type(altId, mockNewText); + const serviceNameNb = screen.getByLabelText(textMock('language.nb')); + await user.clear(serviceNameNb); + await user.type(serviceNameNb, mockNewText); await user.tab(); - expect(updateAppConfigMutation).toHaveBeenCalledTimes(1); + expect(queriesMock.updateAppMetadata).toHaveBeenCalledTimes(1); + expect(queriesMock.updateAppMetadata).toHaveBeenCalledWith( + org, + app, + expect.objectContaining({ title: { nb: mockNewText } }), + ); + expect(queriesMock.upsertTextResources).toHaveBeenCalledTimes(1); + expect(queriesMock.upsertTextResources).toHaveBeenCalledWith(org, app, DEFAULT_LANGUAGE, { + [APP_NAME]: mockNewText, + }); }); it('displays owners full name when it is set', async () => { @@ -148,11 +159,8 @@ describe('AboutTab', () => { }); it('displays owners login name when full name is not set', async () => { - getRepoMetadata.mockImplementation(() => Promise.resolve(mockRepository2)); - renderAboutTab(); - await waitForElementToBeRemoved(() => - screen.queryByTitle(textMock('settings_modal.loading_content')), - ); + const getRepoMetadata = jest.fn().mockImplementation(() => Promise.resolve(mockRepository2)); + await resolveAndWaitForSpinnerToDisappear({ getRepoMetadata }); expect(screen.queryByText(mockRepository1.owner.full_name)).not.toBeInTheDocument(); expect(screen.getByText(mockRepository1.owner.login)).toBeInTheDocument(); @@ -176,58 +184,129 @@ describe('AboutTab', () => { expect(screen.getByText(mockAppMetadata.createdBy)).toBeInTheDocument(); }); - it('calls "doReloadPreview" when saving the app config', async () => { + it('calls "doReloadPreview" when changing service name', async () => { const user = userEvent.setup(); const doReloadPreview = jest.fn(); - await resolveAndWaitForSpinnerToDisappear({ previewContextProps: { doReloadPreview } }); + await resolveAndWaitForSpinnerToDisappear({}, { doReloadPreview }); - const serviceName = screen.getByLabelText(textMock('settings_modal.about_tab_name_label')); - await user.type(serviceName, mockNewText); + const serviceNameNb = screen.getByLabelText(textMock('language.nb')); + await user.type(serviceNameNb, mockNewText); await user.tab(); expect(doReloadPreview).toHaveBeenCalledTimes(1); }); }); -type Props = { - queries: Partial; - previewContextProps: Partial; -}; - -const resolveAndWaitForSpinnerToDisappear = async (props: Partial = {}) => { - getAppConfig.mockImplementation(() => Promise.resolve(mockAppConfig)); - getRepoMetadata.mockImplementation(() => Promise.resolve(mockRepository1)); - getAppMetadata.mockImplementation(() => Promise.resolve(mockAppMetadata)); - - renderAboutTab(props); +const resolveAndWaitForSpinnerToDisappear = async ( + queries: Partial = {}, + previewContextProps: Partial = {}, +) => { + const getTextLanguages = jest.fn().mockImplementation(() => Promise.resolve([DEFAULT_LANGUAGE])); + const getRepoMetadata = jest.fn().mockImplementation(() => Promise.resolve(mockRepository1)); + const getAppMetadata = jest.fn().mockImplementation(() => Promise.resolve(mockAppMetadata)); + + renderAboutTab( + { getTextLanguages, getRepoMetadata, getAppMetadata, ...queries }, + { ...previewContextProps }, + ); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('settings_modal.loading_content')), ); }; -const renderAboutTab = (props: Partial = {}) => { - const { queries, previewContextProps } = props; - - const allQueries: ServicesContextProps = { - ...queriesMock, - getAppConfig, - getRepoMetadata, - getAppMetadata, - ...queries, - }; - - return render( - - - - - - - - - , - ); +const renderAboutTab = ( + queries: Partial = {}, + previewContextProps: Partial = {}, +) => { + const queryClient = createQueryClientMock(); + return renderWithProviders({ ...queries }, queryClient, { ...previewContextProps })(); }; + +describe('getAppTitlesToDisplay', () => { + it('returns empty recommended app titles if appMetadataTitles and appLangCodes are empty', () => { + const appMetadataTitles: KeyValuePairs = {}; + const appLangCodesData: string[] = []; + const appTitles: ServiceNames = getAppTitlesToDisplay( + appMetadataTitles, + appLangCodesData, + ); + expect(appTitles).toEqual({ nb: undefined, nn: undefined, en: undefined }); + }); + + it('returns empty recommended app titles if appMetadataTitles is empty and appLangCodes contains them', () => { + const appMetadataTitles: KeyValuePairs = {}; + const appLangCodesData: string[] = [nb, nn, en]; + const appTitles: ServiceNames = getAppTitlesToDisplay( + appMetadataTitles, + appLangCodesData, + ); + expect(appTitles).toEqual({ nb: undefined, nn: undefined, en: undefined }); + }); + + it('returns the recommended app titles with values if present in appMetadataTitles', () => { + const appNameNb: string = 'appNameNb'; + const appMetadataTitles: KeyValuePairs = { nb: appNameNb }; + const appLangCodesData: string[] = []; + const appTitles: ServiceNames = getAppTitlesToDisplay( + appMetadataTitles, + appLangCodesData, + ); + expect(appTitles).toEqual({ nb: appNameNb, nn: undefined, en: undefined }); + }); + + it('returns the recommended app titles with values if present in appMetadataTitles and if languages are present in appLangCodesData', () => { + const appNameNb: string = 'appNameNb'; + const appMetadataTitles: KeyValuePairs = { nb: appNameNb }; + const appLangCodesData: string[] = [nb, nn, en]; + const appTitles: ServiceNames = getAppTitlesToDisplay( + appMetadataTitles, + appLangCodesData, + ); + expect(appTitles).toEqual({ nb: appNameNb, nn: undefined, en: undefined }); + }); + + it('returns additional empty app titles if languages are present in appLangCodesData', () => { + const appMetadataTitles: KeyValuePairs = {}; + const appLangCodesData: string[] = [se, da]; + const appTitles: ServiceNames = getAppTitlesToDisplay( + appMetadataTitles, + appLangCodesData, + ); + expect(appTitles).toEqual({ + nb: undefined, + nn: undefined, + en: undefined, + se: undefined, + da: undefined, + }); + }); + + it('returns additional app titles with values if languages are present in appLangCodesData and appMetadataTitles', () => { + const appNameSe: string = 'appNameSe'; + const appMetadataTitles: KeyValuePairs = { se: appNameSe }; + const appLangCodesData: string[] = [se, da]; + const appTitles: ServiceNames = getAppTitlesToDisplay( + appMetadataTitles, + appLangCodesData, + ); + expect(appTitles).toEqual({ + nb: undefined, + nn: undefined, + en: undefined, + se: appNameSe, + da: undefined, + }); + }); + + it('return additional app title for language if language is not recommended and not in appLangCodes, but in appMetadataTitles', () => { + const appNameSe: string = 'appNameSe'; + const appMetadataTitles: KeyValuePairs = { se: appNameSe }; + const appLangCodesData: string[] = []; + const appTitles: ServiceNames = getAppTitlesToDisplay( + appMetadataTitles, + appLangCodesData, + ); + expect(appTitles).toEqual({ nb: undefined, nn: undefined, en: undefined, se: appNameSe }); + }); +}); diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx index 53f73ee6130..60823535898 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx @@ -1,11 +1,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { TabHeader } from '../../TabHeader'; -import type { AppConfig } from 'app-shared/types/AppConfig'; import { ErrorMessage } from '@digdir/designsystemet-react'; import { getRepositoryType } from 'app-shared/utils/repository'; -import { useAppConfigMutation } from 'app-development/hooks/mutations'; -import { useAppConfigQuery } from 'app-development/hooks/queries'; import { useAppMetadataQuery, useRepoMetadataQuery } from 'app-shared/hooks/queries'; import { mergeQueryStatuses } from 'app-shared/utils/tanstackQueryUtils'; import { LoadingTabData } from '../../LoadingTabData'; @@ -15,20 +12,24 @@ import { CreatedFor } from './CreatedFor'; import { TabContent } from '../../TabContent'; import { usePreviewContext } from '../../../../../../../../contexts/PreviewContext'; import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams'; +import type { ServiceNames } from './InputFields/InputFields'; +import { RecommendedLanguageFlags } from './InputFields/InputFields'; +import { useUpdateAppTitle } from '../../../hooks/useUpdateAppTitle'; +import { useLanguagesQuery } from '../../../../../../../../hooks/queries'; +import { ArrayUtils } from '@studio/pure-functions'; export const AboutTab = (): React.ReactElement => { const { t } = useTranslation(); const { org, app } = useStudioEnvironmentParams(); - const repositoryType = getRepositoryType(org, app); const { doReloadPreview } = usePreviewContext(); const { - status: appConfigStatus, - data: appConfigData, - error: appConfigError, - } = useAppConfigQuery(org, app); + status: appLangCodesStatus, + data: appLangCodesData, + error: appLangCodesError, + } = useLanguagesQuery(org, app); const { status: repositoryStatus, data: repositoryData, @@ -40,24 +41,24 @@ export const AboutTab = (): React.ReactElement => { error: applicationMetadataError, } = useAppMetadataQuery(org, app); - const { mutate: updateAppConfigMutation } = useAppConfigMutation(org, app); + const updateAppTitle = useUpdateAppTitle(applicationMetadataData); - const handleSaveAppConfig = (appConfig: AppConfig) => { - if (appConfigData.serviceName !== appConfig.serviceName) { + const handleSaveServiceName = (serviceName: string, language: string) => { + if (applicationMetadataData.title[language] !== serviceName) { doReloadPreview(); } - updateAppConfigMutation(appConfig); + updateAppTitle(language, serviceName); }; const displayContent = () => { - switch (mergeQueryStatuses(appConfigStatus, repositoryStatus, applicationMetadataStatus)) { + switch (mergeQueryStatuses(appLangCodesStatus, repositoryStatus, applicationMetadataStatus)) { case 'pending': { return ; } case 'error': { return ( - {appConfigError && {appConfigError.message}} + {appLangCodesError && {appLangCodesError.message}} {repositoryError && {repositoryError.message}} {applicationMetadataError && ( {applicationMetadataError.message} @@ -66,9 +67,18 @@ export const AboutTab = (): React.ReactElement => { ); } case 'success': { + const appTitles: ServiceNames<(typeof appLangCodesData)[number]> = getAppTitlesToDisplay( + applicationMetadataData.title, + appLangCodesData, + ); return ( <> - + { ); }; + +export const getAppTitlesToDisplay = ( + appMetadataTitles: ServiceNames<(typeof appLangCodesData)[number]>, + appLangCodesData: string[], +): ServiceNames<(typeof appLangCodesData)[number]> => { + const recommendedLanguages: string[] = Object.keys(RecommendedLanguageFlags); + const appLangCodesIncludingRecommended: string[] = ArrayUtils.removeDuplicates( + recommendedLanguages.concat(Object.keys(appMetadataTitles)).concat(appLangCodesData), + ); + return Object.fromEntries( + appLangCodesIncludingRecommended.map((lang) => [lang, appMetadataTitles[lang]]), + ); +}; diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.test.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.test.tsx index 0db3ed3475e..dec729d18e4 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.test.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.test.tsx @@ -1,58 +1,52 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import type { InputFieldsProps } from './InputFields'; +import type { InputFieldsProps, ServiceNames } from './InputFields'; import { InputFields } from './InputFields'; -import { mockAppConfig } from 'app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/mocks/appConfigMock'; import { textMock } from '@studio/testing/mocks/i18nMock'; import userEvent from '@testing-library/user-event'; const mockNewText: string = 'test'; +const langNb = 'nb'; +const appLangCodes: string[] = [langNb]; +const onSave = jest.fn(); +const repositoryName = 'repositoryName'; +const serviceNames: ServiceNames<(typeof appLangCodes)[number]> = { + [langNb]: 'mockAppTitleNb', +}; -const mockOnSave = jest.fn(); - -const defaultProps: InputFieldsProps = { - appConfig: mockAppConfig, - onSave: mockOnSave, +const defaultProps: InputFieldsProps<(typeof appLangCodes)[number]> = { + appLangCodes, + onSave, + repositoryName, + serviceNames, }; describe('InputFields', () => { afterEach(jest.clearAllMocks); - it('displays the "repo" input as readonly', async () => { + it('displays the "repo" input as readonly', () => { render(); const repoNameInput = screen.getByLabelText(textMock('settings_modal.about_tab_repo_label')); - expect(repoNameInput).toHaveValue(mockAppConfig.repositoryName); + expect(repoNameInput).toHaveValue(repositoryName); expect(repoNameInput).toHaveAttribute('readonly'); }); - it('displays correct value in "name" input field, and updates the value on change', async () => { + it('displays correct value in nb "name" input field, and updates the value on change', async () => { const user = userEvent.setup(); render(); - const appName = screen.getByLabelText(textMock('settings_modal.about_tab_name_label')); - expect(appName).toHaveValue(mockAppConfig.serviceName); - + const appName = screen.getByLabelText(textMock('language.nb')); + expect(appName).toHaveValue(serviceNames.nb); + await user.clear(appName); await user.type(appName, mockNewText); - expect(appName).toHaveValue(`${mockAppConfig.serviceName}${mockNewText}`); - }); - - it('displays correct value in "alternative id" input field, and updates the value on change', async () => { - const user = userEvent.setup(); - render(); - - const altId = screen.getByLabelText(textMock('settings_modal.about_tab_alt_id_label')); - expect(altId).toHaveValue(mockAppConfig.serviceId); - - await user.type(altId, mockNewText); - - expect(altId).toHaveValue(`${mockAppConfig.serviceId}${mockNewText}`); + expect(appName).toHaveValue(mockNewText); }); describe('InputFields Validation', () => { const user = userEvent.setup(); - const appNameLabel = textMock('settings_modal.about_tab_name_label'); + const appNameLabel = textMock('language.nb'); it('should save changes when the form is valid', async () => { render(); @@ -60,7 +54,7 @@ describe('InputFields', () => { await user.type(appName, mockNewText); await user.tab(); - expect(mockOnSave).toHaveBeenCalledTimes(1); + expect(onSave).toHaveBeenCalledTimes(1); }); it('should not save changes when form is invalid', async () => { @@ -69,7 +63,7 @@ describe('InputFields', () => { await user.clear(appName); await user.tab(); - expect(mockOnSave).toHaveBeenCalledTimes(0); + expect(onSave).toHaveBeenCalledTimes(0); }); it('should toggle error message based on form validation', async () => { diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx index 629fb1a3c9c..b44a2c34828 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx @@ -1,66 +1,124 @@ -import type { FormEvent, ReactNode } from 'react'; +import type { ChangeEvent, ReactNode } from 'react'; import React, { useState } from 'react'; import classes from './InputFields.module.css'; import { useTranslation } from 'react-i18next'; -import type { AppConfig } from 'app-shared/types/AppConfig'; -import { Textfield } from '@digdir/designsystemet-react'; +import { + StudioTextfield, + StudioLabelAsParagraph, + StudioParagraph, + StudioIconTextfield, +} from '@studio/components'; -type AppConfigForm = Pick; +export type ServiceNames = { + [key in T]: string | undefined; +}; + +export enum RecommendedLanguageFlags { + nb = '🇳🇴', + nn = '🇳🇴', + en = '🇬🇧', +} -export type InputFieldsProps = { - appConfig: AppConfig; - onSave: (appConfig: AppConfig) => void; +export type InputFieldsProps = { + appLangCodes: string[]; + onSave: (serviceName: string, language: string) => void; + repositoryName: string; + serviceNames: ServiceNames; }; -export const InputFields = ({ appConfig, onSave }: InputFieldsProps): ReactNode => { +export function InputFields({ + repositoryName, + ...rest +}: InputFieldsProps): ReactNode { const { t } = useTranslation(); - const [appConfigFormErrors, setAppConfigFormErrors] = useState< - Pick - >({ serviceName: '' }); + return ( +
+ + +
+ ); +} - const handleAppConfigFormBlur = (event: FormEvent) => { - const formData = new FormData(event.currentTarget); - const form = Object.fromEntries(formData) as AppConfigForm; - const isFormValid = validateForm(form); - if (isFormValid) { - onSave({ ...appConfig, ...form }); - } +type EditServiceNamesNameProps = Omit, 'repositoryName'>; + +function EditServiceNames({ + serviceNames, + ...rest +}: EditServiceNamesNameProps): React.ReactElement { + const { t } = useTranslation(); + + const appTitleLanguages = Object.keys(serviceNames); + + return ( +
+ {t('settings_modal.about_tab_name_label')} + + {t('settings_modal.about_tab_name_description')} + + {appTitleLanguages.map((lang: string) => ( + + ))} +
+ ); +} + +type EditServiceNameForLanguageProps = { + appLangCodes: string[]; + language: string; + onSave: (serviceName: string, language: string) => void; + serviceName: string; +}; + +function EditServiceNameForLanguage({ + appLangCodes, + language, + onSave, + serviceName, +}: EditServiceNameForLanguageProps): React.ReactElement { + const { t } = useTranslation(); + const [serviceNameError, setServiceNameError] = useState(''); + + const appHasLanguageTranslation = appLangCodes.includes(language); + + const handleOnBlur = (event: ChangeEvent) => { + const isValid = validateServiceName(event.target.value); + if (isValid) onSave(event.target.value, language); }; - const validateForm = (form: AppConfigForm): Boolean => { - if (form.serviceName.length <= 0) { - setAppConfigFormErrors({ serviceName: t('settings_modal.about_tab_name_error') }); + const validateServiceName = (newServiceName: string): Boolean => { + if (newServiceName.length <= 0) { + setServiceNameError(t('settings_modal.about_tab_name_error')); return false; } - setAppConfigFormErrors({ serviceName: '' }); + setServiceNameError(''); return true; }; + const description: string = + !appHasLanguageTranslation && t('settings_modal.about_tab_app_title_no_translation_file'); + return ( -
- - - - + ); -}; +} diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/mocks/appConfigMock.ts b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/mocks/appConfigMock.ts deleted file mode 100644 index 26d98f6f595..00000000000 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/mocks/appConfigMock.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { AppConfig } from 'app-shared/types/AppConfig'; - -export const mockAppConfig: AppConfig = { - repositoryName: 'test', - serviceName: 'test', - serviceId: '', - serviceDescription: '', -}; diff --git a/frontend/app-development/test/mocks.tsx b/frontend/app-development/test/mocks.tsx index 5389e6c8949..61f783d01db 100644 --- a/frontend/app-development/test/mocks.tsx +++ b/frontend/app-development/test/mocks.tsx @@ -5,7 +5,6 @@ import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { BrowserRouter } from 'react-router-dom'; import { PreviewConnectionContextProvider } from 'app-shared/providers/PreviewConnectionContext'; - import { queriesMock } from 'app-shared/mocks/queriesMock'; import type { QueryClient } from '@tanstack/react-query'; import { queryClientConfigMock } from 'app-shared/mocks/queryClientMock'; diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index 2c1c062350e..f8b48c8f307 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -1068,13 +1068,13 @@ "session.dont_show_again": "Nei, ikke vis meldingen igjen", "session.reminder": "Ønsker du en påminnelse neste gang du logger på?", "settings_modal.about_tab_alt_id_description": "Hvis appen har en annen ID i andre systemer, kan du legge den til her. For eksempel et referansenummer.", - "settings_modal.about_tab_alt_id_label": "Alternativ ID", + "settings_modal.about_tab_app_title_no_translation_file": "Appen din har ikke oversettelser på dette språket.", "settings_modal.about_tab_created_by": "Opprettet av", "settings_modal.about_tab_created_date": "Dato: {{date}}", "settings_modal.about_tab_created_for_repo": "Opprettet for", "settings_modal.about_tab_created_for_service": "Denne appen er opprettet for", "settings_modal.about_tab_heading": "Om appen", - "settings_modal.about_tab_name_description": "Navnet blir synlig for brukerne og bør beskrive appen.", + "settings_modal.about_tab_name_description": "Navnet blir synlig for brukerne og bør beskrive appen. Vi anbefaler at appen som et minimum har navn på bokmål, nynorsk og engelsk.", "settings_modal.about_tab_name_error": "Du må fylle ut App-navn. Du kan endre navnet helt frem til du skal publisere appen.", "settings_modal.about_tab_name_label": "Navn på appen", "settings_modal.about_tab_repo_description": "Dette er en unik ID for appen både i URL-er og i API-er.", From d0e11ba2024717d43cb6622d443c4be5ff9e7c8d Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Fri, 24 Jan 2025 12:54:36 +0100 Subject: [PATCH 2/5] render flag as separat component --- .../Tabs/AboutTab/InputFields/InputFields.module.css | 4 ++++ .../Tabs/AboutTab/InputFields/InputFields.tsx | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.module.css b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.module.css index d6b93e0a973..fef2bb33313 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.module.css +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.module.css @@ -7,3 +7,7 @@ padding-bottom: var(--field-spacing); border-bottom: var(--studio-border-divider); } + +.serviceName { + padding: var(--fds-spacing-2) 0; +} diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx index b44a2c34828..c25ebd350d1 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx @@ -111,7 +111,7 @@ function EditServiceNameForLanguage({ return ( } size='small' label={t(`language.${language}`)} description={description} @@ -119,6 +119,16 @@ function EditServiceNameForLanguage({ value={serviceName} onBlur={handleOnBlur} readOnly={!appHasLanguageTranslation} + className={classes.serviceName} /> ); } + +type FlagIconProps = { + language: string; +}; + +const FlagIcon = ({ language }: FlagIconProps): React.ReactNode => { + const languageHasFlag = !!RecommendedLanguageFlags[language]; + return languageHasFlag ? {RecommendedLanguageFlags[language]} : null; +}; From 38fc976fa1c290c4620e3596557160e69f98aaa9 Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Fri, 24 Jan 2025 16:26:53 +0100 Subject: [PATCH 3/5] fix tests --- .../SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx | 2 +- frontend/packages/shared/src/mocks/mocks.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx index 47d67738174..83402bc80b9 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx @@ -114,7 +114,7 @@ describe('AboutTab', () => { [nb, nn, en].forEach((lang) => { const appName = screen.getByRole('textbox', { name: textMock(`language.${lang}`) }); - expect(appName).toHaveAttribute('readonly'); + expect(appName).toBeDisabled(); }); }); diff --git a/frontend/packages/shared/src/mocks/mocks.ts b/frontend/packages/shared/src/mocks/mocks.ts index 380f5250760..54e99b40c4e 100644 --- a/frontend/packages/shared/src/mocks/mocks.ts +++ b/frontend/packages/shared/src/mocks/mocks.ts @@ -168,6 +168,9 @@ export const policy: Policy = { export const applicationMetadata: ApplicationMetadata = { id: '', org: '', + title: { + nb: '', + }, }; export const resourceVersionStatus: ResourceVersionStatus = { From fce679019f19d40daee84a538caf0d6782e282f5 Mon Sep 17 00:00:00 2001 From: andreastanderen Date: Thu, 30 Jan 2025 08:23:56 +0100 Subject: [PATCH 4/5] use getAppTitlesToDisplay from utils --- .../Tabs/AboutTab/AboutTab.test.tsx | 93 +------------------ .../components/Tabs/AboutTab/AboutTab.tsx | 18 +--- frontend/app-development/test/mocks.tsx | 1 + 3 files changed, 4 insertions(+), 108 deletions(-) diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx index 83402bc80b9..295f582ba46 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { screen, waitForElementToBeRemoved } from '@testing-library/react'; -import { AboutTab, getAppTitlesToDisplay } from './AboutTab'; +import { AboutTab } from './AboutTab'; import { textMock } from '@studio/testing/mocks/i18nMock'; import userEvent from '@testing-library/user-event'; import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; @@ -13,13 +13,10 @@ import { APP_NAME, DEFAULT_LANGUAGE } from 'app-shared/constants'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import type { PreviewContextProps } from '../../../../../../../../contexts/PreviewContext'; import { queriesMock } from 'app-shared/mocks/queriesMock'; -import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; -import type { RecommendedLanguageFlags, ServiceNames } from './InputFields/InputFields'; const nb: string = 'nb'; const nn: string = 'nn'; const en: string = 'en'; -const se: string = 'se'; const da: string = 'da'; const mockNewText: string = 'test'; const mockAppTitle = 'mockAppTitle'; @@ -222,91 +219,3 @@ const renderAboutTab = ( const queryClient = createQueryClientMock(); return renderWithProviders({ ...queries }, queryClient, { ...previewContextProps })(); }; - -describe('getAppTitlesToDisplay', () => { - it('returns empty recommended app titles if appMetadataTitles and appLangCodes are empty', () => { - const appMetadataTitles: KeyValuePairs = {}; - const appLangCodesData: string[] = []; - const appTitles: ServiceNames = getAppTitlesToDisplay( - appMetadataTitles, - appLangCodesData, - ); - expect(appTitles).toEqual({ nb: undefined, nn: undefined, en: undefined }); - }); - - it('returns empty recommended app titles if appMetadataTitles is empty and appLangCodes contains them', () => { - const appMetadataTitles: KeyValuePairs = {}; - const appLangCodesData: string[] = [nb, nn, en]; - const appTitles: ServiceNames = getAppTitlesToDisplay( - appMetadataTitles, - appLangCodesData, - ); - expect(appTitles).toEqual({ nb: undefined, nn: undefined, en: undefined }); - }); - - it('returns the recommended app titles with values if present in appMetadataTitles', () => { - const appNameNb: string = 'appNameNb'; - const appMetadataTitles: KeyValuePairs = { nb: appNameNb }; - const appLangCodesData: string[] = []; - const appTitles: ServiceNames = getAppTitlesToDisplay( - appMetadataTitles, - appLangCodesData, - ); - expect(appTitles).toEqual({ nb: appNameNb, nn: undefined, en: undefined }); - }); - - it('returns the recommended app titles with values if present in appMetadataTitles and if languages are present in appLangCodesData', () => { - const appNameNb: string = 'appNameNb'; - const appMetadataTitles: KeyValuePairs = { nb: appNameNb }; - const appLangCodesData: string[] = [nb, nn, en]; - const appTitles: ServiceNames = getAppTitlesToDisplay( - appMetadataTitles, - appLangCodesData, - ); - expect(appTitles).toEqual({ nb: appNameNb, nn: undefined, en: undefined }); - }); - - it('returns additional empty app titles if languages are present in appLangCodesData', () => { - const appMetadataTitles: KeyValuePairs = {}; - const appLangCodesData: string[] = [se, da]; - const appTitles: ServiceNames = getAppTitlesToDisplay( - appMetadataTitles, - appLangCodesData, - ); - expect(appTitles).toEqual({ - nb: undefined, - nn: undefined, - en: undefined, - se: undefined, - da: undefined, - }); - }); - - it('returns additional app titles with values if languages are present in appLangCodesData and appMetadataTitles', () => { - const appNameSe: string = 'appNameSe'; - const appMetadataTitles: KeyValuePairs = { se: appNameSe }; - const appLangCodesData: string[] = [se, da]; - const appTitles: ServiceNames = getAppTitlesToDisplay( - appMetadataTitles, - appLangCodesData, - ); - expect(appTitles).toEqual({ - nb: undefined, - nn: undefined, - en: undefined, - se: appNameSe, - da: undefined, - }); - }); - - it('return additional app title for language if language is not recommended and not in appLangCodes, but in appMetadataTitles', () => { - const appNameSe: string = 'appNameSe'; - const appMetadataTitles: KeyValuePairs = { se: appNameSe }; - const appLangCodesData: string[] = []; - const appTitles: ServiceNames = getAppTitlesToDisplay( - appMetadataTitles, - appLangCodesData, - ); - expect(appTitles).toEqual({ nb: undefined, nn: undefined, en: undefined, se: appNameSe }); - }); -}); diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx index 60823535898..02bdac14869 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/AboutTab.tsx @@ -13,10 +13,9 @@ import { TabContent } from '../../TabContent'; import { usePreviewContext } from '../../../../../../../../contexts/PreviewContext'; import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams'; import type { ServiceNames } from './InputFields/InputFields'; -import { RecommendedLanguageFlags } from './InputFields/InputFields'; import { useUpdateAppTitle } from '../../../hooks/useUpdateAppTitle'; import { useLanguagesQuery } from '../../../../../../../../hooks/queries'; -import { ArrayUtils } from '@studio/pure-functions'; +import { getAppTitlesToDisplay } from './utils/getAppTitlesToDisplay'; export const AboutTab = (): React.ReactElement => { const { t } = useTranslation(); @@ -47,7 +46,7 @@ export const AboutTab = (): React.ReactElement => { if (applicationMetadataData.title[language] !== serviceName) { doReloadPreview(); } - updateAppTitle(language, serviceName); + updateAppTitle({ language, appTitle: serviceName }); }; const displayContent = () => { @@ -96,16 +95,3 @@ export const AboutTab = (): React.ReactElement => { ); }; - -export const getAppTitlesToDisplay = ( - appMetadataTitles: ServiceNames<(typeof appLangCodesData)[number]>, - appLangCodesData: string[], -): ServiceNames<(typeof appLangCodesData)[number]> => { - const recommendedLanguages: string[] = Object.keys(RecommendedLanguageFlags); - const appLangCodesIncludingRecommended: string[] = ArrayUtils.removeDuplicates( - recommendedLanguages.concat(Object.keys(appMetadataTitles)).concat(appLangCodesData), - ); - return Object.fromEntries( - appLangCodesIncludingRecommended.map((lang) => [lang, appMetadataTitles[lang]]), - ); -}; diff --git a/frontend/app-development/test/mocks.tsx b/frontend/app-development/test/mocks.tsx index 61f783d01db..5389e6c8949 100644 --- a/frontend/app-development/test/mocks.tsx +++ b/frontend/app-development/test/mocks.tsx @@ -5,6 +5,7 @@ import type { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { BrowserRouter } from 'react-router-dom'; import { PreviewConnectionContextProvider } from 'app-shared/providers/PreviewConnectionContext'; + import { queriesMock } from 'app-shared/mocks/queriesMock'; import type { QueryClient } from '@tanstack/react-query'; import { queryClientConfigMock } from 'app-shared/mocks/queryClientMock'; From 019bd230823a30098b7e550d893179f21d3cafde Mon Sep 17 00:00:00 2001 From: Nina Kylstad Date: Fri, 31 Jan 2025 14:03:12 +0100 Subject: [PATCH 5/5] removed flags, and coderabbit comments --- .../Tabs/AboutTab/InputFields/InputFields.tsx | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx index c25ebd350d1..d03cfaa2f68 100644 --- a/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx +++ b/frontend/app-development/layout/PageHeader/SubHeader/SettingsModalButton/SettingsModal/components/Tabs/AboutTab/InputFields/InputFields.tsx @@ -13,12 +13,6 @@ export type ServiceNames = { [key in T]: string | undefined; }; -export enum RecommendedLanguageFlags { - nb = '🇳🇴', - nn = '🇳🇴', - en = '🇬🇧', -} - export type InputFieldsProps = { appLangCodes: string[]; onSave: (serviceName: string, language: string) => void; @@ -78,7 +72,7 @@ type EditServiceNameForLanguageProps = { appLangCodes: string[]; language: string; onSave: (serviceName: string, language: string) => void; - serviceName: string; + serviceName: string | undefined; }; function EditServiceNameForLanguage({ @@ -97,7 +91,7 @@ function EditServiceNameForLanguage({ if (isValid) onSave(event.target.value, language); }; - const validateServiceName = (newServiceName: string): Boolean => { + const validateServiceName = (newServiceName: string): boolean => { if (newServiceName.length <= 0) { setServiceNameError(t('settings_modal.about_tab_name_error')); return false; @@ -111,7 +105,6 @@ function EditServiceNameForLanguage({ return ( } size='small' label={t(`language.${language}`)} description={description} @@ -123,12 +116,3 @@ function EditServiceNameForLanguage({ /> ); } - -type FlagIconProps = { - language: string; -}; - -const FlagIcon = ({ language }: FlagIconProps): React.ReactNode => { - const languageHasFlag = !!RecommendedLanguageFlags[language]; - return languageHasFlag ? {RecommendedLanguageFlags[language]} : null; -};