Browse Source

fix(ui) Fix avatars for simple emoji

Javascript strings are UCS2 encoded resulting in slicing/substring
chopping 4+ byte characters in half. Thankfully `Array.from` handles
simple emoji well. This change will not handle emoji created with
combining diacritical marks or zero-width-joiners. I didn't think their
infrequent use justified pulling in another dependency like `runes`.
Mark Story 6 years ago
parent
commit
9b8c9685f6

+ 6 - 1
src/sentry/static/sentry/app/components/letterAvatar.jsx

@@ -50,7 +50,12 @@ const LetterAvatar = createReactClass({
 
 
   getInitials() {
   getInitials() {
     let names = (this.props.displayName.trim() || '?').split(' ');
     let names = (this.props.displayName.trim() || '?').split(' ');
-    let initials = names[0][0] + (names.length > 1 ? names[names.length - 1][0] : '');
+    // Use Array.from as slicing and substring() work on ucs2 segments which
+    // results in only getting half of any 4+ byte character.
+    let initials = Array.from(names[0])[0];
+    if (names.length > 1) {
+      initials += Array.from(names[names.length - 1])[0];
+    }
     return initials.toUpperCase();
     return initials.toUpperCase();
   },
   },
 
 

+ 18 - 0
tests/js/spec/components/letterAvatar.spec.jsx

@@ -31,6 +31,14 @@ describe('LetterAvatar', function() {
     identifier: 'janedoe@example.com',
     identifier: 'janedoe@example.com',
     displayName: ' ',
     displayName: ' ',
   };
   };
+  const USER_8 = {
+    identifier: 'janedoe@example.com',
+    displayName: '\u2603super \u2603duper',
+  };
+  const USER_9 = {
+    identifier: 'janedoe@example.com',
+    displayName: 'jane austen doe',
+  };
 
 
   describe('getInitials()', function() {
   describe('getInitials()', function() {
     it('should get initials based on name', function() {
     it('should get initials based on name', function() {
@@ -62,6 +70,16 @@ describe('LetterAvatar', function() {
       let letterAvatar = TestUtils.renderIntoDocument(<LetterAvatar {...USER_6} />);
       let letterAvatar = TestUtils.renderIntoDocument(<LetterAvatar {...USER_6} />);
       expect(letterAvatar.getInitials()).toEqual('JD');
       expect(letterAvatar.getInitials()).toEqual('JD');
     });
     });
+
+    it('should not slice multibyte characters in half', function() {
+      let letterAvatar = TestUtils.renderIntoDocument(<LetterAvatar {...USER_8} />);
+      expect(letterAvatar.getInitials()).toEqual('\u2603\u2603');
+    });
+
+    it('should pick most last name', function() {
+      let letterAvatar = TestUtils.renderIntoDocument(<LetterAvatar {...USER_9} />);
+      expect(letterAvatar.getInitials()).toEqual('JD');
+    });
   });
   });
 
 
   describe('getColor()', function() {
   describe('getColor()', function() {