feat: add feature to customize onClick behaviour for phone, email and links data type (#16265)

Fixes Issue: #15797

This PR adds a configurable click behavior setting for Phone, Email, and
Links field types, allowing users to customize what happens when
clicking on these fields.

A new option (**Click Behaviour**) to configure the default behaviour
for onClick of data is added in the settings page (Settings → Data Model
→ Object → Field Edit) for Phone, Email and Links data types.

Users can now choose between two actions:
- **Copy to clipboard**: Copies the value to clipboard with a success
toast message
- **Open as link**: Opens the value as a link (tel:, mailto:, or
http/https)

The default behaviour is persisted for all these three types(phone- Copy
to clipboard, email & links: open link) to maintain backward
compatibility.

**Screenshots :** 
<img width="2084" height="1736" alt="image"
src="https://github.com/user-attachments/assets/eb5d129c-e3e0-4334-b426-eb38d8a4840c"
/>

<img width="1474" height="1428" alt="image"
src="https://github.com/user-attachments/assets/487f4e44-6151-4254-bc5c-5398be5fc087"
/>
<img width="1594" height="1398" alt="image"
src="https://github.com/user-attachments/assets/abdbc213-7223-4d57-809e-4d55c90cda80"
/>

---------

Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
This commit is contained in:
Abhishek Kumar
2025-12-12 19:04:47 +05:30
committed by GitHub
parent 4f91b48470
commit 8dbcd506ed
26 changed files with 323 additions and 60 deletions
@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import React, { useMemo } from 'react';
import { type FieldEmailsValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
@@ -10,6 +10,7 @@ import { THEME_COMMON } from 'twenty-ui/theme';
type EmailsDisplayProps = {
value?: FieldEmailsValue;
isFocused?: boolean;
onEmailClick?: (email: string, event: React.MouseEvent<HTMLElement>) => void;
};
const themeSpacing = THEME_COMMON.spacingMultiplicator;
@@ -27,7 +28,11 @@ const StyledContainer = styled.div`
width: 100%;
`;
export const EmailsDisplay = ({ value, isFocused }: EmailsDisplayProps) => {
export const EmailsDisplay = ({
value,
isFocused,
onEmailClick,
}: EmailsDisplayProps) => {
const emails = useMemo(
() =>
[
@@ -40,13 +45,23 @@ export const EmailsDisplay = ({ value, isFocused }: EmailsDisplayProps) => {
return isFocused ? (
<ExpandableList isChipCountDisplayed>
{emails.map((email, index) => (
<RoundedLink key={index} label={email} href={`mailto:${email}`} />
<RoundedLink
key={index}
label={email}
href={`mailto:${email}`}
onClick={(event) => onEmailClick?.(email, event)}
/>
))}
</ExpandableList>
) : (
<StyledContainer>
{emails.map((email, index) => (
<RoundedLink key={index} label={email} href={`mailto:${email}`} />
<RoundedLink
key={index}
label={email}
href={`mailto:${email}`}
onClick={(event) => onEmailClick?.(email, event)}
/>
))}
</StyledContainer>
);
@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import React, { useMemo } from 'react';
import { getFieldLinkDefinedLinks } from '@/object-record/record-field/ui/meta-types/input/utils/getFieldLinkDefinedLinks';
import { type FieldLinksValue } from '@/object-record/record-field/ui/types/FieldMetadata';
@@ -13,9 +13,10 @@ import { checkUrlType } from '~/utils/checkUrlType';
type LinksDisplayProps = {
value?: FieldLinksValue;
onLinkClick?: (url: string, event: React.MouseEvent<HTMLElement>) => void;
};
export const LinksDisplay = ({ value }: LinksDisplayProps) => {
export const LinksDisplay = ({ value, onLinkClick }: LinksDisplayProps) => {
const links = useMemo(() => {
if (!isDefined(value)) {
return [];
@@ -43,9 +44,20 @@ export const LinksDisplay = ({ value }: LinksDisplayProps) => {
<ExpandableList>
{links.map(({ url, label, type }, index) =>
type === LinkType.LinkedIn || type === LinkType.Twitter ? (
<SocialLink key={index} href={url} type={type} label={label} />
<SocialLink
key={index}
href={url}
type={type}
label={label}
onClick={(event) => onLinkClick?.(url, event)}
/>
) : (
<RoundedLink key={index} href={url} label={label} />
<RoundedLink
key={index}
href={url}
label={label}
onClick={(event) => onLinkClick?.(url, event)}
/>
),
)}
</ExpandableList>
@@ -75,13 +75,6 @@ export const PhonesDisplay = ({
}
};
const handleClick = (
number: string,
event: React.MouseEvent<HTMLElement>,
) => {
onPhoneNumberClick?.(number, event);
};
return isFocused ? (
<ExpandableList isChipCountDisplayed>
{phones.map(({ number, callingCode }, index) => {
@@ -95,7 +88,9 @@ export const PhonesDisplay = ({
label={
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
}
onClick={(event) => handleClick(callingCode + number, event)}
onClick={(event) =>
onPhoneNumberClick?.(callingCode + number, event)
}
/>
);
})}
@@ -113,7 +108,9 @@ export const PhonesDisplay = ({
label={
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
}
onClick={(event) => handleClick(callingCode + number, event)}
onClick={(event) =>
onPhoneNumberClick?.(callingCode + number, event)
}
/>
);
})}