diff --git a/.changeset/light-roses-design.md b/.changeset/light-roses-design.md new file mode 100644 index 00000000000..d9080767278 --- /dev/null +++ b/.changeset/light-roses-design.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': patch +'@clerk/types': patch +--- + +Add copy and truncation options to `` component. diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 4ea9cf54e4d..00aace0bbb4 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -3,7 +3,7 @@ { "path": "./dist/clerk.js", "maxSize": "590kB" }, { "path": "./dist/clerk.browser.js", "maxSize": "73.21KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "55KB" }, - { "path": "./dist/ui-common*.js", "maxSize": "98.2KB" }, + { "path": "./dist/ui-common*.js", "maxSize": "99KB" }, { "path": "./dist/vendors*.js", "maxSize": "36KB" }, { "path": "./dist/coinbase*.js", "maxSize": "35.5KB" }, { "path": "./dist/createorganization*.js", "maxSize": "5KB" }, diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx index 437cd1e21be..738b7a1aab2 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx @@ -124,7 +124,12 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer {/* TODO(@COMMERCE): needs localization */} - + + ); +} + export const LineItems = { Root, Group, diff --git a/packages/clerk-js/src/ui/utils/__tests__/truncateTextWithEndVisible.test.ts b/packages/clerk-js/src/ui/utils/__tests__/truncateTextWithEndVisible.test.ts new file mode 100644 index 00000000000..47024086e9c --- /dev/null +++ b/packages/clerk-js/src/ui/utils/__tests__/truncateTextWithEndVisible.test.ts @@ -0,0 +1,41 @@ +import { truncateWithEndVisible } from '../truncateTextWithEndVisible'; + +describe('truncateWithEndVisible', () => { + test('should return empty string when input is empty', () => { + expect(truncateWithEndVisible('')).toBe(''); + }); + + test('should return original string when length is less than maxLength', () => { + expect(truncateWithEndVisible('short')).toBe('short'); + expect(truncateWithEndVisible('123456789', 10)).toBe('123456789'); + }); + + test('should truncate string with default parameters', () => { + expect(truncateWithEndVisible('this is a very long string')).toBe('this is a ve...tring'); + }); + + test('should truncate string with custom maxLength', () => { + expect(truncateWithEndVisible('this is a very long string', 15)).toBe('this is...tring'); + }); + + test('should truncate string with custom endChars', () => { + expect(truncateWithEndVisible('this is a very long string', 20, 3)).toBe('this is a very...ing'); + }); + + test('should handle edge case where maxLength is too small', () => { + expect(truncateWithEndVisible('1234567890', 5, 3)).toBe('...890'); + }); + + test('should handle email addresses', () => { + expect(truncateWithEndVisible('test@example.com', 10)).toBe('te...e.com'); + }); + + test('should handle very long strings', () => { + const longString = 'a'.repeat(1000); + expect(truncateWithEndVisible(longString, 20)).toBe('aaaaaaaaaaaa...aaaaa'); + }); + + test('should handle strings with spaces', () => { + expect(truncateWithEndVisible('hello world this is a test', 15)).toBe('hello w... test'); + }); +}); diff --git a/packages/clerk-js/src/ui/utils/truncateTextWithEndVisible.ts b/packages/clerk-js/src/ui/utils/truncateTextWithEndVisible.ts new file mode 100644 index 00000000000..e6b54e285b4 --- /dev/null +++ b/packages/clerk-js/src/ui/utils/truncateTextWithEndVisible.ts @@ -0,0 +1,38 @@ +/** + * Truncates a string to show the beginning and the last N characters, + * with an ellipsis in the middle. + * + * @param {string} str - The string to truncate + * @param {number} maxLength - Maximum total length of the truncated string (including ellipsis) + * @param {number} endChars - Number of characters to preserve at the end + * @return {string} - Truncated string with ellipsis in the middle + * + * @example + * truncateWithEndVisible('this is a very long string') // returns 'this is a ve...tring' + * truncateWithEndVisible('test@email.com', 10) // returns 'te...e.com' + */ +export function truncateWithEndVisible(str: string, maxLength = 20, endChars = 5): string { + const ELLIPSIS = '...'; + const ELLIPSIS_LENGTH = ELLIPSIS.length; + + if (!str || str.length <= maxLength) { + return str; + } + + if (maxLength <= endChars + ELLIPSIS_LENGTH) { + return ELLIPSIS + str.slice(-endChars); + } + + const chars = Array.from(str); + const totalChars = chars.length; + + if (totalChars <= maxLength) { + return str; + } + + const beginLength = maxLength - endChars - ELLIPSIS_LENGTH; + const beginPortion = chars.slice(0, beginLength).join(''); + const endPortion = chars.slice(-endChars).join(''); + + return beginPortion + ELLIPSIS + endPortion; +} diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index a992b7dfbef..7db117b5895 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -162,6 +162,7 @@ export type ElementsConfig = { lineItemsDescriptionText: WithOptions; lineItemsDescriptionSuffix: WithOptions; lineItemsDescriptionPrefix: WithOptions; + lineItemsCopyButton: WithOptions; logoBox: WithOptions; logoImage: WithOptions;