Merge changes Iec0f7a36,I7931944f

* changes:
  Convert gr-smart-search_test.js to typescript
  Fix template problems with gr-smart-search
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index 6fd8639..d8036e1 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -109,7 +109,6 @@
     "elements/checks/gr-hovercard-run_html.ts",
     "elements/core/gr-main-header/gr-main-header_html.ts",
     "elements/core/gr-search-bar/gr-search-bar_html.ts",
-    "elements/core/gr-smart-search/gr-smart-search_html.ts",
     "elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_html.ts",
     "elements/diff/gr-diff-builder/gr-diff-builder-element_html.ts",
     "elements/diff/gr-diff-host/gr-diff-host_html.ts",
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts
index aa7e2e0..7419713 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts
@@ -32,6 +32,12 @@
 const SELF_EXPRESSION = 'self';
 const ME_EXPRESSION = 'me';
 
+declare global {
+  interface HTMLElementEventMap {
+    'handle-search': CustomEvent<SearchBarHandleSearchDetail>;
+  }
+}
+
 @customElement('gr-smart-search')
 export class GrSmartSearch extends PolymerElement {
   static get template() {
@@ -39,7 +45,7 @@
   }
 
   @property({type: String})
-  searchQuery?: string;
+  searchQuery = '';
 
   @property({type: Object})
   _config?: ServerInfo;
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.js
deleted file mode 100644
index f3a9965..0000000
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.js
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * @license
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import '../../../test/common-test-setup-karma.js';
-import './gr-smart-search.js';
-import {stubRestApi} from '../../../test/test-utils.js';
-
-const basicFixture = fixtureFromElement('gr-smart-search');
-
-suite('gr-smart-search tests', () => {
-  let element;
-
-  setup(() => {
-    element = basicFixture.instantiate();
-  });
-
-  test('Autocompletes accounts', () => {
-    stubRestApi('getSuggestedAccounts').callsFake(() =>
-      Promise.resolve([
-        {
-          name: 'fred',
-          email: 'fred@goog.co',
-        },
-      ])
-    );
-    return element._fetchAccounts('owner', 'fr').then(s => {
-      assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
-    });
-  });
-
-  test('Inserts self as option when valid', () => {
-    stubRestApi('getSuggestedAccounts').callsFake( () =>
-      Promise.resolve([
-        {
-          name: 'fred',
-          email: 'fred@goog.co',
-        },
-      ])
-    );
-    element._fetchAccounts('owner', 's')
-        .then(s => {
-          assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
-          assert.deepEqual(s[1], {text: 'owner:self'});
-        })
-        .then(() => element._fetchAccounts('owner', 'selfs'))
-        .then(s => {
-          assert.notEqual(s[0], {text: 'owner:self'});
-        });
-  });
-
-  test('Inserts me as option when valid', () => {
-    stubRestApi('getSuggestedAccounts').callsFake( () =>
-      Promise.resolve([
-        {
-          name: 'fred',
-          email: 'fred@goog.co',
-        },
-      ])
-    );
-    return element._fetchAccounts('owner', 'm')
-        .then(s => {
-          assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
-          assert.deepEqual(s[1], {text: 'owner:me'});
-        })
-        .then(() => element._fetchAccounts('owner', 'meme'))
-        .then(s => {
-          assert.notEqual(s[0], {text: 'owner:me'});
-        });
-  });
-
-  test('Autocompletes groups', () => {
-    stubRestApi('getSuggestedGroups').callsFake( () =>
-      Promise.resolve({
-        Polygerrit: 0,
-        gerrit: 0,
-        gerrittest: 0,
-      })
-    );
-    return element._fetchGroups('ownerin', 'pol').then(s => {
-      assert.deepEqual(s[0], {text: 'ownerin:Polygerrit'});
-    });
-  });
-
-  test('Autocompletes projects', () => {
-    stubRestApi('getSuggestedProjects').callsFake( () =>
-      Promise.resolve({Polygerrit: 0}));
-    return element._fetchProjects('project', 'pol').then(s => {
-      assert.deepEqual(s[0], {text: 'project:Polygerrit'});
-    });
-  });
-
-  test('Autocomplete doesnt override exact matches to input', () => {
-    stubRestApi('getSuggestedGroups').callsFake( () =>
-      Promise.resolve({
-        Polygerrit: 0,
-        gerrit: 0,
-        gerrittest: 0,
-      })
-    );
-    return element._fetchGroups('ownerin', 'gerrit').then(s => {
-      assert.deepEqual(s[0], {text: 'ownerin:Polygerrit'});
-      assert.deepEqual(s[1], {text: 'ownerin:gerrit'});
-      assert.deepEqual(s[2], {text: 'ownerin:gerrittest'});
-    });
-  });
-
-  test('Autocompletes accounts with no email', () => {
-    stubRestApi('getSuggestedAccounts').callsFake( () =>
-      Promise.resolve([{name: 'fred'}]));
-    return element._fetchAccounts('owner', 'fr').then(s => {
-      assert.deepEqual(s[0], {text: 'owner:"fred"', label: 'fred'});
-    });
-  });
-
-  test('Autocompletes accounts with email', () => {
-    stubRestApi('getSuggestedAccounts').callsFake( () =>
-      Promise.resolve([{email: 'fred@goog.co'}]));
-    return element._fetchAccounts('owner', 'fr').then(s => {
-      assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: ''});
-    });
-  });
-});
-
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.ts b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.ts
new file mode 100644
index 0000000..0218a8f
--- /dev/null
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.ts
@@ -0,0 +1,143 @@
+/**
+ * @license
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import '../../../test/common-test-setup-karma';
+import './gr-smart-search';
+import {GrSmartSearch} from './gr-smart-search';
+import {stubRestApi} from '../../../test/test-utils';
+import {EmailAddress, GroupId, UrlEncodedRepoName} from '../../../types/common';
+
+const basicFixture = fixtureFromElement('gr-smart-search');
+
+suite('gr-smart-search tests', () => {
+  let element: GrSmartSearch;
+
+  setup(() => {
+    element = basicFixture.instantiate();
+  });
+
+  test('Autocompletes accounts', () => {
+    stubRestApi('getSuggestedAccounts').callsFake(() =>
+      Promise.resolve([
+        {
+          name: 'fred',
+          email: 'fred@goog.co' as EmailAddress,
+        },
+      ])
+    );
+    return element._fetchAccounts('owner', 'fr').then(s => {
+      assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
+    });
+  });
+
+  test('Inserts self as option when valid', () => {
+    stubRestApi('getSuggestedAccounts').callsFake(() =>
+      Promise.resolve([
+        {
+          name: 'fred',
+          email: 'fred@goog.co' as EmailAddress,
+        },
+      ])
+    );
+    element
+      ._fetchAccounts('owner', 's')
+      .then(s => {
+        assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
+        assert.deepEqual(s[1], {text: 'owner:self'});
+      })
+      .then(() => element._fetchAccounts('owner', 'selfs'))
+      .then(s => {
+        assert.notEqual(s[0], {text: 'owner:self'});
+      });
+  });
+
+  test('Inserts me as option when valid', () => {
+    stubRestApi('getSuggestedAccounts').callsFake(() =>
+      Promise.resolve([
+        {
+          name: 'fred',
+          email: 'fred@goog.co' as EmailAddress,
+        },
+      ])
+    );
+    return element
+      ._fetchAccounts('owner', 'm')
+      .then(s => {
+        assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: 'fred'});
+        assert.deepEqual(s[1], {text: 'owner:me'});
+      })
+      .then(() => element._fetchAccounts('owner', 'meme'))
+      .then(s => {
+        assert.notEqual(s[0], {text: 'owner:me'});
+      });
+  });
+
+  test('Autocompletes groups', () => {
+    stubRestApi('getSuggestedGroups').callsFake(() =>
+      Promise.resolve({
+        Polygerrit: {id: '4c97682e6ce61b7247f3381b6f1789356666de7f' as GroupId},
+        gerrit: {id: '4c97682e6ce61b7247f3381b6f1789356666de7f' as GroupId},
+        gerrittest: {id: '4c97682e6ce61b7247f3381b6f1789356666de7f' as GroupId},
+      })
+    );
+    return element._fetchGroups('ownerin', 'pol').then(s => {
+      assert.deepEqual(s[0], {text: 'ownerin:Polygerrit'});
+    });
+  });
+
+  test('Autocompletes projects', () => {
+    stubRestApi('getSuggestedProjects').callsFake(() =>
+      Promise.resolve({Polygerrit: {id: 'test' as UrlEncodedRepoName}})
+    );
+    return element._fetchProjects('project', 'pol').then(s => {
+      assert.deepEqual(s[0], {text: 'project:Polygerrit'});
+    });
+  });
+
+  test('Autocomplete doesnt override exact matches to input', () => {
+    stubRestApi('getSuggestedGroups').callsFake(() =>
+      Promise.resolve({
+        Polygerrit: {id: '4c97682e6ce61b7247f3381b6f1789356666de7f' as GroupId},
+        gerrit: {id: '4c97682e6ce61b7247f3381b6f1789356666de7f' as GroupId},
+        gerrittest: {id: '4c97682e6ce61b7247f3381b6f1789356666de7f' as GroupId},
+      })
+    );
+    return element._fetchGroups('ownerin', 'gerrit').then(s => {
+      assert.deepEqual(s[0], {text: 'ownerin:Polygerrit'});
+      assert.deepEqual(s[1], {text: 'ownerin:gerrit'});
+      assert.deepEqual(s[2], {text: 'ownerin:gerrittest'});
+    });
+  });
+
+  test('Autocompletes accounts with no email', () => {
+    stubRestApi('getSuggestedAccounts').callsFake(() =>
+      Promise.resolve([{name: 'fred'}])
+    );
+    return element._fetchAccounts('owner', 'fr').then(s => {
+      assert.deepEqual(s[0], {text: 'owner:"fred"', label: 'fred'});
+    });
+  });
+
+  test('Autocompletes accounts with email', () => {
+    stubRestApi('getSuggestedAccounts').callsFake(() =>
+      Promise.resolve([{email: 'fred@goog.co' as EmailAddress}])
+    );
+    return element._fetchAccounts('owner', 'fr').then(s => {
+      assert.deepEqual(s[0], {text: 'owner:fred@goog.co', label: ''});
+    });
+  });
+});