diff --git a/src/components/common/CreatorCard.tsx b/src/components/common/CreatorCard.tsx
index cc9edae..f5068e2 100644
--- a/src/components/common/CreatorCard.tsx
+++ b/src/components/common/CreatorCard.tsx
@@ -24,7 +24,10 @@ import toast from 'react-hot-toast';
import showToast from '@/utils/toast.util';
import { formatCompactNumber } from '@/utils/numberFormat.utils';
import { formatCreatorKeyPriceDisplay } from '@/utils/keyPriceDisplay.utils';
-import { formatCreatorHandle } from '@/utils/handleDisplay.utils';
+import {
+ formatCreatorHandle,
+ truncateHandle,
+} from '@/utils/handleDisplay.utils';
import { normalizeCreatorDisplayName } from '@/utils/creatorDisplayName.utils';
import { getCreatorPriceChartAccessibilityCopy } from '@/utils/creatorPriceChartAccessibility.utils';
import { formatJoinDate } from '@/utils/formatJoinDate';
@@ -78,6 +81,8 @@ const CreatorCard: React.FC = ({
const displayInstructorHandle =
formatCreatorHandle(creator.instructorId) || '@creator';
const displaySocialHandle = formatCreatorHandle(creator.socialHandle);
+ const truncatedInstructorHandle = truncateHandle(displayInstructorHandle);
+ const truncatedSocialHandle = truncateHandle(displaySocialHandle);
const displayCreatorName =
normalizeCreatorDisplayName(creator.title) || 'Unnamed creator';
const priceChartAccessibility = getCreatorPriceChartAccessibilityCopy({
@@ -300,7 +305,7 @@ const CreatorCard: React.FC = ({
creatorShareSupply={creator.creatorShareSupply}
isVerified={creator.isVerified}
>
- {displayInstructorHandle}
+ {truncatedInstructorHandle}
@@ -324,7 +329,7 @@ const CreatorCard: React.FC = ({
creatorShareSupply={creator.creatorShareSupply}
isVerified={creator.isVerified}
>
- {displaySocialHandle}
+ {truncatedSocialHandle}
) : (
@@ -410,7 +415,7 @@ const CreatorCard: React.FC = ({
}
value={
creator.socialHandle
- ? displaySocialHandle
+ ? truncatedSocialHandle
: 'No public handle'
}
valueTitle={
diff --git a/src/utils/__tests__/handleDisplay.utils.test.ts b/src/utils/__tests__/handleDisplay.utils.test.ts
index e93e0e1..11e7829 100644
--- a/src/utils/__tests__/handleDisplay.utils.test.ts
+++ b/src/utils/__tests__/handleDisplay.utils.test.ts
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
-import { formatCreatorHandle } from '../handleDisplay.utils';
+import { formatCreatorHandle, truncateHandle } from '../handleDisplay.utils';
describe('formatCreatorHandle', () => {
it('lowercases mixed-case handles and prepends @', () => {
@@ -32,7 +32,9 @@ describe('formatCreatorHandle', () => {
});
it('is idempotent: formatting an already-formatted handle is a no-op', () => {
- expect(formatCreatorHandle(formatCreatorHandle('ARivers'))).toBe('@arivers');
+ expect(formatCreatorHandle(formatCreatorHandle('ARivers'))).toBe(
+ '@arivers'
+ );
});
it('does not modify the underlying string the caller passes in', () => {
@@ -44,3 +46,35 @@ describe('formatCreatorHandle', () => {
expect(raw).toBe('ARivers');
});
});
+
+describe('truncateHandle', () => {
+ it('returns short handle unchanged (under max length)', () => {
+ expect(truncateHandle('@short', 20)).toBe('@short');
+ expect(truncateHandle('abc', 5)).toBe('abc');
+ });
+
+ it('returns exact max handle unchanged', () => {
+ expect(truncateHandle('12345678901234567890', 20)).toBe(
+ '12345678901234567890'
+ );
+ expect(truncateHandle('abcde', 5)).toBe('abcde');
+ });
+
+ it('truncates one over max handle with ellipsis', () => {
+ expect(truncateHandle('123456789012345678901', 20)).toBe(
+ '12345678901234567890...'
+ );
+ expect(truncateHandle('abcdef', 5)).toBe('abcde...');
+ });
+
+ it('uses default max of 20 characters when maxLength is not specified', () => {
+ // Exactly 20 chars should not be truncated
+ expect(truncateHandle('12345678901234567890')).toBe(
+ '12345678901234567890'
+ );
+ // 21 chars (one over max) should be truncated
+ expect(truncateHandle('123456789012345678901')).toBe(
+ '12345678901234567890...'
+ );
+ });
+});
diff --git a/src/utils/handleDisplay.utils.ts b/src/utils/handleDisplay.utils.ts
index 4d5ebef..7989e7a 100644
--- a/src/utils/handleDisplay.utils.ts
+++ b/src/utils/handleDisplay.utils.ts
@@ -19,8 +19,25 @@ export const formatCreatorHandle = (raw: string | null | undefined): string => {
if (raw == null) return '';
const trimmed = raw.trim();
if (trimmed === '') return '';
- const withoutLeadingAt = trimmed.startsWith('@') ? trimmed.slice(1) : trimmed;
+ const withoutLeadingAt = trimmed.startsWith('@')
+ ? trimmed.slice(1)
+ : trimmed;
const normalised = withoutLeadingAt.trim().toLowerCase();
if (normalised === '') return '';
return `@${normalised}`;
};
+
+/**
+ * Truncates a creator handle to a maximum length, appending an ellipsis
+ * only when truncation occurs.
+ */
+export const truncateHandle = (
+ handle: string,
+ maxLength: number = 20
+): string => {
+ if (!handle) return '';
+ if (handle.length <= maxLength) {
+ return handle;
+ }
+ return `${handle.slice(0, maxLength)}...`;
+};