diff --git a/frontend/language/src/nb.json b/frontend/language/src/nb.json index 6e77cad7e0a..5cda2b68054 100644 --- a/frontend/language/src/nb.json +++ b/frontend/language/src/nb.json @@ -1334,7 +1334,7 @@ "ux_editor.component_properties.addButton": "'Legg til'-knapp", "ux_editor.component_properties.alertOnChange": "Bruker skal få advarsel når de gjør endring", "ux_editor.component_properties.alertOnDelete": "Bruker skal få advarsel når de sletter", - "ux_editor.component_properties.align": "Plassering*", + "ux_editor.component_properties.align": "Plassering av tekstinnhold", "ux_editor.component_properties.alwaysShowAddButton": "Vis alltid 'Legg til'-knapp", "ux_editor.component_properties.attribution": "Opphav", "ux_editor.component_properties.autocomplete": "HTML autofullfør", @@ -1347,6 +1347,7 @@ "ux_editor.component_properties.compact": "Bruk kompakt visning", "ux_editor.component_properties.componentRef": "ID på komponenten det gjelder (componentRef)", "ux_editor.component_properties.config_is_expression_message": "Denne egenskapen er konfigurert som et uttrykk, og kan foreløpig ikke redigeres her.", + "ux_editor.component_properties.currency": "Valuta", "ux_editor.component_properties.current_task": "Kun gjeldende oppgave", "ux_editor.component_properties.dataListId": "Id på dataliste", "ux_editor.component_properties.dataTypeIds": "Liste med ID på datatyper som skal vises", @@ -1357,6 +1358,51 @@ "ux_editor.component_properties.edit": "Redigeringsvalg", "ux_editor.component_properties.editButton": "'Endre'-knapp", "ux_editor.component_properties.elements": "Elementer som skal vises (når ingen er satt vises alle)", + "ux_editor.component_properties.enum_All": "Alt", + "ux_editor.component_properties.enum_AllExceptRequired": "Alle utenom påkrevd", + "ux_editor.component_properties.enum_Component": "Komponent", + "ux_editor.component_properties.enum_CustomBackend": "Egendefinert i kode", + "ux_editor.component_properties.enum_Expression": "Uttrykk", + "ux_editor.component_properties.enum_Required": "Påkrevd", + "ux_editor.component_properties.enum_Schema": "Skjema", + "ux_editor.component_properties.enum_always": "Alltid", + "ux_editor.component_properties.enum_asc": "Alfabetisk", + "ux_editor.component_properties.enum_auto": "Automatisk", + "ux_editor.component_properties.enum_avoid": "Unngå sideskift", + "ux_editor.component_properties.enum_bottom": "Bunn", + "ux_editor.component_properties.enum_celsius": "Celsius", + "ux_editor.component_properties.enum_center": "Senter", + "ux_editor.component_properties.enum_centimeter": "Centimeter", + "ux_editor.component_properties.enum_day": "Dag", + "ux_editor.component_properties.enum_degree": "Grader", + "ux_editor.component_properties.enum_desc": "Motsatt alfabetisk", + "ux_editor.component_properties.enum_foot": "Fot", + "ux_editor.component_properties.enum_gram": "Gram", + "ux_editor.component_properties.enum_hectare": "Hekto", + "ux_editor.component_properties.enum_hour": "Time", + "ux_editor.component_properties.enum_inch": "Tomme", + "ux_editor.component_properties.enum_kilogram": "Kilogram", + "ux_editor.component_properties.enum_kilometer": "Kilometer", + "ux_editor.component_properties.enum_left": "Venstre", + "ux_editor.component_properties.enum_liter": "Liter", + "ux_editor.component_properties.enum_meter": "Meter", + "ux_editor.component_properties.enum_milliliter": "Milliliter", + "ux_editor.component_properties.enum_millimeter": "Millimeter", + "ux_editor.component_properties.enum_millisecond": "Millisekund", + "ux_editor.component_properties.enum_minute": "Minutt", + "ux_editor.component_properties.enum_month": "Måned", + "ux_editor.component_properties.enum_neutral": "Nøytral", + "ux_editor.component_properties.enum_percent": "Prosent", + "ux_editor.component_properties.enum_prefix": "Prefiks (Før tall)", + "ux_editor.component_properties.enum_right": "Høyre", + "ux_editor.component_properties.enum_search": "Søk", + "ux_editor.component_properties.enum_second": "Sekund", + "ux_editor.component_properties.enum_subtle": "Forsiktig", + "ux_editor.component_properties.enum_suffix": "Suffiks (Etter tall)", + "ux_editor.component_properties.enum_text": "Tekst", + "ux_editor.component_properties.enum_top": "Topp", + "ux_editor.component_properties.enum_week": "Uke", + "ux_editor.component_properties.enum_year": "År", "ux_editor.component_properties.excludedChildren": "Komponenter som skal ekskluderes fra gruppens oppsummering", "ux_editor.component_properties.filter": "Filter", "ux_editor.component_properties.format": "Format", @@ -1396,6 +1442,7 @@ "ux_editor.component_properties.pageBreak": "PDF-innstillinger (pageBreak)", "ux_editor.component_properties.pageRef": "Navnet til siden det gjelder (pageRef)", "ux_editor.component_properties.pagination": "Paginering", + "ux_editor.component_properties.position": "Plassering av valuta", "ux_editor.component_properties.preselectedOptionIndex": "Indeks/plassering av forhåndsvalgt verdi (preselectedOptionIndex)", "ux_editor.component_properties.queryParameters": "Parametere i spørringen", "ux_editor.component_properties.readOnly": "Feltet skal være skrivebeskyttet (readOnly)", @@ -1437,6 +1484,7 @@ "ux_editor.component_properties.taskId": "Oppgave-ID", "ux_editor.component_properties.timeStamp": "Inkluder tidsstempel i dato (på som standard)", "ux_editor.component_properties.triggers": "Feltet skal trigge:", + "ux_editor.component_properties.unit": "Enhet", "ux_editor.component_properties.url": "Lenke (URL)", "ux_editor.component_properties.useComponentGrid": "Bruk komponentens grid-oppsett", "ux_editor.component_properties.validFileEndings": "Skriv inn gyldige filtyper (Eksempel: .jpeg, .pdf, .png)", diff --git a/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.tsx b/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.tsx index fb7ae936005..ba1f6a220f1 100644 --- a/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.tsx +++ b/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.tsx @@ -43,6 +43,7 @@ export function LayoutSetsContainer() { onChange={(event) => onLayoutSetClick(event.target.value)} value={selectedFormLayoutSetName} className={classes.layoutSetsDropDown} + size='small' > {layoutSetNames.map((set: string) => { return ( diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.module.css b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.module.css new file mode 100644 index 00000000000..6825b4ad8c0 --- /dev/null +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.module.css @@ -0,0 +1,3 @@ +.objectPropertyContainer { + gap: 0.5rem; +} diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx index f74c62576b7..683fc7bb28c 100644 --- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx +++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx @@ -14,6 +14,7 @@ import { EditGrid } from './editModal/EditGrid'; import type { FormItem } from '../../types/FormItem'; import type { UpdateFormMutateOptions } from '../../containers/FormItemContext'; import { useComponentPropertyDescription } from '../../hooks/useComponentPropertyDescription'; +import classes from './FormComponentConfig.module.css'; export interface IEditFormComponentProps { editFormId: string; @@ -196,7 +197,7 @@ export const FormComponentConfig = ({ {/** Object properties */} {objectPropertyKeys.map((propertyKey) => { return ( - + {componentPropertyLabel(propertyKey)} diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx index 56f13a1b107..49bafb58d01 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditStringValue.tsx @@ -5,6 +5,7 @@ import { FormField } from '../../FormField'; import { Textfield } from '@digdir/designsystemet-react'; import { LegacySelect } from '@digdir/design-system-react'; import { useComponentPropertyLabel } from '../../../hooks/useComponentPropertyLabel'; +import { useComponentPropertyEnumValue } from '@altinn/ux-editor/hooks/useComponentPropertyEnumValue'; export interface EditStringValueProps extends IGenericEditComponent { propertyKey: string; @@ -23,6 +24,7 @@ export const EditStringValue = ({ }: EditStringValueProps) => { const { t } = useTranslation(); const componentPropertyLabel = useComponentPropertyLabel(); + const componentEnumValue = useComponentPropertyEnumValue(); const handleValueChange = (newValue: string) => { handleComponentChange({ @@ -49,7 +51,7 @@ export const EditStringValue = ({ ({ - label: value, + label: componentEnumValue(value), value: value, }))} onChange={(e: any) => fieldProps.onChange(e)} diff --git a/frontend/packages/ux-editor/src/hooks/useComponentPropertyEnumValue.test.ts b/frontend/packages/ux-editor/src/hooks/useComponentPropertyEnumValue.test.ts new file mode 100644 index 00000000000..187acfa2a38 --- /dev/null +++ b/frontend/packages/ux-editor/src/hooks/useComponentPropertyEnumValue.test.ts @@ -0,0 +1,37 @@ +import { renderHook } from '@testing-library/react'; +import { textMock } from '@studio/testing/mocks/i18nMock'; +import { useComponentPropertyEnumValue } from '@altinn/ux-editor/hooks/useComponentPropertyEnumValue'; +import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs'; + +const enumValueWithoutText = 'enumValueWithoutText'; +const customTextMockToHandleUndefined = ( + keys: string | string[], + variables?: KeyValuePairs, +) => { + const key = Array.isArray(keys) ? keys[0] : keys; + if (key === `ux_editor.component_properties.enum_${enumValueWithoutText}`) return key; + return variables + ? '[mockedText(' + key + ', ' + JSON.stringify(variables) + ')]' + : '[mockedText(' + key + ')]'; +}; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: customTextMockToHandleUndefined, + }), +})); + +describe('useComponentPropertyEnumValue', () => { + it('Returns a function that returns the enum value', () => { + const result = renderHook(() => useComponentPropertyEnumValue()).result.current; + const propertyEnumValue = result('testEnumValue'); + expect(propertyEnumValue).toEqual( + textMock('ux_editor.component_properties.enum_testEnumValue'), + ); + }); + it('Returns a function that returns the enum value if there was no text key for the description', () => { + const result = renderHook(() => useComponentPropertyEnumValue()).result.current; + const propertyEnumValue = result(enumValueWithoutText); + expect(propertyEnumValue).toEqual(enumValueWithoutText); + }); +}); diff --git a/frontend/packages/ux-editor/src/hooks/useComponentPropertyEnumValue.ts b/frontend/packages/ux-editor/src/hooks/useComponentPropertyEnumValue.ts new file mode 100644 index 00000000000..b2d4ff09d7a --- /dev/null +++ b/frontend/packages/ux-editor/src/hooks/useComponentPropertyEnumValue.ts @@ -0,0 +1,14 @@ +import { useTranslation } from 'react-i18next'; +import { useCallback } from 'react'; + +export const useComponentPropertyEnumValue = () => { + const { t } = useTranslation(); + return useCallback( + (value: string) => { + const translationKey: string = `ux_editor.component_properties.enum_${value}`; + const translation = t(translationKey); + return translation === translationKey ? value : translation; + }, + [t], + ); +}; diff --git a/frontend/packages/ux-editor/src/utils/component.test.ts b/frontend/packages/ux-editor/src/utils/component.test.ts index 93a3f1f428c..1d201661936 100644 --- a/frontend/packages/ux-editor/src/utils/component.test.ts +++ b/frontend/packages/ux-editor/src/utils/component.test.ts @@ -242,200 +242,208 @@ describe('Component utils', () => { }); }); - it('should return false if property ref is not supported', () => { - [PropertyTypes.object, PropertyTypes.array].forEach((type) => { - expect( - isPropertyTypeSupported({ - $ref: getExpressionSchemaDefinitionReference(type), - }), - ).toBe(false); - }); - }); - - it('should return false if property ref is not supported', () => { + it('should return true if property enum is supported', () => { expect( isPropertyTypeSupported({ - $ref: 'test', + enum: ['enumValue1', 'enumValue2'], }), - ).toBe(false); + ).toBe(true); }); + }); - it('should return true if property type is supported and propertyKey is undefined', () => { + it('should return false if property ref is not supported', () => { + [PropertyTypes.object, PropertyTypes.array].forEach((type) => { expect( isPropertyTypeSupported({ - type: 'string', + $ref: getExpressionSchemaDefinitionReference(type), }), - ).toBe(true); + ).toBe(false); }); }); - describe('getExpressionSchemaDefinitionReference', () => { - it('should return correct reference for given type', () => { - expect(getExpressionSchemaDefinitionReference(PropertyTypes.array)).toBe( - `${EXPRESSION_SCHEMA_BASE_DEFINITION_REFERENCE}array`, - ); - }); + it('should return false if property ref is not supported', () => { + expect( + isPropertyTypeSupported({ + $ref: 'test', + }), + ).toBe(false); }); - describe('propertyTypeMatcher', () => { - it('should return false if property does not exist', () => { - expect(propertyTypeMatcher(undefined, PropertyTypes.string)).toBe(false); - }); + it('should return true if property type is supported and propertyKey is undefined', () => { + expect( + isPropertyTypeSupported({ + type: 'string', + }), + ).toBe(true); + }); +}); - it('should return true if property type matches', () => { - expect(propertyTypeMatcher({ type: 'string' }, PropertyTypes.string)).toBe(true); - }); +describe('getExpressionSchemaDefinitionReference', () => { + it('should return correct reference for given type', () => { + expect(getExpressionSchemaDefinitionReference(PropertyTypes.array)).toBe( + `${EXPRESSION_SCHEMA_BASE_DEFINITION_REFERENCE}array`, + ); + }); +}); - it('should return false if property type does not match', () => { - expect(propertyTypeMatcher({ type: 'number' }, PropertyTypes.string)).toBe(false); - }); +describe('propertyTypeMatcher', () => { + it('should return false if property does not exist', () => { + expect(propertyTypeMatcher(undefined, PropertyTypes.string)).toBe(false); + }); - it('should return true if property has a supported ref', () => { - expect( - propertyTypeMatcher( - { - $ref: getExpressionSchemaDefinitionReference(PropertyTypes.string), - }, - PropertyTypes.string, - ), - ).toBe(true); - }); + it('should return true if property type matches', () => { + expect(propertyTypeMatcher({ type: 'string' }, PropertyTypes.string)).toBe(true); + }); - it('should return true for a property of string type with enum even if type: string is not defined explicitly', () => { - expect( - propertyTypeMatcher( - { - enum: ['test'], - }, - PropertyTypes.string, - ), - ).toBe(true); - }); + it('should return false if property type does not match', () => { + expect(propertyTypeMatcher({ type: 'number' }, PropertyTypes.string)).toBe(false); + }); - it('should return false for a property with no type defined and no enum defined', () => { - expect(propertyTypeMatcher({ something: 'test' }, PropertyTypes.string)).toBe(false); - }); + it('should return true if property has a supported ref', () => { + expect( + propertyTypeMatcher( + { + $ref: getExpressionSchemaDefinitionReference(PropertyTypes.string), + }, + PropertyTypes.string, + ), + ).toBe(true); + }); - it('should return true for a property of array type with items that have enum value', () => { - expect( - propertyTypeMatcher( - { - type: 'array', - items: { - enum: ['test'], - }, + it('should return true for a property of string type with enum even if type: string is not defined explicitly', () => { + expect( + propertyTypeMatcher( + { + enum: ['test'], + }, + PropertyTypes.string, + ), + ).toBe(true); + }); + + it('should return false for a property with no type defined and no enum defined', () => { + expect(propertyTypeMatcher({ something: 'test' }, PropertyTypes.string)).toBe(false); + }); + + it('should return true for a property of array type with items that have enum value', () => { + expect( + propertyTypeMatcher( + { + type: 'array', + items: { + enum: ['test'], }, - PropertyTypes.array, - ), - ).toBe(true); - }); + }, + PropertyTypes.array, + ), + ).toBe(true); + }); - it('should return false for a property of array type with items that have no enum value', () => { - expect( - propertyTypeMatcher( - { - type: 'array', - items: { - type: 'string', - }, + it('should return false for a property of array type with items that have no enum value', () => { + expect( + propertyTypeMatcher( + { + type: 'array', + items: { + type: 'string', }, - PropertyTypes.array, - ), - ).toBe(false); - }); + }, + PropertyTypes.array, + ), + ).toBe(false); + }); - it('should return false for a property of array type with no items defined', () => { - expect(propertyTypeMatcher({ type: 'array' }, PropertyTypes.array)).toBe(false); - }); + it('should return false for a property of array type with no items defined', () => { + expect(propertyTypeMatcher({ type: 'array' }, PropertyTypes.array)).toBe(false); + }); - it('should return false for a property of object type with no properties defined', () => { - expect(propertyTypeMatcher({ type: 'object' }, PropertyTypes.object)).toBe(false); - }); + it('should return false for a property of object type with no properties defined', () => { + expect(propertyTypeMatcher({ type: 'object' }, PropertyTypes.object)).toBe(false); + }); - it('should return false for a property of object type with additionalProperties defined', () => { - expect( - propertyTypeMatcher( - { - type: 'object', - properties: {}, - additionalProperties: { - type: 'string', - }, + it('should return false for a property of object type with additionalProperties defined', () => { + expect( + propertyTypeMatcher( + { + type: 'object', + properties: {}, + additionalProperties: { + type: 'string', }, - PropertyTypes.object, - ), - ).toBe(false); - }); + }, + PropertyTypes.object, + ), + ).toBe(false); + }); - it('should return true for a property of object type with defined properties and no additionalProperties', () => { - expect( - propertyTypeMatcher( - { - type: 'object', - properties: { - testProperty: { - type: 'string', - }, + it('should return true for a property of object type with defined properties and no additionalProperties', () => { + expect( + propertyTypeMatcher( + { + type: 'object', + properties: { + testProperty: { + type: 'string', }, }, - PropertyTypes.object, - ), - ).toBe(true); - }); + }, + PropertyTypes.object, + ), + ).toBe(true); }); +}); - describe('getSupportedPropertyKeysForPropertyType', () => { - it('should return empty array if no properties are provided', () => { - expect(getSupportedPropertyKeysForPropertyType({}, [PropertyTypes.string])).toEqual([]); - }); +describe('getSupportedPropertyKeysForPropertyType', () => { + it('should return empty array if no properties are provided', () => { + expect(getSupportedPropertyKeysForPropertyType({}, [PropertyTypes.string])).toEqual([]); + }); - it('should return empty array if no property keys are of the expected property types', () => { - expect( - getSupportedPropertyKeysForPropertyType( - { - testProperty: { - type: 'number', - }, + it('should return empty array if no property keys are of the expected property types', () => { + expect( + getSupportedPropertyKeysForPropertyType( + { + testProperty: { + type: 'number', }, - [PropertyTypes.string], - ), - ).toEqual([]); - }); + }, + [PropertyTypes.string], + ), + ).toEqual([]); + }); - it('should return array of property keys of the expected property types', () => { - expect( - getSupportedPropertyKeysForPropertyType( - { - testProperty: { - type: 'string', - }, - testProperty2: { - type: 'number', - }, + it('should return array of property keys of the expected property types', () => { + expect( + getSupportedPropertyKeysForPropertyType( + { + testProperty: { + type: 'string', }, - [PropertyTypes.string], - ), - ).toEqual(['testProperty']); - }); + testProperty2: { + type: 'number', + }, + }, + [PropertyTypes.string], + ), + ).toEqual(['testProperty']); + }); - it('should only return property keys that are not in the excludeKeys array', () => { - expect( - getSupportedPropertyKeysForPropertyType( - { - testProperty: { - type: 'string', - }, - testProperty1: { - type: 'string', - }, - testProperty2: { - type: 'number', - }, + it('should only return property keys that are not in the excludeKeys array', () => { + expect( + getSupportedPropertyKeysForPropertyType( + { + testProperty: { + type: 'string', }, - [PropertyTypes.string], - ['testProperty'], - ), - ).toEqual(['testProperty1']); - }); + testProperty1: { + type: 'string', + }, + testProperty2: { + type: 'number', + }, + }, + [PropertyTypes.string], + ['testProperty'], + ), + ).toEqual(['testProperty1']); }); }); diff --git a/frontend/packages/ux-editor/src/utils/component.ts b/frontend/packages/ux-editor/src/utils/component.ts index 684f5fa08be..88363401c3c 100644 --- a/frontend/packages/ux-editor/src/utils/component.ts +++ b/frontend/packages/ux-editor/src/utils/component.ts @@ -64,7 +64,7 @@ export const propertyTypeMatcher = (property: KeyValuePairs, propertyType: Prope // Currently only supporting array of strings with specified enum values return baseMatch && !!property.items?.enum; case PropertyTypes.object: - // Currently only supporting object with specifiec properties and no additional properties + // Currently only supporting object with specific properties and no additional properties return baseMatch && !!property.properties && !property.additionalProperties; default: return baseMatch; @@ -192,6 +192,7 @@ const supportedPropertyRefs = supportedPropertyTypes * @returns A boolean indicating if the property is supported. */ export const isPropertyTypeSupported = (property: KeyValuePairs) => { + if (property.enum) return true; if (property?.$ref) { return supportedPropertyRefs.includes(property.$ref); }