From c7d8b48d7250edbf5c83311a93da33ace212b0fe Mon Sep 17 00:00:00 2001
From: "Mahatthana (Kelvin) Nomsawadi" <mahatthana.n@gmail.com>
Date: Wed, 20 Jul 2022 14:49:30 +0700
Subject: [PATCH] Fix rendering segment and metric revision history (#24097)

* Fix rendering segment and metric revision history

* Address Alexander's feedback

* Remove unused styled-system props
---
 .../components/UserAvatar/UserAvatar.jsx      | 54 --------------
 .../UserAvatar/UserAvatar.styled.tsx          | 22 ++++++
 .../components/UserAvatar/UserAvatar.tsx      | 64 +++++++++++++++++
 .../UserAvatar/UserAvatar.unit.spec.tsx       | 71 +++++++++++++++++++
 4 files changed, 157 insertions(+), 54 deletions(-)
 delete mode 100644 frontend/src/metabase/components/UserAvatar/UserAvatar.jsx
 create mode 100644 frontend/src/metabase/components/UserAvatar/UserAvatar.styled.tsx
 create mode 100644 frontend/src/metabase/components/UserAvatar/UserAvatar.tsx
 create mode 100644 frontend/src/metabase/components/UserAvatar/UserAvatar.unit.spec.tsx

diff --git a/frontend/src/metabase/components/UserAvatar/UserAvatar.jsx b/frontend/src/metabase/components/UserAvatar/UserAvatar.jsx
deleted file mode 100644
index e7a9dcf6a74..00000000000
--- a/frontend/src/metabase/components/UserAvatar/UserAvatar.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-/* eslint-disable react/prop-types */
-import React from "react";
-import styled from "@emotion/styled";
-import { color, height, width } from "styled-system";
-
-import { color as brandColor } from "metabase/lib/colors";
-
-const Avatar = styled.div`
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 999px;
-  font-weight: 900;
-  line-height: 1;
-  ${height};
-  ${width};
-  ${color};
-  background-color: ${({ bg = brandColor("brand") }) => bg};
-`;
-
-Avatar.defaultProps = {
-  color: "white",
-  height: ["3em"],
-  width: ["3em"],
-};
-
-function initial(name) {
-  return typeof name === "string" ? name.charAt(0).toUpperCase() : "";
-}
-
-function userInitials(user) {
-  if (user) {
-    return nameInitials(user) || emailInitials(user);
-  }
-
-  return null;
-}
-
-function nameInitials(user) {
-  return initial(user.first_name) + initial(user.last_name);
-}
-
-function emailInitials(user) {
-  const emailUsername = user.email.split("@")[0];
-  return emailUsername.slice(0, 2).toUpperCase();
-}
-
-const UserAvatar = ({ user, ...props }) => (
-  <Avatar {...props}>{userInitials(user) || "?"}</Avatar>
-);
-
-UserAvatar.displayName = "UserAvatar";
-
-export default UserAvatar;
diff --git a/frontend/src/metabase/components/UserAvatar/UserAvatar.styled.tsx b/frontend/src/metabase/components/UserAvatar/UserAvatar.styled.tsx
new file mode 100644
index 00000000000..ef9ecf9308e
--- /dev/null
+++ b/frontend/src/metabase/components/UserAvatar/UserAvatar.styled.tsx
@@ -0,0 +1,22 @@
+import styled from "@emotion/styled";
+import { color as brandColor, color } from "metabase/lib/colors";
+
+export interface AvatarProps {
+  color?: string;
+  height?: string[];
+  width?: string[];
+  bg?: string;
+}
+
+export const Avatar = styled.div<AvatarProps>`
+  color: ${color("white")};
+  width: 3em;
+  height: 3em;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 999px;
+  font-weight: 900;
+  line-height: 1;
+  background-color: ${({ bg = brandColor("brand") }) => bg};
+`;
diff --git a/frontend/src/metabase/components/UserAvatar/UserAvatar.tsx b/frontend/src/metabase/components/UserAvatar/UserAvatar.tsx
new file mode 100644
index 00000000000..a88200adbdc
--- /dev/null
+++ b/frontend/src/metabase/components/UserAvatar/UserAvatar.tsx
@@ -0,0 +1,64 @@
+/* eslint-disable react/prop-types */
+import React from "react";
+
+import MetabaseUtils from "metabase/lib/utils";
+import { Avatar, AvatarProps } from "./UserAvatar.styled";
+
+interface UserAvatarProps extends AvatarProps {
+  user: User;
+}
+
+interface GroupProps {
+  user: Group;
+}
+
+interface User {
+  first_name: string | null;
+  last_name: string | null;
+  common_name: string;
+  email?: string;
+}
+
+interface Group {
+  first_name: string;
+}
+
+export default function UserAvatar({
+  user,
+  ...props
+}: UserAvatarProps | GroupProps) {
+  return <Avatar {...props}>{userInitials(user) || "?"}</Avatar>;
+}
+
+function initial(name?: string | null) {
+  return name ? name.charAt(0).toUpperCase() : "";
+}
+
+function userInitials(user: User | Group) {
+  if (user) {
+    return nameInitials(user) || emailInitials(user as User);
+  }
+
+  return null;
+}
+
+function nameInitials(user: User | Group) {
+  if ("common_name" in user) {
+    return initial(user.first_name) + initial(user.last_name);
+  }
+
+  // render group
+  return initial(user.first_name);
+}
+
+function emailInitials(user: User) {
+  const email = [user.email, user.common_name].find(maybeEmail =>
+    MetabaseUtils.isEmail(maybeEmail),
+  );
+  if (email) {
+    const emailUsername = email.split("@")[0];
+    return emailUsername.slice(0, 2).toUpperCase();
+  }
+
+  return null;
+}
diff --git a/frontend/src/metabase/components/UserAvatar/UserAvatar.unit.spec.tsx b/frontend/src/metabase/components/UserAvatar/UserAvatar.unit.spec.tsx
new file mode 100644
index 00000000000..479fadbc249
--- /dev/null
+++ b/frontend/src/metabase/components/UserAvatar/UserAvatar.unit.spec.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+import { render } from "@testing-library/react";
+import UserAvatar from "./UserAvatar";
+
+describe("UserAvatar", () => {
+  describe("Users", () => {
+    test("render user with name", async () => {
+      const revisionUser = {
+        first_name: "Testy",
+        last_name: "Tableton",
+        common_name: "Testy Tableton",
+        email: "user@metabase.test",
+      };
+
+      const { findByText } = render(<UserAvatar user={revisionUser} />);
+
+      expect(await findByText("TT")).toBeInTheDocument();
+    });
+
+    test("render user without name", async () => {
+      const revisionUser = {
+        first_name: null,
+        last_name: null,
+        common_name: "user@metabase.test",
+        email: "user@metabase.test",
+      };
+
+      const { findByText } = render(<UserAvatar user={revisionUser} />);
+
+      expect(await findByText("US")).toBeInTheDocument();
+    });
+  });
+
+  describe("Revision history", () => {
+    test("render user with name", async () => {
+      const revisionUser = {
+        first_name: "Testy",
+        last_name: "Tableton",
+        common_name: "Testy Tableton",
+      };
+
+      const { findByText } = render(<UserAvatar user={revisionUser} />);
+
+      expect(await findByText("TT")).toBeInTheDocument();
+    });
+
+    test("render user without name", async () => {
+      const revisionUser = {
+        first_name: null,
+        last_name: null,
+        common_name: "user@metabase.test",
+      };
+
+      const { findByText } = render(<UserAvatar user={revisionUser} />);
+
+      expect(await findByText("US")).toBeInTheDocument();
+    });
+  });
+
+  describe("Admin > Groups", () => {
+    test("render group name", async () => {
+      const revisionUser = {
+        first_name: "Admin",
+      };
+
+      const { findByText } = render(<UserAvatar user={revisionUser} />);
+
+      expect(await findByText("A")).toBeInTheDocument();
+    });
+  });
+});
-- 
GitLab