fds-vue-core 7.2.6 → 8.0.0
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/dist/components/Form/FdsPhonenumber/FdsPhonenumber.stories.d.ts +0 -1
- package/dist/components/Form/FdsPhonenumber/FdsPhonenumber.vue.d.ts +0 -1
- package/dist/components/Form/FdsPhonenumber/types.d.ts +0 -7
- package/dist/components/Form/FdsPhonenumber/validatePhone.d.ts +2 -7
- package/dist/fds-vue-core.cjs.js +35 -42
- package/dist/fds-vue-core.cjs.js.map +1 -1
- package/dist/fds-vue-core.es.js +35 -42
- package/dist/fds-vue-core.es.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/components/Blocks/FdsBlockLink/FdsBlockLink.vue +17 -1
- package/src/components/Form/FdsPhonenumber/FdsPhonenumber.stories.ts +0 -32
- package/src/components/Form/FdsPhonenumber/FdsPhonenumber.vue +3 -10
- package/src/components/Form/FdsPhonenumber/countries.ts +1 -1
- package/src/components/Form/FdsPhonenumber/types.ts +0 -8
- package/src/components/Form/FdsPhonenumber/validatePhone.ts +9 -34
- package/src/index.ts +1 -5
- package/src/lang/en.json +1 -1
- package/src/lang/sv.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fds-vue-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "FDS Vue Core Component Library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/fds-vue-core.cjs.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"axios": "1.16.1",
|
|
52
|
-
"date-fns": "4.
|
|
52
|
+
"date-fns": "4.4.0",
|
|
53
53
|
"imask": "7.6.1",
|
|
54
54
|
"libphonenumber-js": "1.12.36",
|
|
55
55
|
"tailwindcss": "4.3.0",
|
|
@@ -58,23 +58,23 @@
|
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@chromatic-com/storybook": "5.2.1",
|
|
60
60
|
"@intlify/unplugin-vue-i18n": "11.2.3",
|
|
61
|
-
"@storybook/addon-a11y": "10.4.
|
|
62
|
-
"@storybook/addon-docs": "10.4.
|
|
63
|
-
"@storybook/addon-vitest": "10.4.
|
|
64
|
-
"@storybook/vue3": "10.4.
|
|
65
|
-
"@storybook/vue3-vite": "10.4.
|
|
61
|
+
"@storybook/addon-a11y": "10.4.1",
|
|
62
|
+
"@storybook/addon-docs": "10.4.1",
|
|
63
|
+
"@storybook/addon-vitest": "10.4.1",
|
|
64
|
+
"@storybook/vue3": "10.4.1",
|
|
65
|
+
"@storybook/vue3-vite": "10.4.1",
|
|
66
66
|
"@tailwindcss/vite": "4.3.0",
|
|
67
67
|
"@types/node": "22.16.5",
|
|
68
68
|
"@vitejs/plugin-vue": "6.0.7",
|
|
69
69
|
"@vitest/browser": "3.2.4",
|
|
70
|
-
"fg-devkit": "1.8.
|
|
71
|
-
"storybook": "10.4.
|
|
72
|
-
"tsx": "4.22.
|
|
70
|
+
"fg-devkit": "1.8.6",
|
|
71
|
+
"storybook": "10.4.1",
|
|
72
|
+
"tsx": "4.22.4",
|
|
73
73
|
"vite": "7.3.2",
|
|
74
74
|
"vite-plugin-dts": "4.5.4",
|
|
75
75
|
"vite-plugin-vue-devtools": "8.1.2",
|
|
76
|
-
"vitest": "
|
|
77
|
-
"vue": "3.5.
|
|
76
|
+
"vitest": "4.1.8",
|
|
77
|
+
"vue": "3.5.35"
|
|
78
78
|
},
|
|
79
79
|
"knip": {
|
|
80
80
|
"ignoreBinaries": [
|
|
@@ -32,6 +32,7 @@ const autoId = `fds-block-link-${Math.random().toString(36).slice(2, 9)}`
|
|
|
32
32
|
const blockLinkId = computed(() => id.value ?? autoId)
|
|
33
33
|
|
|
34
34
|
const isInteractive = computed(() => !props.disabled && props.interactive)
|
|
35
|
+
const keyboardTabIndex = computed(() => (isInteractive.value ? 0 : -1))
|
|
35
36
|
|
|
36
37
|
const innerClasses = computed(() => [
|
|
37
38
|
props.disabled ? 'cursor-not-allowed shadow-none hover:border-transparent active:bg-transparent' : 'cursor-pointer',
|
|
@@ -65,7 +66,21 @@ function handleClick(event: Event) {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
function handleKeydown(event: KeyboardEvent) {
|
|
68
|
-
if (
|
|
69
|
+
if (!isInteractive.value) return
|
|
70
|
+
|
|
71
|
+
const isEnter = event.key === 'Enter' || event.code === 'Enter' || event.code === 'NumpadEnter'
|
|
72
|
+
const isSpace = event.key === ' ' || event.key === 'Spacebar'
|
|
73
|
+
|
|
74
|
+
// Links (incl. router-link): Enter activates natively; only Space needs a synthetic click.
|
|
75
|
+
if (componentType.value === 'a' || componentType.value === 'router-link') {
|
|
76
|
+
if (isSpace) {
|
|
77
|
+
event.preventDefault()
|
|
78
|
+
handleClick(event)
|
|
79
|
+
}
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isEnter || isSpace) {
|
|
69
84
|
event.preventDefault()
|
|
70
85
|
handleClick(event)
|
|
71
86
|
}
|
|
@@ -117,6 +132,7 @@ defineSlots<{
|
|
|
117
132
|
:download="componentType === 'a' ? download : undefined"
|
|
118
133
|
:disabled="componentType === 'button' ? props.disabled : undefined"
|
|
119
134
|
:aria-disabled="props.disabled"
|
|
135
|
+
:tabindex="keyboardTabIndex"
|
|
120
136
|
:data-testid="dataTestid"
|
|
121
137
|
@click="handleClick"
|
|
122
138
|
@keydown="handleKeydown"
|
|
@@ -15,7 +15,6 @@ const meta: Meta<typeof FdsPhonenumber> = {
|
|
|
15
15
|
invalidMessage: { control: 'text' },
|
|
16
16
|
defaultCountry: { control: 'text' },
|
|
17
17
|
locale: { control: 'text' },
|
|
18
|
-
numberType: { control: 'select', options: ['mobile', 'any'] },
|
|
19
18
|
},
|
|
20
19
|
args: {
|
|
21
20
|
label: 'Telefonnummer',
|
|
@@ -80,37 +79,6 @@ export const InvalidNumber: Story = {
|
|
|
80
79
|
}),
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
export const AllowAnyNumberType: Story = {
|
|
84
|
-
args: {
|
|
85
|
-
numberType: 'any',
|
|
86
|
-
meta: 'Mobil och fast telefon accepteras (t.ex. Stockholm 08 + 6–7 siffror)',
|
|
87
|
-
},
|
|
88
|
-
render: (args) => ({
|
|
89
|
-
components: { FdsPhonenumber },
|
|
90
|
-
setup() {
|
|
91
|
-
// Stockholm: 08 + 7 siffror
|
|
92
|
-
const number = ref('081234567')
|
|
93
|
-
const country = ref('SE')
|
|
94
|
-
const phoneValid = ref<boolean | null>(null)
|
|
95
|
-
const e164 = ref('')
|
|
96
|
-
return { args, number, country, phoneValid, e164 }
|
|
97
|
-
},
|
|
98
|
-
template: `
|
|
99
|
-
<div>
|
|
100
|
-
<FdsPhonenumber
|
|
101
|
-
v-bind="args"
|
|
102
|
-
v-model="number"
|
|
103
|
-
v-model:country="country"
|
|
104
|
-
@valid="phoneValid = $event"
|
|
105
|
-
@update:e164="e164 = $event"
|
|
106
|
-
/>
|
|
107
|
-
<p class="mt-4 text-sm">valid: {{ phoneValid }}</p>
|
|
108
|
-
<p class="text-sm">E.164: {{ e164 || '—' }}</p>
|
|
109
|
-
</div>
|
|
110
|
-
`,
|
|
111
|
-
}),
|
|
112
|
-
}
|
|
113
|
-
|
|
114
82
|
export const EnglishLocale: Story = {
|
|
115
83
|
args: {
|
|
116
84
|
locale: 'en',
|
|
@@ -34,7 +34,6 @@ const props = withDefaults(defineProps<FdsPhonenumberProps>(), {
|
|
|
34
34
|
defaultCountry: 'SE',
|
|
35
35
|
countries: undefined,
|
|
36
36
|
locale: undefined,
|
|
37
|
-
numberType: 'mobile',
|
|
38
37
|
disabled: false,
|
|
39
38
|
dataTestid: undefined,
|
|
40
39
|
selectClass: undefined,
|
|
@@ -78,8 +77,6 @@ const countryItems = computed(() => {
|
|
|
78
77
|
)
|
|
79
78
|
})
|
|
80
79
|
|
|
81
|
-
const phoneValidationOptions = computed(() => ({ numberType: props.numberType }))
|
|
82
|
-
|
|
83
80
|
/** Set after the phone number field has blurred; validation does not run on input. */
|
|
84
81
|
const committedValid = ref<boolean | null>(null)
|
|
85
82
|
|
|
@@ -104,19 +101,15 @@ const showInvalidMessage = computed(
|
|
|
104
101
|
const noCountryResults = ref(false)
|
|
105
102
|
|
|
106
103
|
function runValidation() {
|
|
107
|
-
const validationState = getPhoneValidationState(
|
|
108
|
-
nationalNumber.value ?? '',
|
|
109
|
-
country.value ?? 'SE',
|
|
110
|
-
phoneValidationOptions.value,
|
|
111
|
-
)
|
|
104
|
+
const validationState = getPhoneValidationState(nationalNumber.value ?? '', country.value ?? 'SE')
|
|
112
105
|
committedValid.value = validationState
|
|
113
106
|
emit('valid', validationState)
|
|
114
107
|
|
|
115
|
-
const result = validatePhoneNumber(nationalNumber.value ?? '', country.value ?? 'SE'
|
|
108
|
+
const result = validatePhoneNumber(nationalNumber.value ?? '', country.value ?? 'SE')
|
|
116
109
|
emit('update:e164', result.isValid && result.phoneNumber ? result.phoneNumber : '')
|
|
117
110
|
}
|
|
118
111
|
|
|
119
|
-
watch(
|
|
112
|
+
watch(country, () => {
|
|
120
113
|
if (committedValid.value === null) {
|
|
121
114
|
return
|
|
122
115
|
}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import type { FdsInputProps } from '../FdsInput/types'
|
|
2
2
|
import type { CountryPhoneOption } from './countries'
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* `mobile` – mobile numbers only (Google libphonenumber metadata).
|
|
6
|
-
* `any` – mobile and fixed-line numbers.
|
|
7
|
-
*/
|
|
8
|
-
export type FdsPhonenumberNumberType = 'mobile' | 'any'
|
|
9
|
-
|
|
10
4
|
type FdsPhonenumberInputPassthroughProps = Pick<
|
|
11
5
|
FdsInputProps,
|
|
12
6
|
| 'id'
|
|
@@ -37,8 +31,6 @@ export interface FdsPhonenumberProps extends FdsPhonenumberInputPassthroughProps
|
|
|
37
31
|
countries?: CountryPhoneOption[]
|
|
38
32
|
/** BCP 47 locale for country names in the list, e.g. `sv-SE` or `en`. Falls back to FDS i18n locale. */
|
|
39
33
|
locale?: string
|
|
40
|
-
/** Which number types are accepted when validating with libphonenumber-js. */
|
|
41
|
-
numberType?: FdsPhonenumberNumberType
|
|
42
34
|
disabled?: boolean
|
|
43
35
|
dataTestid?: string
|
|
44
36
|
selectClass?: string
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import parsePhoneNumberFromString from 'libphonenumber-js/max'
|
|
3
|
-
import type { FdsPhonenumberNumberType } from './types'
|
|
4
|
-
|
|
5
|
-
interface ValidatePhoneOptions {
|
|
6
|
-
numberType?: FdsPhonenumberNumberType
|
|
7
|
-
}
|
|
1
|
+
import parsePhoneNumberFromString, { type CountryCode, type NumberType } from 'libphonenumber-js/mobile'
|
|
8
2
|
|
|
9
3
|
/** Result shape kept stable for consumers (same fields as previous `phone` package integration). */
|
|
10
4
|
export interface PhoneValidationResult {
|
|
@@ -24,16 +18,9 @@ const INVALID_RESULT: PhoneValidationResult = {
|
|
|
24
18
|
}
|
|
25
19
|
|
|
26
20
|
const MOBILE_NUMBER_TYPES = new Set<NumberType>(['MOBILE', 'FIXED_LINE_OR_MOBILE'])
|
|
27
|
-
const ANY_NUMBER_TYPES = new Set<NumberType>(['MOBILE', 'FIXED_LINE', 'FIXED_LINE_OR_MOBILE'])
|
|
28
21
|
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
return numberType === 'any'
|
|
32
|
-
}
|
|
33
|
-
if (numberType === 'mobile') {
|
|
34
|
-
return MOBILE_NUMBER_TYPES.has(type)
|
|
35
|
-
}
|
|
36
|
-
return ANY_NUMBER_TYPES.has(type)
|
|
22
|
+
function isMobileNumberType(type: NumberType | undefined): boolean {
|
|
23
|
+
return type !== undefined && MOBILE_NUMBER_TYPES.has(type)
|
|
37
24
|
}
|
|
38
25
|
|
|
39
26
|
function buildCandidates(value: string): string[] {
|
|
@@ -61,11 +48,7 @@ function toValidationResult(parsed: NonNullable<ReturnType<typeof parsePhoneNumb
|
|
|
61
48
|
}
|
|
62
49
|
}
|
|
63
50
|
|
|
64
|
-
function runPhoneValidation(
|
|
65
|
-
value: string,
|
|
66
|
-
countryIso2: string,
|
|
67
|
-
numberType: FdsPhonenumberNumberType,
|
|
68
|
-
): PhoneValidationResult {
|
|
51
|
+
function runPhoneValidation(value: string, countryIso2: string): PhoneValidationResult {
|
|
69
52
|
const country = countryIso2 as CountryCode
|
|
70
53
|
|
|
71
54
|
for (const candidate of buildCandidates(value)) {
|
|
@@ -73,7 +56,7 @@ function runPhoneValidation(
|
|
|
73
56
|
if (!parsed?.isValid()) {
|
|
74
57
|
continue
|
|
75
58
|
}
|
|
76
|
-
if (
|
|
59
|
+
if (isMobileNumberType(parsed.getType())) {
|
|
77
60
|
return toValidationResult(parsed)
|
|
78
61
|
}
|
|
79
62
|
}
|
|
@@ -81,27 +64,19 @@ function runPhoneValidation(
|
|
|
81
64
|
return INVALID_RESULT
|
|
82
65
|
}
|
|
83
66
|
|
|
84
|
-
export function validatePhoneNumber(
|
|
85
|
-
nationalNumber: string,
|
|
86
|
-
countryIso2: string,
|
|
87
|
-
options: ValidatePhoneOptions = {},
|
|
88
|
-
): PhoneValidationResult {
|
|
67
|
+
export function validatePhoneNumber(nationalNumber: string, countryIso2: string): PhoneValidationResult {
|
|
89
68
|
const trimmed = nationalNumber.trim()
|
|
90
69
|
if (!trimmed) {
|
|
91
70
|
return INVALID_RESULT
|
|
92
71
|
}
|
|
93
72
|
|
|
94
|
-
return runPhoneValidation(trimmed, countryIso2
|
|
73
|
+
return runPhoneValidation(trimmed, countryIso2)
|
|
95
74
|
}
|
|
96
75
|
|
|
97
76
|
/** `null` when empty, otherwise whether the number is valid for the selected country. */
|
|
98
|
-
export function getPhoneValidationState(
|
|
99
|
-
nationalNumber: string,
|
|
100
|
-
countryIso2: string,
|
|
101
|
-
options: ValidatePhoneOptions = {},
|
|
102
|
-
): boolean | null {
|
|
77
|
+
export function getPhoneValidationState(nationalNumber: string, countryIso2: string): boolean | null {
|
|
103
78
|
if (!nationalNumber.trim()) {
|
|
104
79
|
return null
|
|
105
80
|
}
|
|
106
|
-
return validatePhoneNumber(nationalNumber, countryIso2
|
|
81
|
+
return validatePhoneNumber(nationalNumber, countryIso2).isValid
|
|
107
82
|
}
|
package/src/index.ts
CHANGED
|
@@ -263,11 +263,7 @@ export {
|
|
|
263
263
|
type PhoneValidationResult,
|
|
264
264
|
} from './components/Form/FdsPhonenumber/validatePhone'
|
|
265
265
|
|
|
266
|
-
export type {
|
|
267
|
-
FdsPhonenumberEmits,
|
|
268
|
-
FdsPhonenumberNumberType,
|
|
269
|
-
FdsPhonenumberProps,
|
|
270
|
-
} from './components/Form/FdsPhonenumber/types'
|
|
266
|
+
export type { FdsPhonenumberEmits, FdsPhonenumberProps } from './components/Form/FdsPhonenumber/types'
|
|
271
267
|
|
|
272
268
|
// Table component types
|
|
273
269
|
export type { FdsTableProps } from './components/Table/FdsTable/types'
|
package/src/lang/en.json
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"FdsDevModeStorage.tabs.localStorage": "localStorage",
|
|
30
30
|
"FdsDevModeStorage.tabs.sessionStorage": "sessionStorage",
|
|
31
31
|
"FdsDevModeStorage.valueLabel": "Value",
|
|
32
|
-
"FdsInput.clearInput": "
|
|
32
|
+
"FdsInput.clearInput": "Delete text",
|
|
33
33
|
"FdsInput.hidePassword": "Hide",
|
|
34
34
|
"FdsInput.openDatePicker": "Open date picker",
|
|
35
35
|
"FdsInput.showPassword": "Show",
|
package/src/lang/sv.json
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"FdsDevModeStorage.tabs.localStorage": "localStorage",
|
|
30
30
|
"FdsDevModeStorage.tabs.sessionStorage": "sessionStorage",
|
|
31
31
|
"FdsDevModeStorage.valueLabel": "Värde",
|
|
32
|
-
"FdsInput.clearInput": "Rensa
|
|
32
|
+
"FdsInput.clearInput": "Rensa text",
|
|
33
33
|
"FdsInput.hidePassword": "Dölj",
|
|
34
34
|
"FdsInput.openDatePicker": "Öppna datumväljare",
|
|
35
35
|
"FdsInput.showPassword": "Visa",
|