UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

520 lines (470 loc) 17.4 kB
--- title: 'Field.PostalCodeAndCity' description: '`Field.PostalCodeAndCity` is a wrapper component for input of two separate values with user experience tailored for postal code and city values.' version: 10.104.0 generatedAt: 2026-04-17T18:46:12.732Z checksum: 090b7d977ba4be5e2c4c04d199a30a4048416c59f443a56985df2f80629d9c40 --- # Field.PostalCodeAndCity ## Import ```tsx import { Field } from '@dnb/eufemia/extensions/forms' render(<Field.PostalCodeAndCity />) ``` ## Description `Field.PostalCodeAndCity` is a wrapper component for input of two separate [input of strings](/uilib/extensions/forms/base-fields/String) with user experience tailored for postal code and city values. These fields are meant for Norwegian postal codes and cities. The postal code input takes a 4-digit string as values, since it's meant for Norwegian postal codes. A Norwegian postal code can have a leading zero, which is why the value is a string and not a number. More info about postal codes can be found at [Posten](https://www.bring.no/tjenester/adressetjenester/postnummer). It supports the HTML [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, and by default set it to `postal-code` for the postal code field, and to `address-level2` for the city field. There is a corresponding [Value.PostalCodeAndCity](/uilib/extensions/forms/Value/PostalCodeAndCity) component. ## Relevant links - [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/extensions/forms/Field/PostalCodeAndCity) - [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity) ## Validation and autofill Read more about how to use the [Bring API](/uilib/extensions/forms/Connectors/Bring/) to validate and autofill a postal code and city name. ## Demos ### Empty ```tsx render( <Field.PostalCodeAndCity postalCode={{ onChange: (value) => console.log('postalCode onChange', value), }} city={{ onChange: (value) => console.log('city onChange', value), }} /> ) ``` ### Placeholder ```tsx render( <Field.PostalCodeAndCity postalCode={{ placeholder: '????', onChange: (value) => console.log('postalCode onChange', value), }} city={{ placeholder: 'Your city', onChange: (value) => console.log('city onChange', value), }} /> ) ``` ### Label ```tsx render( <Field.PostalCodeAndCity postalCode={{ label: 'PNR', onChange: (value) => console.log('postalCode onChange', value), }} city={{ label: 'CTY', onChange: (value) => console.log('city onChange', value), }} /> ) ``` ### Label and value ```tsx render( <Field.PostalCodeAndCity postalCode={{ label: 'Pnr.', value: '0788', onChange: (value) => console.log('postalCode onChange', value), }} city={{ value: 'Oslo', onChange: (value) => console.log('city onChange', value), }} /> ) ``` ### Iterate over array By using the `itemPath` property, you can iterate over an array and use the `postalCode` and `city` properties to render the fields. ```tsx render( <Iterate.Array value={[ { postalCode: '0788', city: 'Oslo', }, { postalCode: '0789', city: 'Bergen', }, ]} > <Field.PostalCodeAndCity postalCode={{ itemPath: '/postalCode', }} city={{ itemPath: '/city', }} /> </Iterate.Array> ) ``` ### Disabled ```tsx render( <Field.PostalCodeAndCity postalCode={{ value: '1234', disabled: true, onChange: (value) => console.log('postalCode onChange', value), }} city={{ value: 'Oslo', disabled: true, onChange: (value) => console.log('city onChange', value), }} /> ) ``` ### With help ```tsx render( <Field.PostalCodeAndCity postalCode={{ onChange: (value) => console.log('postalCode onChange', value), }} city={{ onChange: (value) => console.log('city onChange', value), }} help={{ title: 'Help is available', content: 'Helping others, encouraging others, are often acts of being kind that have more meaning that you may realize.', }} /> ) ``` ### Error ```tsx render( <Field.PostalCodeAndCity postalCode={{}} city={{}} error={new Error('This is what is wrong...')} /> ) ``` ### Validation - Required ```tsx render( <Field.PostalCodeAndCity postalCode={{ required: true, }} city={{ required: true, }} /> ) ``` ### Path Based Country The `country` property supports a field path as value. This allows you to set the `country` based on the value of another field. ```tsx render( <Form.Handler> <Form.Card> <Field.SelectCountry path="/country" defaultValue="NO" /> <Field.PostalCodeAndCity countryCode="/country" /> </Form.Card> </Form.Handler> ) ``` ### Non-Norwegian Postal Codes If you want to allow for a postal code that is not Norwegian, just set the `country` property to the desired country, and add your own custom validation. NB: As of today, setting `country` property to anything other than `NO` will only remove the default norwegian postal code pattern, mask, and placeholder, but not actually set the postal code pattern, mask, and placeholder for the value provided to the `country` property. This functionality will hopefully be implemented in the future. ```tsx render( <Form.Handler translations={{ 'nb-NO': { 'PostalCode.errorPattern': 'Dette er ikke et gyldig postnummer (fem siffer).', }, 'en-GB': { 'PostalCode.errorPattern': 'This is not a valid postal code (five-digits).', }, }} > <Field.PostalCodeAndCity countryCode="DE" postalCode={{ pattern: '^[0-9]{5}$', onBlurValidator: undefined, mask: [/\\d/, /\\d/, /\\d/, /\\d/, /\\d/], placeholder: '00000', width: '5.4rem', }} city={{ pattern: '^[a-zA-ZäöüÄÖÜß -]+$', width: 'stretch', }} /> </Form.Handler> ) ``` ```tsx render( <Field.PostalCodeAndCity postalCode={{ label: 'With a very long label', }} city={{ label: 'With a very long label', }} /> ) ``` ## Properties ### Field-specific properties ```json { "props": { "countryCode": { "doc": "Defines which country the postal code and city is for, based on the [ISO 3166-1 alpha-2 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) i.e. `NO`, `DE` etc. Setting it to anything other than `NO` will remove the default norwegian postal code pattern. You can also use the value of another field to define the countryCode, by using a path value i.e. `/myCountryCodePath`. Defaults to `NO`.", "type": ["Path", "string"], "status": "optional" }, "postalCode": { "doc": "Properties for the [Field.String](/uilib/extensions/forms/base-fields/String/) component for postal code.", "type": "object", "status": "required" }, "city": { "doc": "Properties for the [Field.String](/uilib/extensions/forms/base-fields/String/) component for city.", "type": "object", "status": "required" }, "help": { "doc": "Provide help content for the field using `title` and `content` as a string or React.Node. Additionally, you can set `open` to `true` to display the inline help, set the `breakout` property to `false` to disable the breakout of the inline help content, set `outset` to `false` to display the help text inline (inset) instead of the default outset behavior, or use `renderAs` set to `dialog` to render the content in a [Dialog](/uilib/components/dialog/) (recommended for larger amounts of content).", "type": "object", "status": "optional" }, "size": { "doc": "The sizes you can choose is `default` (2rem), `medium` (2.5rem) and `large` (3rem) are supported component sizes. Defaults to `default` / `null`. Also, if you define a number like `size={2}` then it will be forwarded as the input element attribute. Consider rather setting field sizes with [Form.Appearance](/uilib/extensions/forms/Form/Appearance/).", "type": ["string", "number"], "status": "optional" } } } ``` ### General properties ```json { "props": { "label": { "doc": "Label text displayed above the field. Most fields already have a default label, so check the field translations for an existing label entry. Only set `label` when you need to override the default.", "type": "string", "status": "optional" }, "labelDescription": { "doc": "A more discreet text displayed beside the label (i.e for \"(optional)\").", "type": "string", "status": "optional" }, "labelDescriptionInline": { "doc": "If true, the `labelDescription` will be displayed on the same line as the label.", "type": "boolean", "status": "optional" }, "labelSrOnly": { "doc": "Use `true` to make the label only readable by screen readers.", "type": "boolean", "status": "optional" }, "labelSize": { "doc": "Define one of the following [heading sizes](/uilib/elements/heading/): `medium` or `large`.", "type": ["string", "false"], "status": "optional" }, "help": { "doc": "Provide help content for the field using `title` and `content` as a string or React.Node. Additionally, you can set `open` to `true` to display the inline help, set the `breakout` property to `false` to disable the breakout of the inline help content, set `outset` to `false` to display the help text inline (inset) instead of the default outset behavior, or use `renderAs` set to `dialog` to render the content in a [Dialog](/uilib/components/dialog/) (recommended for larger amounts of content).", "type": "object", "status": "optional" }, "hideHelpButton": { "doc": "Set `true` when you render the inline help button outside the label (e.g. inside a checkbox suffix) so FieldBlock skips drawing the default label help button.", "type": "boolean", "status": "optional" }, "statusPosition": { "doc": "Controls where status messages (`error`, `warning`, `info`) are visually shown. Use `below` (default) or `above`.", "type": ["\"below\"", "\"above\""], "status": "optional" }, "layout": { "doc": "Layout for the label and input. Can be `horizontal` or `vertical`.", "type": "string", "status": "optional" }, "layoutOptions": { "doc": "Use this to set additional options for the `horizontal` layout. E.g. `{ width: \"medium\" }`. You can also use a custom width `{number}rem`. Instead of a width, you can use a min/max width. E.g. `{ minWidth: \"6rem\", maxWidth: \"12rem\" }`.", "type": "object", "status": "optional" }, "width": { "doc": "Will set the width for the whole block. Use `small`, `medium`, `large` for predefined standard widths. You can also set a custom width `{number}rem` or use `stretch` or `false`.", "type": ["string", "false"], "status": "optional" }, "contentWidth": { "doc": "Will set the width for its contents. Use `small`, `medium`, `large` for predefined standard widths. You can also set a custom width `{number}rem` or use `stretch` or `false`.", "type": ["string", "false"], "status": "optional" }, "[Space](/uilib/layout/space/properties)": { "doc": "Spacing properties like `top` or `bottom` are supported.", "type": ["string", "object"], "status": "optional" }, "labelHeight": { "doc": "Defines the height of an component (size prop), so the label can be aligned correctly. Can be `default`, `small`, `medium`, `large`.", "type": "string", "status": "optional" }, "asFieldset": { "doc": "Use `true` when you have several form elements. This way a `fieldset` with a `legend` is used.", "type": "boolean", "status": "optional" }, "align": { "doc": "`center` or `bottom` for aligning the contents vertically. Defaults to `bottom`.", "type": ["string", "false"], "status": "optional" }, "disableStatusSummary": { "doc": "Use `true` to disable the error summary.", "type": "boolean", "status": "optional" }, "composition": { "doc": "Use `true` for when you have more than one field wrapped.", "type": "true", "status": "optional" }, "disabled": { "doc": "Set `true` to make the inner [FormLabel](/uilib/components/form-label/) behave as disabled.", "type": "boolean", "status": "optional" } }, "pick": ["width", "help", "[Space](/uilib/layout/space/properties)"] } ``` ## Translations ```json { "locales": ["da-DK", "en-GB", "nb-NO", "sv-SE"], "entries": { "City.errorPattern": { "nb-NO": "Du må skrive inn et gyldig stedsnavn. Kun bokstaver, bindestrek og mellomrom er tillatt.", "en-GB": "You must enter a valid city name. Only letters, hyphens and spaces are allowed.", "sv-SE": "Du måste ange ett giltigt ortsnamn. Endast bokstäver, bindestreck och mellanslag är tillåtna.", "da-DK": "Du skal indtaste et gyldigt bynavn. Kun bogstaver, bindestreger og mellemrum er tilladt." }, "City.errorRequired": { "nb-NO": "Du må fylle inn et stedsnavn.", "en-GB": "You must enter a city name.", "sv-SE": "Du måste fylla i ett ortsnamn.", "da-DK": "Du skal udfylde et bynavn." }, "City.label": { "nb-NO": "Sted", "en-GB": "City", "sv-SE": "Ort", "da-DK": "By" }, "Field.errorPattern": { "nb-NO": "Du må skrive inn en gyldig verdi.", "en-GB": "You must enter a valid value.", "sv-SE": "Du måste ange ett giltigt värde.", "da-DK": "Du skal indtaste en gyldig værdi." }, "Field.errorRequired": { "nb-NO": "Dette feltet må fylles ut.", "en-GB": "This field is required.", "sv-SE": "Detta fält måste fyllas i.", "da-DK": "Dette felt skal udfyldes." }, "Field.errorSummary": { "nb-NO": "Feil som må rettes:", "en-GB": "Please correct the following errors:", "sv-SE": "Fel som måste åtgärdas:", "da-DK": "Felter der skal rettes:" }, "Field.errorSummaryTitle": { "nb-NO": "Feil som må rettes", "en-GB": "Please correct the following errors", "sv-SE": "Fel som måste åtgärdas", "da-DK": "Felter der skal rettes" }, "Field.optionalLabelSuffix": { "nb-NO": "(valgfritt)", "en-GB": "(optional)", "sv-SE": "(valfritt)", "da-DK": "(valgfrit)" }, "Field.stateSummary": { "nb-NO": "Oppsummering:", "en-GB": "Summary:", "sv-SE": "Sammanfattning:", "da-DK": "Oversigt:" }, "PostalCode.errorPattern": { "nb-NO": "Du må skrive inn et gyldig postnummer (fire siffer).", "en-GB": "You must enter a valid postcode (4 digits).", "sv-SE": "Du måste ange ett giltigt postnummer (fyra siffror).", "da-DK": "Du skal indtaste et gyldigt postnummer (fire cifre)." }, "PostalCode.errorRequired": { "nb-NO": "Du må fylle inn et postnummer.", "en-GB": "You must enter a postcode.", "sv-SE": "Du måste fylla i ett postnummer.", "da-DK": "Du skal udfylde et postnummer." }, "PostalCode.label": { "nb-NO": "Postnr.", "en-GB": "Postcode", "sv-SE": "Postnr.", "da-DK": "Postnr." } } } ``` ## Events ```json { "props": { "onChange": { "doc": "Will be called on value changes made by the user, with the new value as argument. When an `async` function is used, the corresponding [FieldBlock](/uilib/extensions/forms/create-component/FieldBlock/) will show an indicator on the field label. You can return `{ success: 'saved' } as const` to show a success symbol, or an error or an object with these keys `{ info: 'Info message', warning: 'Warning message', error: Error('My error') } as const`. The second parameter is an object that e.g. contains `props` (all given `Field.*` properties).", "type": "(value) => void", "status": "optional" }, "onFocus": { "doc": "Will be called when the component gets into focus. Like clicking inside a text input or opening a dropdown. Called with active value as argument. The second parameter is an object that e.g. contains `props` (all given `Field.*` properties).", "type": "(value) => void", "status": "optional" }, "onBlur": { "doc": "Will be called when the component stop being in focus. Like when going to next field, or closing a dropdown. Called with active value as argument. The second parameter is an object that e.g. contains `props` (all given `Field.*` properties).", "type": "(value) => void", "status": "optional" }, "onStatusChange": { "doc": "Called whenever the status messages (info, warning or error) gets visible or changes. Receives the current `{ info, warning, error }` object.", "type": "({ info?, warning?, error? }: FieldStatus) => void", "status": "optional" } } } ```