Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/format/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ formatOrganizationNumber('0000000000') // => '000000-0000'

## Methods

* formatPhoneNumber
* formatOrganizationNumber
* formatObosMembershipNumber
34 changes: 34 additions & 0 deletions packages/format/src/format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ import { describe, expect, test } from 'vitest';
import {
formatObosMembershipNumber as formatObosMembershipNumberNo,
formatOrganizationNumber as formatOrganizationNumberNo,
formatPhoneNumber as formatPhoneNumberNo,
} from './no';
import {
formatObosMembershipNumber as formatObosMembershipNumberSe,
formatOrganizationNumber as formatOrganizationNumberSe,
formatPhoneNumber as formatPhoneNumberSe,
} from './se';

describe('no', () => {
test.each([
['22865500', '22 86 55 00'],
['80000000', '800 00 000'],
])('formatPhoneNumber(%s) -> %s', (input, expected) => {
expect(formatPhoneNumberNo(input)).toBe(expected);
});

test.each([
['000000000', '000 000 000'],
['000 000 000', '000 000 000'],
Expand All @@ -20,6 +29,31 @@ describe('no', () => {
});

describe('se', () => {
test.each([
// mobile phone numbers
['0701234567', '070-123 45 67'],
['070 12 345 67', '070-123 45 67'],
// 2 digit area code
['0812345', '08-123 45'],
['08123456', '08-12 34 56'],
['081234567', '08-123 45 67'],
['0812345678', '08-123 456 78'],
// 3 digit area code
['03112345', '031-123 45'],
['031123456', '031-12 34 56'],
['0311234567', '031-123 45 67'],
['03112345678', '031-123 456 78'],
// 4 digit area code
['030312345', '0303-123 45'],
['0303123456', '0303-12 34 56'],
['03031234567', '0303-123 45 67'],
['030312345678', '0303-123 456 78'],
// invalid, too long a number
['0303123456789', '0303123456789'],
])('formatPhoneNumber(%s) -> %s', (input, expected) => {
expect(formatPhoneNumberSe(input)).toBe(expected);
});

test.each([
['0000000000', '000000-0000'],
['000000-0000', '000000-0000'],
Expand Down
31 changes: 31 additions & 0 deletions packages/format/src/no.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
import { replaceIfMatch } from './utils';

// Regular phone number format is: 00 00 00 00
// if the number starts with 8, it's an 800-series number, with the format: 800 00 000
// See https://sprakradet.no/godt-og-korrekt-sprak/rettskriving-og-grammatikk/tall-tid-dato/
const REGULAR_PHONE_NUMBER_FORMAT = /^(\d{2})(\d{2})(\d{2})(\d{2})$/;
const EIGHT_HUNDRED_SERIES_PHONE_NUMBER_FORMAT = /^(\d{3})(\d{2})(\d{3})$/;

/**
* Format a phone number
* @example
* ```
* formatPhoneNumber('00000000') // => '00 00 00 00'
* formatPhoneNumber('80000000') // => '800 00 000'
* ```
*/
export function formatPhoneNumber(input: string): string {
const number = replaceIfMatch(
input,
REGULAR_PHONE_NUMBER_FORMAT,
'$1 $2 $3 $4',
);

// if the number starts with 8, it's an 800-series number, so we'll format it differently
return number.startsWith('8')
? replaceIfMatch(
number,
EIGHT_HUNDRED_SERIES_PHONE_NUMBER_FORMAT,
'$1 $2 $3',
)
: number;
}

const ORG_NUMBER_FORMAT = /^(\d{3})(\d{3})(\d{3})$/;

/**
Expand Down
57 changes: 56 additions & 1 deletion packages/format/src/se.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,59 @@
import { replaceIfMatch } from './utils';
import { cleanInput, replaceIfMatch } from './utils';

const MOBILE_PHONE_NUMBER_FORMAT = /^(07[02369]{1})(\d{3})(\d{2})(\d{2})$/;
// subscriber numbers, without the area codes, can be 5, 6, 7 or 8 digits long
const SUBSCRIBER_NUMBER_FORMATS = {
5: /^(\d{3})(\d{2})$/,
6: /^(\d{2})(\d{2})(\d{2})$/,
7: /^(\d{3})(\d{2})(\d{2})$/,
8: /^(\d{3})(\d{3})(\d{2})$/,
};
// SE numbers have area codes of 2, 3 or 4 digits
const TWO_DIGIT_AREA_CODE = /^08/;
const THREE_DIGIT_AREA_CODE =
/^0(11|13|16|18|19|21|23|26|31|33|35|36|40|42|44|46|54|60|63|90)/;

/**
* Format a phone number
* @example
* ```
* formatPhoneNumber('07012345678') // => '070-123 45 678'
* formatPhoneNumber('0812345') // => '08-123 45'
* formatPhoneNumber('0311234567') // => '031-123 45 67'
* formatPhoneNumber('0303123456') // => '0303-12 34 56'
* ```
*/
export function formatPhoneNumber(input: string): string {
const normalizedInput = cleanInput(input);

if (MOBILE_PHONE_NUMBER_FORMAT.test(normalizedInput)) {
return normalizedInput.replace(MOBILE_PHONE_NUMBER_FORMAT, '$1-$2 $3 $4');
}

const areaCodeLength = TWO_DIGIT_AREA_CODE.test(normalizedInput)
? 2
: THREE_DIGIT_AREA_CODE.test(normalizedInput)
? 3
: 4;

const areaCode = normalizedInput.substring(0, areaCodeLength);
const subscriberNumber = normalizedInput.substring(areaCodeLength);

// if the subscriber number length is not in the formats, return the input as is
if (!(subscriberNumber.length in SUBSCRIBER_NUMBER_FORMATS)) {
return normalizedInput;
}

const subscriberNumberFormat =
SUBSCRIBER_NUMBER_FORMATS[
// the cast should be okay here, as we've checked the length above
subscriberNumber.length as keyof typeof SUBSCRIBER_NUMBER_FORMATS
];

const replacePattern = subscriberNumber.length === 5 ? '$1 $2' : '$1 $2 $3';

return `${areaCode}-${subscriberNumber.replace(subscriberNumberFormat, replacePattern)}`;
}

const ORG_NUMBER_FORMAT = /^(\d{6})(\d{4})$/;

Expand Down
10 changes: 7 additions & 3 deletions packages/format/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ export function replaceIfMatch(
regex: RegExp,
replacerPattern: string,
): string {
const normalizedInput = cleanInput(input);

return normalizedInput.replace(regex, replacerPattern);
}

export function cleanInput(input: string): string {
// We're extremely lenient when attemtping to format the input.
// We remove everything that isn't a letter or a number, that way we can get rid of any
// formatting that might already be present in the input, eg spaces, hyphens or dots
const cleaned = input.replace(/[^a-zA-Z0-9]/g, '');

return cleaned.replace(regex, replacerPattern);
return input.replace(/[^a-zA-Z0-9]/g, '');
}
Loading