diff --git a/web/app/components/document/sidebar/related-resources/add/external-resource.hbs b/web/app/components/document/sidebar/related-resources/add/external-resource.hbs
index 6ef1bdc0c..480171df6 100644
--- a/web/app/components/document/sidebar/related-resources/add/external-resource.hbs
+++ b/web/app/components/document/sidebar/related-resources/add/external-resource.hbs
@@ -19,7 +19,7 @@
{{@url}}
-
+
diff --git a/web/app/components/document/sidebar/related-resources/list-item/resource.hbs b/web/app/components/document/sidebar/related-resources/list-item/resource.hbs
index 1cafa3dec..0ab4d9db4 100644
--- a/web/app/components/document/sidebar/related-resources/list-item/resource.hbs
+++ b/web/app/components/document/sidebar/related-resources/list-item/resource.hbs
@@ -6,10 +6,7 @@
{{else}}
-
+
{{/if}}
diff --git a/web/app/components/document/sidebar/related-resources/list-item/resource.ts b/web/app/components/document/sidebar/related-resources/list-item/resource.ts
index 54017d1f3..c3a1a45eb 100644
--- a/web/app/components/document/sidebar/related-resources/list-item/resource.ts
+++ b/web/app/components/document/sidebar/related-resources/list-item/resource.ts
@@ -51,10 +51,11 @@ export default class DocumentSidebarRelatedResourcesListItemResourceComponent ex
/**
* The full URL of an ExternalResource
*/
- private get url() {
+ protected get url() {
assert("url must exist in the resource", "url" in this.args.resource);
return this.args.resource.url;
}
+
/**
* The url to display in the ResourceList.
* Strips the protocol, "www" and path.
diff --git a/web/app/components/favicon.hbs b/web/app/components/favicon.hbs
new file mode 100644
index 000000000..eb1338e93
--- /dev/null
+++ b/web/app/components/favicon.hbs
@@ -0,0 +1,17 @@
+{{#if this.fallbackIconIsShown}}
+
+{{else}}
+
+{{/if}}
diff --git a/web/app/components/favicon.ts b/web/app/components/favicon.ts
new file mode 100644
index 000000000..0ff42c36a
--- /dev/null
+++ b/web/app/components/favicon.ts
@@ -0,0 +1,26 @@
+import { action } from "@ember/object";
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+
+interface FaviconComponentSignature {
+ Element: SVGElement | HTMLImageElement;
+ Args: {
+ url: string;
+ };
+}
+
+export default class FaviconComponent extends Component {
+ @tracked protected fallbackIconIsShown = false;
+
+ protected readonly faviconURL = `https://www.google.com/s2/favicons?sz=64&domain=${this.args.url}`;
+
+ @action protected showFallbackIcon() {
+ this.fallbackIconIsShown = true;
+ }
+}
+
+declare module "@glint/environment-ember-loose/registry" {
+ export default interface Registry {
+ Favicon: typeof FaviconComponent;
+ }
+}
diff --git a/web/app/helpers/get-link-icon.ts b/web/app/helpers/get-link-icon.ts
deleted file mode 100644
index 09b56bc1a..000000000
--- a/web/app/helpers/get-link-icon.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { helper } from "@ember/component/helper";
-
-export interface GetLinkIconSignature {
- Args: {
- Positional: [string | undefined];
- };
- Return: string;
-}
-
-const getLinkIconHelper = helper(([url]) => {
- if (url) {
- const urlParts = url.split("/");
- let domain = urlParts[2];
-
- if (domain) {
- const domainParts = domain.split(".");
-
- domain = domainParts[domainParts.length - 2];
-
- if (domain) {
- if (domain.includes("figma")) {
- return "figma-color";
- }
- if (domain.includes("google")) {
- return "google-color";
- }
- if (domain.includes("datadog")) {
- return "datadog-color";
- }
- if (domain.includes("github")) {
- return "github-color";
- }
- if (domain.includes("codepen")) {
- return "codepen-color";
- }
- if (domain.includes("slack")) {
- return "slack-color";
- }
- if (domain.includes("loom")) {
- return "loom-color";
- }
- }
- }
- }
- return "globe";
-});
-
-export default getLinkIconHelper;
-
-declare module "@glint/environment-ember-loose/registry" {
- export default interface Registry {
- "get-link-icon": typeof getLinkIconHelper;
- }
-}
diff --git a/web/tests/integration/components/favicon-test.ts b/web/tests/integration/components/favicon-test.ts
new file mode 100644
index 000000000..afab56cc4
--- /dev/null
+++ b/web/tests/integration/components/favicon-test.ts
@@ -0,0 +1,39 @@
+import { module, test } from "qunit";
+import { setupRenderingTest } from "ember-qunit";
+import { find, render, waitFor } from "@ember/test-helpers";
+import { hbs } from "ember-cli-htmlbars";
+
+const FALLBACK_ICON_SELECTOR = "[data-test-fallback-favicon]";
+const FAVICON_SELECTOR = "[data-test-favicon]";
+
+module("Integration | Component | favicon", function (hooks) {
+ setupRenderingTest(hooks);
+
+ test("it appends the passed-in URL to the google favicon path", async function (assert) {
+ await render(hbs`
+
+ `);
+
+ assert
+ .dom(FAVICON_SELECTOR)
+ .hasAttribute(
+ "src",
+ "https://www.google.com/s2/favicons?sz=64&domain=https://hashicorp.com"
+ );
+ });
+
+ test("it shows a fallback icon when the favicon URL is invalid", async function (assert) {
+ await render(hbs`
+
+ `);
+
+ assert.dom(FALLBACK_ICON_SELECTOR).doesNotExist();
+
+ find(FAVICON_SELECTOR)?.dispatchEvent(new Event("error"));
+
+ await waitFor(FALLBACK_ICON_SELECTOR);
+
+ assert.dom(FALLBACK_ICON_SELECTOR).exists();
+ assert.dom(FAVICON_SELECTOR).doesNotExist();
+ });
+});