Merge "LabelTypes: Return an Optional to make clear that value can be absent"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.ts b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.ts
index b576896..0f8752d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.ts
@@ -43,6 +43,9 @@
   @property({type: Boolean})
   saveOnChange = false;
 
+  @property({type: Boolean})
+  showTooltipBelow = false;
+
   private readonly restApiService = appContext.restApiService;
 
   /** @override */
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
index 9943b58..3ebb58f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
@@ -34,6 +34,7 @@
     id="sideBySideBtn"
     link=""
     has-tooltip=""
+    position-below="[[showTooltipBelow]]"
     class$="[[_computeSideBySideSelected(mode)]]"
     title="Side-by-side diff"
     aria-pressed="[[isSideBySideSelected(mode)]]"
@@ -45,6 +46,7 @@
     id="unifiedBtn"
     link=""
     has-tooltip=""
+    position-below="[[showTooltipBelow]]"
     title="Unified diff"
     class$="[[_computeUnifiedSelected(mode)]]"
     aria-pressed="[[isUnifiedSelected(mode)]]"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
index 8d69007d..743f905 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
@@ -343,6 +343,7 @@
             id="modeSelect"
             save-on-change="[[!_diffPrefsDisabled]]"
             mode="{{changeViewState.diffMode}}"
+            show-tooltip-below=""
           ></gr-diff-mode-selector>
         </div>
         <span
@@ -355,6 +356,7 @@
               link=""
               class="prefsButton"
               has-tooltip=""
+              position-below=""
               title="Diff preferences"
               on-click="_handlePrefsTap"
               ><iron-icon icon="gr-icons:settings"></iron-icon
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.ts
similarity index 60%
rename from polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.js
rename to polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.ts
index df8632f..bb70855 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.ts
@@ -15,19 +15,26 @@
  * limitations under the License.
  */
 
-import '../../../test/common-test-setup-karma.js';
-import './gr-avatar.js';
-import {getPluginLoader} from '../gr-js-api-interface/gr-plugin-loader.js';
-import {appContext} from '../../../services/app-context.js';
+import '../../../test/common-test-setup-karma';
+import './gr-avatar';
+import {GrAvatar} from './gr-avatar';
+import {getPluginLoader} from '../gr-js-api-interface/gr-plugin-loader';
+import {appContext} from '../../../services/app-context';
+import {AvatarInfo} from '../../../types/common';
+import {
+  createAccountWithEmail,
+  createAccountWithId,
+} from '../../../test/test-data-generators';
 
 const basicFixture = fixtureFromElement('gr-avatar');
 
 suite('gr-avatar tests', () => {
-  let element;
-  const defaultAvatars = [
+  let element: GrAvatar;
+  const defaultAvatars: AvatarInfo[] = [
     {
       url: 'https://cdn.example.com/s12-p/photo.jpg',
       height: 12,
+      width: 0,
     },
   ];
 
@@ -36,68 +43,74 @@
   });
 
   test('account without avatar', () => {
-    assert.equal(
-        element._buildAvatarURL({
-          _account_id: 123,
-        }),
-        '');
+    assert.equal(element._buildAvatarURL(createAccountWithId(123)), '');
   });
 
   test('methods', () => {
     assert.equal(
-        element._buildAvatarURL({
-          _account_id: 123,
-          avatars: defaultAvatars,
-        }),
-        '/accounts/123/avatar?s=16');
+      element._buildAvatarURL({
+        ...createAccountWithId(123),
+        avatars: defaultAvatars,
+      }),
+      '/accounts/123/avatar?s=16'
+    );
     assert.equal(
-        element._buildAvatarURL({
-          email: 'test@example.com',
-          avatars: defaultAvatars,
-        }),
-        '/accounts/test%40example.com/avatar?s=16');
+      element._buildAvatarURL({
+        ...createAccountWithEmail('test@example.com'),
+        avatars: defaultAvatars,
+      }),
+      '/accounts/test%40example.com/avatar?s=16'
+    );
     assert.equal(
-        element._buildAvatarURL({
-          name: 'John Doe',
-          avatars: defaultAvatars,
-        }),
-        '/accounts/John%20Doe/avatar?s=16');
+      element._buildAvatarURL({
+        name: 'John Doe',
+        avatars: defaultAvatars,
+      }),
+      '/accounts/John%20Doe/avatar?s=16'
+    );
     assert.equal(
-        element._buildAvatarURL({
-          username: 'John_Doe',
-          avatars: defaultAvatars,
-        }),
-        '/accounts/John_Doe/avatar?s=16');
+      element._buildAvatarURL({
+        username: 'John_Doe',
+        avatars: defaultAvatars,
+      }),
+      '/accounts/John_Doe/avatar?s=16'
+    );
     assert.equal(
-        element._buildAvatarURL({
-          _account_id: 123,
-          avatars: [
-            {
-              url: 'https://cdn.example.com/s12-p/photo.jpg',
-              height: 12,
-            },
-            {
-              url: 'https://cdn.example.com/s16-p/photo.jpg',
-              height: 16,
-            },
-            {
-              url: 'https://cdn.example.com/s100-p/photo.jpg',
-              height: 100,
-            },
-          ],
-        }),
-        'https://cdn.example.com/s16-p/photo.jpg');
+      element._buildAvatarURL({
+        ...createAccountWithId(123),
+        avatars: [
+          {
+            url: 'https://cdn.example.com/s12-p/photo.jpg',
+            height: 12,
+            width: 0,
+          },
+          {
+            url: 'https://cdn.example.com/s16-p/photo.jpg',
+            height: 16,
+            width: 0,
+          },
+          {
+            url: 'https://cdn.example.com/s100-p/photo.jpg',
+            height: 100,
+            width: 0,
+          },
+        ] as AvatarInfo[],
+      }),
+      'https://cdn.example.com/s16-p/photo.jpg'
+    );
     assert.equal(
-        element._buildAvatarURL({
-          _account_id: 123,
-          avatars: [
-            {
-              url: 'https://cdn.example.com/s95-p/photo.jpg',
-              height: 95,
-            },
-          ],
-        }),
-        '/accounts/123/avatar?s=16');
+      element._buildAvatarURL({
+        ...createAccountWithId(123),
+        avatars: [
+          {
+            url: 'https://cdn.example.com/s95-p/photo.jpg',
+            height: 95,
+            width: 0,
+          },
+        ] as AvatarInfo[],
+      }),
+      '/accounts/123/avatar?s=16'
+    );
     assert.equal(element._buildAvatarURL(undefined), '');
   });
 
@@ -114,7 +127,7 @@
 
       element.imageSize = 64;
       element.account = {
-        _account_id: 123,
+        ...createAccountWithId(123),
         avatars: defaultAvatars,
       };
       flush();
@@ -131,14 +144,14 @@
         assert.isFalse(element.hasAttribute('hidden'));
 
         assert.isTrue(
-            element.style.backgroundImage.includes(
-                '/accounts/123/avatar?s=64'));
+          element.style.backgroundImage.includes('/accounts/123/avatar?s=64')
+        );
       });
     });
   });
 
   suite('plugin has avatars', () => {
-    let element;
+    let element: GrAvatar;
 
     setup(() => {
       stub('gr-avatar', '_getConfig').callsFake(() =>
@@ -166,7 +179,7 @@
   });
 
   suite('config not set', () => {
-    let element;
+    let element: GrAvatar;
 
     setup(() => {
       stub('gr-avatar', '_getConfig').callsFake(() => Promise.resolve({}));
@@ -180,7 +193,7 @@
 
       element.imageSize = 64;
       element.account = {
-        _account_id: 123,
+        ...createAccountWithId(123),
         avatars: defaultAvatars,
       };
       // Emulate plugins loaded.
@@ -195,4 +208,3 @@
     });
   });
 });
-
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index 0f2608e..96c05ee 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -43,6 +43,7 @@
   ActionNameToActionInfoMap,
   ApprovalInfo,
   AuthInfo,
+  AvatarInfo,
   BasePatchSetNum,
   BranchName,
   BrandType,
@@ -124,6 +125,7 @@
   ActionNameToActionInfoMap,
   ApprovalInfo,
   AuthInfo,
+  AvatarInfo,
   BasePatchSetNum,
   BranchName,
   BrandType,