fds-vue-core 8.2.4 → 8.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fds-vue-core",
3
- "version": "8.2.4",
3
+ "version": "8.3.1",
4
4
  "description": "FDS Vue Core Component Library",
5
5
  "type": "module",
6
6
  "main": "./dist/fds-vue-core.cjs.js",
@@ -52,28 +52,29 @@
52
52
  "date-fns": "4.4.0",
53
53
  "imask": "7.6.1",
54
54
  "libphonenumber-js": "1.13.7",
55
- "tailwindcss": "4.3.1"
55
+ "personnummer": "3.2.1",
56
+ "tailwindcss": "4.3.3"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@chromatic-com/storybook": "5.2.1",
59
60
  "@intlify/unplugin-vue-i18n": "11.2.4",
60
- "@storybook/addon-a11y": "10.4.6",
61
- "@storybook/addon-docs": "10.4.6",
62
- "@storybook/addon-vitest": "10.4.6",
63
- "@storybook/vue3": "10.4.6",
64
- "@storybook/vue3-vite": "10.4.6",
65
- "@tailwindcss/vite": "4.3.1",
61
+ "@storybook/addon-a11y": "10.5.4",
62
+ "@storybook/addon-docs": "10.5.4",
63
+ "@storybook/addon-vitest": "10.5.4",
64
+ "@storybook/vue3": "10.5.4",
65
+ "@storybook/vue3-vite": "10.5.4",
66
+ "@tailwindcss/vite": "4.3.3",
66
67
  "@types/node": "22.16.5",
67
- "@vitejs/plugin-vue": "6.0.7",
68
- "@vitest/browser": "3.2.4",
69
- "fg-devkit": "1.12.5",
70
- "storybook": "10.4.6",
71
- "tsx": "4.22.4",
68
+ "@vitejs/plugin-vue": "6.0.8",
69
+ "@vitest/browser": "3.2.7",
70
+ "fg-devkit": "1.12.7",
71
+ "storybook": "10.5.4",
72
+ "tsx": "4.23.1",
72
73
  "vite": "7.3.5",
73
74
  "vite-plugin-dts": "4.5.4",
74
- "vite-plugin-vue-devtools": "8.1.3",
75
- "vitest": "4.1.9",
76
- "vue": "3.5.38",
75
+ "vite-plugin-vue-devtools": "8.2.1",
76
+ "vitest": "4.1.10",
77
+ "vue": "3.5.40",
77
78
  "vue-i18n": "11.4.6"
78
79
  },
79
80
  "knip": {
@@ -477,7 +477,7 @@ const deleteCookieEntry = () => {
477
477
  :type="action.input.type ?? 'text'"
478
478
  :modelValue="getActionInputValue(index, action.input.defaultValue)"
479
479
  :placeholder="action.input.placeholder"
480
- @update:modelValue="setActionInputValue(index, $event)"
480
+ @update:modelValue="setActionInputValue(index, $event ?? '')"
481
481
  />
482
482
  </div>
483
483
  </div>
@@ -10,10 +10,7 @@ defineOptions({
10
10
 
11
11
  // Support v-model as boolean (single checkbox) or array (checkbox group)
12
12
  // Also support :checked prop for backward compatibility
13
- const modelValue = defineModel<boolean | Array<string | number | boolean | null>>({
14
- default: undefined,
15
- required: false,
16
- })
13
+ const modelValue = defineModel<boolean | Array<string | number | boolean | null>>()
17
14
 
18
15
  const props = withDefaults(defineProps<FdsCheckboxProps>(), {
19
16
  label: undefined,
@@ -133,13 +130,13 @@ const internalChecked = computed({
133
130
  newArray.splice(index, 1)
134
131
  }
135
132
  }
136
- modelValue.value = newArray
133
+ modelValue.value = newArray as boolean | Array<string | number | boolean | null>
137
134
  emit('update:checked', checked)
138
135
  emit('change', checked)
139
136
  emit('input', checked)
140
137
  } else {
141
138
  // Boolean mode
142
- modelValue.value = checked
139
+ modelValue.value = checked as boolean | Array<string | number | boolean | null>
143
140
  emit('update:checked', checked)
144
141
  emit('change', checked)
145
142
  emit('input', checked)
@@ -14,7 +14,7 @@ defineOptions({
14
14
  inheritAttrs: false,
15
15
  })
16
16
 
17
- const modelValue = defineModel<string>({ default: undefined, required: false })
17
+ const modelValue = defineModel<string>({ default: '' })
18
18
 
19
19
  const props = withDefaults(defineProps<FdsInputProps>(), {
20
20
  value: undefined,
@@ -182,7 +182,7 @@ const internalValue = computed({
182
182
  get: () => (modelValue.value !== undefined ? modelValue.value : (props.value ?? '')),
183
183
  set: (newValue: string) => {
184
184
  if (modelValue.value !== undefined) {
185
- modelValue.value = newValue
185
+ modelValue.value = newValue as string
186
186
  }
187
187
  emit('update:value', newValue)
188
188
  },
@@ -252,7 +252,7 @@ const createMask = () => {
252
252
 
253
253
  maskInstance.on('accept', () => {
254
254
  if (maskInstance) {
255
- internalValue.value = maskInstance.value
255
+ internalValue.value = maskInstance.value as string
256
256
  }
257
257
  })
258
258
  }
@@ -7,7 +7,7 @@ defineOptions({
7
7
  inheritAttrs: false,
8
8
  })
9
9
 
10
- const modelValue = defineModel<string | number | boolean | null>({ default: undefined, required: false })
10
+ const modelValue = defineModel<string | number | boolean | null>()
11
11
 
12
12
  const props = withDefaults(defineProps<FdsRadioProps>(), {
13
13
  label: undefined,
@@ -9,7 +9,7 @@ defineOptions({
9
9
  })
10
10
 
11
11
  // Support both v-model (modelValue) and :value prop for backward compatibility
12
- const modelValue = defineModel<string>({ default: undefined, required: false })
12
+ const modelValue = defineModel<string>()
13
13
 
14
14
  const props = withDefaults(defineProps<FdsSelectProps>(), {
15
15
  value: undefined,
@@ -88,7 +88,7 @@ const internalValue = computed({
88
88
  set: (newValue: string) => {
89
89
  // Update modelValue if it's being used
90
90
  if (modelValue.value !== undefined) {
91
- modelValue.value = newValue
91
+ modelValue.value = newValue as string
92
92
  }
93
93
  // Always emit update:value for backward compatibility
94
94
  emit('update:value', newValue)
@@ -0,0 +1,131 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { ref } from 'vue'
3
+ import FdsSsn from './FdsSsn.vue'
4
+
5
+ const meta: Meta<typeof FdsSsn> = {
6
+ title: 'FDS/Form/FdsSsn',
7
+ component: FdsSsn,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ label: { control: 'text' },
11
+ meta: { control: 'text' },
12
+ disabled: { control: 'boolean' },
13
+ optional: { control: 'boolean' },
14
+ valid: { control: 'select', options: [undefined, true, false, null] },
15
+ invalidMessage: { control: 'text' },
16
+ allowCoordinationNumber: { control: 'boolean' },
17
+ allowInterimNumber: { control: 'boolean' },
18
+ },
19
+ args: {
20
+ label: 'Personnummer',
21
+ meta: '',
22
+ disabled: false,
23
+ optional: false,
24
+ valid: undefined,
25
+ invalidMessage: 'Ange ett giltigt personnummer',
26
+ allowCoordinationNumber: true,
27
+ allowInterimNumber: true,
28
+ },
29
+ }
30
+
31
+ export default meta
32
+ type Story = StoryObj<typeof meta>
33
+
34
+ export const Default: Story = {
35
+ render: (args) => ({
36
+ components: { FdsSsn },
37
+ setup() {
38
+ const ssn = ref('')
39
+ const ssnValid = ref<boolean | null>(null)
40
+ return { args, ssn, ssnValid }
41
+ },
42
+ template: `
43
+ <div>
44
+ <FdsSsn
45
+ v-bind="args"
46
+ v-model="ssn"
47
+ @valid="ssnValid = $event"
48
+ />
49
+ <p class="mt-4 text-sm">v-model: {{ ssn || '—' }}</p>
50
+ <p class="text-sm">valid: {{ ssnValid }}</p>
51
+ </div>
52
+ `,
53
+ }),
54
+ }
55
+
56
+ export const InvalidValue: Story = {
57
+ args: {
58
+ valid: false,
59
+ },
60
+ render: (args) => ({
61
+ components: { FdsSsn },
62
+ setup() {
63
+ const ssn = ref('198501011234')
64
+ return { args, ssn }
65
+ },
66
+ template: `
67
+ <FdsSsn v-bind="args" v-model="ssn" />
68
+ `,
69
+ }),
70
+ }
71
+
72
+ export const ValidPersonnummer: Story = {
73
+ render: (args) => ({
74
+ components: { FdsSsn },
75
+ setup() {
76
+ const ssn = ref('198507099805')
77
+ return { args, ssn }
78
+ },
79
+ template: `
80
+ <FdsSsn v-bind="args" v-model="ssn" />
81
+ `,
82
+ }),
83
+ }
84
+
85
+ export const CoordinationNumber: Story = {
86
+ args: {
87
+ allowCoordinationNumber: true,
88
+ },
89
+ render: (args) => ({
90
+ components: { FdsSsn },
91
+ setup() {
92
+ const ssn = ref('198506698805')
93
+ const ssnValid = ref<boolean | null>(null)
94
+ return { args, ssn, ssnValid }
95
+ },
96
+ template: `
97
+ <div>
98
+ <FdsSsn
99
+ v-bind="args"
100
+ v-model="ssn"
101
+ @valid="ssnValid = $event"
102
+ />
103
+ <p class="mt-4 text-sm">valid: {{ ssnValid }}</p>
104
+ </div>
105
+ `,
106
+ }),
107
+ }
108
+
109
+ export const InterimNumber: Story = {
110
+ args: {
111
+ allowInterimNumber: true,
112
+ },
113
+ render: (args) => ({
114
+ components: { FdsSsn },
115
+ setup() {
116
+ const ssn = ref('19850709T125')
117
+ const ssnValid = ref<boolean | null>(null)
118
+ return { args, ssn, ssnValid }
119
+ },
120
+ template: `
121
+ <div>
122
+ <FdsSsn
123
+ v-bind="args"
124
+ v-model="ssn"
125
+ @valid="ssnValid = $event"
126
+ />
127
+ <p class="mt-4 text-sm">valid: {{ ssnValid }}</p>
128
+ </div>
129
+ `,
130
+ }),
131
+ }
@@ -0,0 +1,133 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref, useAttrs, watch } from 'vue'
3
+ import { useFdsI18n } from '../../../plugin/useFdsI18n'
4
+ import FdsInput from '../FdsInput/FdsInput.vue'
5
+ import { getSsnMask, getSsnMaskOptions } from './ssnMask'
6
+ import type { FdsSsnEmits, FdsSsnProps } from './types'
7
+ import { getSsnValidationState, stripSsnSeparators, type SsnValidationOptions } from '../../../helpers/validateSsn'
8
+
9
+ defineOptions({
10
+ inheritAttrs: false,
11
+ })
12
+
13
+ const modelValue = defineModel<string>({ default: '' })
14
+ const attrs = useAttrs()
15
+
16
+ const props = withDefaults(defineProps<FdsSsnProps>(), {
17
+ label: undefined,
18
+ meta: undefined,
19
+ optional: false,
20
+ valid: undefined,
21
+ invalidMessage: undefined,
22
+ id: undefined,
23
+ autocomplete: 'off',
24
+ required: false,
25
+ placeholder: undefined,
26
+ name: undefined,
27
+ autofocus: false,
28
+ readonly: false,
29
+ allowCoordinationNumber: true,
30
+ allowInterimNumber: true,
31
+ disabled: false,
32
+ dataTestid: undefined,
33
+ inputClass: undefined,
34
+ })
35
+
36
+ const emit = defineEmits<FdsSsnEmits>()
37
+
38
+ const { t } = useFdsI18n()
39
+
40
+ const inputAttrs = computed(() => {
41
+ const { class: _class, style, ...rest } = attrs
42
+ return {
43
+ ...rest,
44
+ ...(style == null ? {} : { style: style as string | Record<string, unknown> }),
45
+ }
46
+ })
47
+
48
+ const forwardedInputProps = computed(() => ({
49
+ id: props.id,
50
+ autocomplete: props.autocomplete,
51
+ required: props.required,
52
+ placeholder: props.placeholder,
53
+ name: props.name,
54
+ autofocus: props.autofocus,
55
+ readonly: props.readonly,
56
+ inputmode: props.allowInterimNumber ? 'text' : 'numeric',
57
+ ...inputAttrs.value,
58
+ }))
59
+
60
+ const validationOptions = computed(
61
+ (): SsnValidationOptions => ({
62
+ allowCoordinationNumber: props.allowCoordinationNumber,
63
+ allowInterimNumber: props.allowInterimNumber,
64
+ }),
65
+ )
66
+
67
+ const ssnMask = computed(() => getSsnMask(props.allowInterimNumber))
68
+ const ssnMaskOptions = computed(() => getSsnMaskOptions(props.allowInterimNumber))
69
+
70
+ /** Set after blur; validation does not run on input. */
71
+ const committedValid = ref<boolean | null>(null)
72
+
73
+ const resolvedLabel = computed(() => (props.label === undefined ? t('FdsSsn.label') : props.label))
74
+
75
+ const resolvedInvalidMessage = computed(() =>
76
+ props.invalidMessage === undefined ? t('FdsSsn.invalidSsn') : props.invalidMessage,
77
+ )
78
+
79
+ const displayValid = computed((): boolean | null | undefined => {
80
+ if (props.valid === false) return false
81
+ if (props.valid === true) return true
82
+ if (committedValid.value === false) return false
83
+ if (committedValid.value === true) return true
84
+ return props.valid
85
+ })
86
+
87
+ function normalizeValue() {
88
+ const normalized = stripSsnSeparators(modelValue.value ?? '')
89
+ if (normalized !== (modelValue.value ?? '')) {
90
+ modelValue.value = normalized
91
+ }
92
+ }
93
+
94
+ function runValidation() {
95
+ const validationState = getSsnValidationState(modelValue.value ?? '', validationOptions.value)
96
+ committedValid.value = validationState
97
+ emit('valid', validationState)
98
+ }
99
+
100
+ watch([() => props.allowCoordinationNumber, () => props.allowInterimNumber], () => {
101
+ if (committedValid.value === null) {
102
+ return
103
+ }
104
+ runValidation()
105
+ })
106
+
107
+ function handleBlur(ev: FocusEvent) {
108
+ normalizeValue()
109
+ runValidation()
110
+ emit('blur', ev)
111
+ }
112
+ </script>
113
+
114
+ <template>
115
+ <div class="w-full">
116
+ <FdsInput
117
+ v-model="modelValue"
118
+ :mask="ssnMask"
119
+ :mask-options="ssnMaskOptions"
120
+ :label="resolvedLabel"
121
+ :meta="meta"
122
+ :valid="displayValid"
123
+ :disabled="disabled"
124
+ :optional="optional"
125
+ :invalid-message="resolvedInvalidMessage"
126
+ :ariaLabel="resolvedLabel"
127
+ :data-testid="dataTestid"
128
+ :class="inputClass"
129
+ v-bind="forwardedInputProps"
130
+ @blur="handleBlur"
131
+ />
132
+ </div>
133
+ </template>
@@ -0,0 +1,20 @@
1
+ /** IMask pattern for ÅÅÅÅMMDDKKKK (12 digits, no separator). */
2
+ export const SSN_MASK = '000000000000'
3
+
4
+ /** Alphanumeric mask when interim numbers (letters in num part) are allowed. */
5
+ export const SSN_INTERIM_MASK = 'XXXXXXXXXXXX'
6
+
7
+ export const SSN_INTERIM_MASK_OPTIONS = {
8
+ lazy: true,
9
+ definitions: {
10
+ X: /[0-9A-Za-z]/,
11
+ },
12
+ } as const
13
+
14
+ export function getSsnMask(allowInterimNumber: boolean): string {
15
+ return allowInterimNumber ? SSN_INTERIM_MASK : SSN_MASK
16
+ }
17
+
18
+ export function getSsnMaskOptions(allowInterimNumber: boolean) {
19
+ return allowInterimNumber ? SSN_INTERIM_MASK_OPTIONS : { lazy: true }
20
+ }
@@ -0,0 +1,36 @@
1
+ import type { FdsInputProps } from '../FdsInput/types'
2
+
3
+ type FdsSsnInputPassthroughProps = Pick<
4
+ FdsInputProps,
5
+ | 'id'
6
+ | 'autocomplete'
7
+ | 'required'
8
+ | 'placeholder'
9
+ | 'name'
10
+ | 'autofocus'
11
+ | 'readonly'
12
+ | 'inputClass'
13
+ >
14
+
15
+ export interface FdsSsnProps extends FdsSsnInputPassthroughProps {
16
+ label?: string
17
+ meta?: string
18
+ optional?: boolean
19
+ valid?: boolean | null
20
+ invalidMessage?: string
21
+ /** Personnummer as shown in the input (12 characters, no separator). */
22
+ modelValue?: string
23
+ allowCoordinationNumber?: boolean
24
+ allowInterimNumber?: boolean
25
+ disabled?: boolean
26
+ dataTestid?: string
27
+ onValid?: ((value: boolean | null) => void) | Array<(value: boolean | null) => void>
28
+ onBlur?: ((event: FocusEvent) => void) | Array<(event: FocusEvent) => void>
29
+ 'onUpdate:modelValue'?: ((value: string) => void) | Array<(value: string) => void>
30
+ }
31
+
32
+ export interface FdsSsnEmits {
33
+ 'update:modelValue': [value: string]
34
+ valid: [value: boolean | null]
35
+ blur: [ev: FocusEvent]
36
+ }
@@ -12,7 +12,7 @@ defineOptions({
12
12
  })
13
13
 
14
14
  // Support both v-model (modelValue) and :value prop for backward compatibility
15
- const modelValue = defineModel<string>({ default: undefined, required: false })
15
+ const modelValue = defineModel<string>()
16
16
 
17
17
  const props = withDefaults(defineProps<FdsTextareaProps>(), {
18
18
  value: undefined,
@@ -67,7 +67,7 @@ const internalValue = computed({
67
67
  get: () => (modelValue.value !== undefined ? modelValue.value : (props.value ?? '')),
68
68
  set: (newValue: string) => {
69
69
  if (modelValue.value !== undefined) {
70
- modelValue.value = newValue
70
+ modelValue.value = newValue as string
71
71
  }
72
72
  emit('update:value', newValue)
73
73
  },
@@ -0,0 +1,63 @@
1
+ import Personnummer from 'personnummer'
2
+ import { normalizePidSearchValue } from '../composables/useIsPid'
3
+
4
+ export interface SsnValidationOptions {
5
+ allowCoordinationNumber?: boolean
6
+ allowInterimNumber?: boolean
7
+ }
8
+
9
+ export interface SsnValidationResult {
10
+ isValid: boolean
11
+ /** Normalized value without separators; `null` when empty or invalid. */
12
+ digits: string | null
13
+ }
14
+
15
+ const INVALID_RESULT: SsnValidationResult = {
16
+ isValid: false,
17
+ digits: null,
18
+ }
19
+
20
+ const DEFAULT_SSN_VALIDATION_OPTIONS = {
21
+ allowCoordinationNumber: true,
22
+ allowInterimNumber: true,
23
+ } as const
24
+
25
+ function resolveSsnValidationOptions(options?: SsnValidationOptions) {
26
+ return {
27
+ ...DEFAULT_SSN_VALIDATION_OPTIONS,
28
+ ...options,
29
+ }
30
+ }
31
+
32
+ /** Strip separators; keeps alphanumeric characters for interim numbers. */
33
+ export function stripSsnSeparators(value: string): string {
34
+ return normalizePidSearchValue(value)
35
+ }
36
+
37
+ /** Validate a Swedish personal identity number. */
38
+ export function validateSsnNumber(value: string, options?: SsnValidationOptions): SsnValidationResult {
39
+ const trimmed = value.trim()
40
+ if (!trimmed) {
41
+ return INVALID_RESULT
42
+ }
43
+
44
+ const resolvedOptions = resolveSsnValidationOptions(options)
45
+ const isValid = Personnummer.valid(trimmed, resolvedOptions)
46
+
47
+ return {
48
+ isValid,
49
+ digits: isValid ? stripSsnSeparators(trimmed) : null,
50
+ }
51
+ }
52
+
53
+ export function validateSsn(value: string, options?: SsnValidationOptions): boolean {
54
+ return validateSsnNumber(value, options).isValid
55
+ }
56
+
57
+ /** `null` when empty, otherwise whether the value is a valid personnummer. */
58
+ export function getSsnValidationState(value: string, options?: SsnValidationOptions): boolean | null {
59
+ if (!value.trim()) {
60
+ return null
61
+ }
62
+ return validateSsn(value, options)
63
+ }
package/src/index.ts CHANGED
@@ -31,6 +31,7 @@ import FdsWizard from './components/FdsWizard/FdsWizard.vue'
31
31
  import FdsCheckbox from './components/Form/FdsCheckbox/FdsCheckbox.vue'
32
32
  import FdsInput from './components/Form/FdsInput/FdsInput.vue'
33
33
  import FdsPhonenumber from './components/Form/FdsPhonenumber/FdsPhonenumber.vue'
34
+ import FdsSsn from './components/Form/FdsSsn/FdsSsn.vue'
34
35
  import FdsRadio from './components/Form/FdsRadio/FdsRadio.vue'
35
36
  import FdsSelect from './components/Form/FdsSelect/FdsSelect.vue'
36
37
  import FdsTextarea from './components/Form/FdsTextarea/FdsTextarea.vue'
@@ -113,6 +114,7 @@ export {
113
114
  FdsModal,
114
115
  FdsPagination,
115
116
  FdsPhonenumber,
117
+ FdsSsn,
116
118
  FdsPopover,
117
119
  FdsRadio,
118
120
  FdsSearchSelect,
@@ -196,6 +198,7 @@ const FdsVueCorePlugin: Plugin = {
196
198
  app.component('FdsCheckbox', FdsCheckbox)
197
199
  app.component('FdsTextarea', FdsTextarea)
198
200
  app.component('FdsPhonenumber', FdsPhonenumber)
201
+ app.component('FdsSsn', FdsSsn)
199
202
  app.component('FdsSelect', FdsSelect)
200
203
  app.component('FdsTable', FdsTable)
201
204
  app.component('FdsTableHead', FdsTableHead)
@@ -283,6 +286,19 @@ export {
283
286
 
284
287
  export type { FdsPhonenumberEmits, FdsPhonenumberProps } from './components/Form/FdsPhonenumber/types'
285
288
 
289
+ export {
290
+ getSsnValidationState,
291
+ stripSsnSeparators,
292
+ validateSsn,
293
+ validateSsnNumber,
294
+ type SsnValidationOptions,
295
+ type SsnValidationResult,
296
+ } from './helpers/validateSsn'
297
+
298
+ export { getSsnMask, getSsnMaskOptions, SSN_INTERIM_MASK, SSN_MASK } from './components/Form/FdsSsn/ssnMask'
299
+
300
+ export type { FdsSsnEmits, FdsSsnProps } from './components/Form/FdsSsn/types'
301
+
286
302
  // Table component types
287
303
  export type { FdsTableProps } from './components/Table/FdsTable/types'
288
304
 
package/src/lang/en.json CHANGED
@@ -50,6 +50,8 @@
50
50
  "FdsPhonenumber.invalidPhone": "Enter a valid phone number",
51
51
  "FdsPhonenumber.noCountryResults": "No country matches your search",
52
52
  "FdsPhonenumber.phoneNumber": "Phone number",
53
+ "FdsSsn.invalidSsn": "Enter a valid personal identity number",
54
+ "FdsSsn.label": "Personal identity number",
53
55
  "FdsSearchSelectPro.loadingMore": "Loading more...",
54
56
  "FdsSearchSelectPro.showMore": "Show more",
55
57
  "FdsSearchSelectPro.unspecified": "Unspecified",
package/src/lang/sv.json CHANGED
@@ -50,6 +50,8 @@
50
50
  "FdsPhonenumber.invalidPhone": "Ange ett giltigt telefonnummer",
51
51
  "FdsPhonenumber.noCountryResults": "Inget land matchar sökningen",
52
52
  "FdsPhonenumber.phoneNumber": "Telefonnummer",
53
+ "FdsSsn.invalidSsn": "Ange ett giltigt personnummer",
54
+ "FdsSsn.label": "Personnummer",
53
55
  "FdsSearchSelectPro.loadingMore": "Hämtar fler...",
54
56
  "FdsSearchSelectPro.showMore": "Visa fler",
55
57
  "FdsSearchSelectPro.unspecified": "Ospecificerat",