Facebook links: display profile instead of full url (#16414)

Like we do for LinkedIn/Twitter

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Thomas des Francs
2025-12-12 18:49:58 +01:00
committed by GitHub
parent d36d0d49d1
commit 7f50486de5
10 changed files with 181 additions and 82 deletions
@@ -1,5 +1,6 @@
import { isNonEmptyString } from '@sniptt/guards';
import { LinkType, RoundedLink, SocialLink } from 'twenty-ui/navigation';
import { checkUrlType } from '~/utils/checkUrlType';
type LinkDisplayProps = {
value: { url: string; label?: string | null };
@@ -22,13 +23,13 @@ export const LinkDisplay = ({ value }: LinkDisplayProps) => {
? value.label
: url?.replace(/^http[s]?:\/\/(?:[w]+\.)?/gm, '').replace(/^[w]+\./gm, '');
const type = displayedValue.startsWith('linkedin.')
? LinkType.LinkedIn
: displayedValue.startsWith('twitter.')
? LinkType.Twitter
: LinkType.Url;
const type = checkUrlType(absoluteUrl);
if (type === LinkType.LinkedIn || type === LinkType.Twitter) {
if (
type === LinkType.LinkedIn ||
type === LinkType.Twitter ||
type === LinkType.Facebook
) {
return <SocialLink href={absoluteUrl} type={type} label={displayedValue} />;
}
@@ -43,7 +43,9 @@ export const LinksDisplay = ({ value, onLinkClick }: LinksDisplayProps) => {
return (
<ExpandableList>
{links.map(({ url, label, type }, index) =>
type === LinkType.LinkedIn || type === LinkType.Twitter ? (
type === LinkType.LinkedIn ||
type === LinkType.Twitter ||
type === LinkType.Facebook ? (
<SocialLink
key={index}
href={url}
@@ -23,7 +23,11 @@ export const URLDisplay = ({ value }: URLDisplayProps) => {
const type = checkUrlType(absoluteUrl);
if (type === LinkType.LinkedIn || type === LinkType.Twitter) {
if (
type === LinkType.LinkedIn ||
type === LinkType.Twitter ||
type === LinkType.Facebook
) {
return (
<EllipsisDisplay>
<SocialLink
@@ -21,4 +21,8 @@ describe('checkUrlType', () => {
it('should return "url", if neither linkedin nor twitter url', () => {
expect(checkUrlType('https://www.example.com')).toBe('url');
});
it('should return "facebook", if facebook url', () => {
expect(checkUrlType('https://www.facebook.com/john-doe')).toBe('facebook');
});
});
@@ -1,40 +0,0 @@
import { getDisplayValueByUrlType } from '~/utils/getDisplayValueByUrlType';
import { LinkType } from 'twenty-ui/navigation';
describe('getDisplayValueByUrlType', () => {
it('should return the linkedin username from the url', () => {
expect(
getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/in/håkan-fisk',
}),
).toBe('håkan-fisk');
expect(
getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/in/Matías',
}),
).toBe('Matías');
expect(
getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/in/Mårten',
}),
).toBe('Mårten');
expect(
getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/in/Sörvik',
}),
).toBe('Sörvik');
});
it('should return the twitter username from the url', () => {
expect(
getDisplayValueByUrlType({
type: LinkType.Twitter,
href: 'https://www.twitter.com/john-doe',
}),
).toBe('@john-doe');
});
});
@@ -9,6 +9,9 @@ export const checkUrlType = (url: string) => {
if (/^(https?:\/\/)?(www\.)?x\.com\/.+$/.test(url)) {
return LinkType.Twitter;
}
if (/^(https?:\/\/)?(www\.)?facebook\.com\/.+$/.test(url)) {
return LinkType.Facebook;
}
return LinkType.Url;
};
@@ -1,34 +0,0 @@
import { isDefined } from 'twenty-shared/utils';
import { type LinkType } from 'twenty-ui/navigation';
type getUrlDisplayValueByUrlTypeProps = {
type: LinkType;
href: string;
};
export const getDisplayValueByUrlType = ({
type,
href,
}: getUrlDisplayValueByUrlTypeProps) => {
if (type === 'linkedin') {
const matches = href.match(
/(?:https?:\/\/)?(?:www.)?linkedin.com\/(?:in|company|school)\/(.*)/,
);
if (isDefined(matches?.[1])) {
return decodeURIComponent(matches?.[1]);
} else {
return 'LinkedIn';
}
}
if (type === 'twitter') {
const matches = href.match(
/(?:https?:\/\/)?(?:www.)?twitter.com\/([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
);
if (isDefined(matches?.[1])) {
return `@${matches?.[1]}`;
} else {
return '@twitter';
}
}
};
@@ -7,6 +7,7 @@ export enum LinkType {
Url = 'url',
LinkedIn = 'linkedin',
Twitter = 'twitter',
Facebook = 'facebook',
}
type SocialLinkProps = {
@@ -0,0 +1,149 @@
import { LinkType } from '@ui/navigation/link/components/SocialLink';
import { getDisplayValueByUrlType } from '../getDisplayValueByUrlType';
describe('getDisplayValueByUrlType', () => {
describe('linkedin', () => {
it('should extract username from LinkedIn profile URL', () => {
const result = getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/in/johndoe',
});
expect(result).toBe('johndoe');
});
it('should extract company name from LinkedIn company URL', () => {
const result = getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/company/acme-corp',
});
expect(result).toBe('acme-corp');
});
it('should extract school name from LinkedIn school URL', () => {
const result = getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/school/mit',
});
expect(result).toBe('mit');
});
it('should handle LinkedIn URL without protocol', () => {
const result = getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'linkedin.com/in/johndoe',
});
expect(result).toBe('johndoe');
});
it('should handle LinkedIn URL without www', () => {
const result = getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://linkedin.com/in/johndoe',
});
expect(result).toBe('johndoe');
});
it('should decode URL-encoded characters in LinkedIn username', () => {
const result = getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/in/john%20doe',
});
expect(result).toBe('john doe');
});
it('should return "LinkedIn" for invalid LinkedIn URLs', () => {
const result = getDisplayValueByUrlType({
type: LinkType.LinkedIn,
href: 'https://www.linkedin.com/feed',
});
expect(result).toBe('LinkedIn');
});
});
describe('twitter', () => {
it('should extract username from Twitter URL with @ prefix', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Twitter,
href: 'https://www.twitter.com/johndoe',
});
expect(result).toBe('@johndoe');
});
it('should handle Twitter URL without protocol', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Twitter,
href: 'twitter.com/johndoe',
});
expect(result).toBe('@johndoe');
});
it('should handle Twitter URL without www', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Twitter,
href: 'https://twitter.com/johndoe',
});
expect(result).toBe('@johndoe');
});
it('should return "@twitter" when no username can be extracted', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Twitter,
href: 'https://www.twitter.com',
});
expect(result).toBe('@twitter');
});
});
describe('facebook', () => {
it('should extract username from Facebook profile URL', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Facebook,
href: 'https://www.facebook.com/johndoe',
});
expect(result).toBe('johndoe');
});
it('should handle Facebook URL without protocol', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Facebook,
href: 'facebook.com/johndoe',
});
expect(result).toBe('johndoe');
});
it('should handle Facebook URL without www', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Facebook,
href: 'https://facebook.com/johndoe',
});
expect(result).toBe('johndoe');
});
it('should decode URL-encoded characters in Facebook username', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Facebook,
href: 'https://www.facebook.com/john%20doe',
});
expect(result).toBe('john doe');
});
it('should return "Facebook" for invalid Facebook URLs', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Facebook,
href: 'https://www.facebook.com/',
});
expect(result).toBe('Facebook');
});
});
describe('url type', () => {
it('should return undefined for generic url type', () => {
const result = getDisplayValueByUrlType({
type: LinkType.Url,
href: 'https://example.com',
});
expect(result).toBeUndefined();
});
});
});
@@ -31,4 +31,13 @@ export const getDisplayValueByUrlType = ({
return '@twitter';
}
}
if (type === 'facebook') {
const matches = href.match(/(?:https?:\/\/)?(?:www.)?facebook.com\/(.+)/);
if (isDefined(matches?.[1])) {
return decodeURIComponent(matches?.[1]);
} else {
return 'Facebook';
}
}
};