Skip to content
Closed
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
23 changes: 18 additions & 5 deletions modules/preview-react/avatar/lib/getInitialsFromName.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
/**
Extracts initials from a person's name by taking the first letter of the first and last name parts. It also sanitizes input to filter out common name prefixes and suffixes.
*/
export const getInitialsFromName = (name: string) => {
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function lacks JSDoc documentation. Based on the codebase convention (see modules/preview-react/select/lib/utils.ts and modules/preview-react/select/lib/scrolling.ts), exported utility functions should have JSDoc comments that describe their purpose, parameters, and return values.

Consider adding JSDoc documentation like:
/**

  • Extracts initials from a person's name by taking the first letter of the first and last name parts.
  • Removes parenthetical content (e.g., titles, departments) and filters out numeric and Roman numeral suffixes.
  • @param name - The full name to extract initials from
  • @returns A string containing the extracted initials in uppercase, or an empty string if no valid name parts are found
    */

Copilot uses AI. Check for mistakes.
Comment thread
mannycarrera4 marked this conversation as resolved.
// Trim and split by one or more whitespace characters
const nameParts = name.trim().split(/\s+/).filter(Boolean);
// Remove parenthetical content (e.g., titles, departments) and trim
const sanitized = name.replace(/\([^)]*\)/g, '').trim();
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parenthetical content removal only handles round parentheses (). If the intent is to sanitize titles, departments, or similar metadata, you should also consider removing content in other bracket types like square brackets [] or curly braces {}, which are also commonly used for similar purposes.

For example, the current implementation won't sanitize "John Smith [Engineering]" or "John Smith {CEO}". Consider using a pattern like /[\(\[\{][^\)\]\}]*[\)\]\}]/g if you want to handle all bracket types.

Suggested change
const sanitized = name.replace(/\([^)]*\)/g, '').trim();
const sanitized = name.replace(/[\(\[\{][^\)\]\}]*[\)\]\}]/g, '').trim();

Copilot uses AI. Check for mistakes.

const first = nameParts[0];
// Split by one or more whitespace characters
const nameParts = sanitized.split(/\s+/).filter(Boolean);

// Filter out parts that are purely numeric or Roman numerals (suffixes like III, IV)
const filteredParts = nameParts.filter(part => {
const isNumeric = /^\d+$/.test(part);
const isRomanNumeral = /^[IVX]+$/i.test(part);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Roman numeral regex pattern is too broad and will incorrectly match valid name parts. For example, it will match "Vin" (contains only I, V, X), "Vivi" (contains only I, V, X), or any word composed solely of the letters I, V, and X in any combination. This could incorrectly filter out legitimate names or parts of names.

Consider using a more precise pattern that matches only valid Roman numerals like: /^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/i or a simpler whitelist approach that matches common suffixes: /^(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|XIII|XIV|XV)$/i

Suggested change
const isRomanNumeral = /^[IVX]+$/i.test(part);
const isRomanNumeral = /^(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|XIII|XIV|XV)$/i.test(part);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't think we need to test for roman numerals like LX. Low odds of running into Alan Smith the 60th, but let's say Pope Leo XIV is on Workday. We should probably accommodate his Holiness.

return !isNumeric && !isRomanNumeral;
});

const first = filteredParts[0];
const firstInitial = first?.[0] || '';
const last = nameParts.length > 1 ? nameParts[nameParts.length - 1] : undefined;
const last = filteredParts.length > 1 ? filteredParts[filteredParts.length - 1] : undefined;
const lastInitial = last?.[0] || '';

return `${firstInitial}${lastInitial}`;
return `${firstInitial}${lastInitial}`.toUpperCase();
};
55 changes: 55 additions & 0 deletions modules/preview-react/avatar/spec/getInitialsFromName.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {getInitialsFromName} from '../lib/getInitialsFromName';

describe('getInitialsFromName', () => {
it('should return initials from a simple first and last name', () => {
expect(getInitialsFromName('Logan McNeil')).toBe('LM');
});

it('should return initials from a single name', () => {
expect(getInitialsFromName('Logan')).toBe('L');
});

it('should remove parenthetical content', () => {
expect(getInitialsFromName('Logan McNeil (SFV, VP Director)')).toBe('LM');
});

it('should filter out Roman numeral suffixes', () => {
expect(getInitialsFromName('Logan McNeil III')).toBe('LM');
expect(getInitialsFromName('John Smith IV')).toBe('JS');
expect(getInitialsFromName('Mary Johnson II')).toBe('MJ');
});

it('should handle multiple middle names', () => {
expect(getInitialsFromName('John Paul Jones')).toBe('JJ');
});

it('should handle names with both parenthetical content and suffixes', () => {
expect(getInitialsFromName('Logan McNeil III (VP Director)')).toBe('LM');
});

it('should handle extra whitespace', () => {
expect(getInitialsFromName(' Logan McNeil ')).toBe('LM');
});

it('should return empty string for empty input', () => {
expect(getInitialsFromName('')).toBe('');
});

it('should handle names with only parenthetical content', () => {
expect(getInitialsFromName('(VP Director)')).toBe('');
});

it('should handle names with numeric suffixes', () => {
expect(getInitialsFromName('Logan McNeil 3')).toBe('LM');
});

it('should work with names from various cultures', () => {
expect(getInitialsFromName('José García')).toBe('JG');
expect(getInitialsFromName('Mohammed Al-Rashid')).toBe('MA');
});
Comment on lines +48 to +49
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function doesn't handle hyphenated names or names with apostrophes consistently. For example:

  • "Mary-Jane Smith" would produce "MS" (treating "Mary-Jane" as one word)
  • "O'Brien" would be treated as one word
  • "Jean-Luc Picard" would produce "JP"

While the current behavior may be acceptable, consider whether hyphenated first/last names should be split or kept together. The test case for "Mohammed Al-Rashid" expects "MA", which suggests the current behavior is intentional, but this should be explicitly tested for various hyphenation scenarios to ensure consistency.

Copilot uses AI. Check for mistakes.

it('should handle mixed case Roman numerals', () => {
expect(getInitialsFromName('Logan McNeil iii')).toBe('LM');
expect(getInitialsFromName('Logan McNeil Iii')).toBe('LM');
Comment on lines +51 to +53
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These test cases expose a flaw in the implementation. "Iii" and "iii" are not valid Roman numerals (the valid form is "III"), yet the implementation's regex /^[IVX]+$/i will match them. This test passes because it validates the current buggy behavior.

When the Roman numeral regex is fixed to only match valid Roman numerals, these tests should be updated to either:

  1. Expect "LI" as the result (treating "Iii" as a name part, not a suffix), or
  2. Use valid Roman numerals like "III" if the intent is to test suffix filtering
Suggested change
it('should handle mixed case Roman numerals', () => {
expect(getInitialsFromName('Logan McNeil iii')).toBe('LM');
expect(getInitialsFromName('Logan McNeil Iii')).toBe('LM');
it('should filter valid Roman numeral suffixes', () => {
expect(getInitialsFromName('Logan McNeil III')).toBe('LM');
expect(getInitialsFromName('Logan McNeil II')).toBe('LM');

Copilot uses AI. Check for mistakes.
});
});
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for edge case where someone has a single-letter name that happens to be I, V, or X (e.g., "X Æ A-12" or "Malcolm X"). Due to the overly broad Roman numeral regex /^[IVX]+$/i, these valid name parts would be incorrectly filtered out.

Add test cases like:

  • getInitialsFromName('Malcolm X') should return 'MX', not 'M'
  • getInitialsFromName('X Smith') should return 'XS', not 'S'

This edge case will be resolved when the Roman numeral regex is fixed to match only valid Roman numeral suffixes.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function does not handle unmatched parentheses or other bracket types (e.g., square brackets, curly braces). Consider adding test cases for edge cases like:

  • Unmatched opening parenthesis: "John Smith ("
  • Unmatched closing parenthesis: "John Smith )"
  • Square brackets: "John Smith [Department]"
  • Nested parentheses: "John Smith ((nested))"

The current regex /\([^)]*\)/g only handles matched parentheses, which could leave orphaned opening parentheses in the sanitized string.

Copilot uses AI. Check for mistakes.
Loading