Add exception to UI software name-to-icon mapping for ZoomInfo (#19509)

This commit is contained in:
Sarah Gillespie 2024-06-12 13:49:18 -05:00 committed by GitHub
parent dcd551f671
commit e5316331f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 100 additions and 50 deletions

View file

@ -0,0 +1 @@
- Fixed UI bug where Zoom icon was displayed for ZoomInfo.

View file

@ -1,10 +1,5 @@
import React, { ComponentType, SVGProps } from "react";
import {
SOFTWARE_NAME_TO_ICON_MAP,
SOFTWARE_SOURCE_TO_ICON_MAP,
SOFTWARE_ICON_SIZES,
SoftwareIconSizes,
} from "../";
import React from "react";
import getMatchedSoftwareIcon from "../";
const baseClass = "software-icon";
@ -14,43 +9,20 @@ interface ISoftwareIconProps {
size?: SoftwareIconSizes;
}
const matchInMap = (
map: Record<string, ComponentType<SVGProps<SVGSVGElement>>>,
potentialKey?: string
) => {
if (!potentialKey) {
return null;
}
const SOFTWARE_ICON_SIZES: Record<string, string> = {
medium: "24",
meduim_large: "64", // TODO: rename this to large and update large to xlarge
large: "96",
} as const;
const sanitizedKey = potentialKey.trim().toLowerCase();
const match = Object.entries(map).find(([namePrefix, icon]) => {
if (sanitizedKey.startsWith(namePrefix)) {
return icon;
}
return null;
});
return match ? match[1] : null;
};
type SoftwareIconSizes = keyof typeof SOFTWARE_ICON_SIZES;
const SoftwareIcon = ({
name,
source,
name = "",
source = "",
size = "medium",
}: ISoftwareIconProps) => {
// try to find a match for name
let MatchedIcon = matchInMap(SOFTWARE_NAME_TO_ICON_MAP, name);
// otherwise, try to find a match for source
if (!MatchedIcon) {
MatchedIcon = matchInMap(SOFTWARE_SOURCE_TO_ICON_MAP, source);
}
// default to 'package'
if (!MatchedIcon) {
MatchedIcon = SOFTWARE_SOURCE_TO_ICON_MAP.package;
}
const MatchedIcon = getMatchedSoftwareIcon({ name, source });
return (
<MatchedIcon
width={SOFTWARE_ICON_SIZES[size]}

View file

@ -1,5 +1,6 @@
import { HOST_LINUX_PLATFORMS } from "interfaces/platform";
import Linux from "components/icons/Linux";
import { ISoftware } from "interfaces/software";
import AcrobatReader from "./AcrobatReader";
import ChromeApp from "./ChromeApp";
import Excel from "./Excel";
@ -29,9 +30,8 @@ const LINUX_OS_NAME_TO_ICON_MAP = HOST_LINUX_PLATFORMS.reduce(
// SOFTWARE_NAME_TO_ICON_MAP list "special" applications that have a defined
// icon for them, keys refer to application names, and are intended to be fuzzy
// matched in the application logic.
export const SOFTWARE_NAME_TO_ICON_MAP = {
const SOFTWARE_NAME_TO_ICON_MAP = {
"adobe acrobat reader": AcrobatReader,
"google chrome": ChromeApp,
"microsoft excel": Excel,
falcon: Falcon,
firefox: Firefox,
@ -41,7 +41,6 @@ export const SOFTWARE_NAME_TO_ICON_MAP = {
"microsoft teams": Teams,
"visual studio code": VisualStudioCode,
"microsoft word": Word,
zoom: Zoom,
darwin: MacOS,
windows: WindowsOS,
chrome: ChromeOS,
@ -50,7 +49,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = {
// SOFTWARE_SOURCE_TO_ICON_MAP maps different software sources to a defined
// icon.
export const SOFTWARE_SOURCE_TO_ICON_MAP = {
const SOFTWARE_SOURCE_TO_ICON_MAP = {
package: Package,
apt_sources: Package,
deb_packages: Package,
@ -71,10 +70,88 @@ export const SOFTWARE_SOURCE_TO_ICON_MAP = {
vscode_extensions: Extension,
} as const;
export const SOFTWARE_ICON_SIZES: Record<string, string> = {
medium: "24",
meduim_large: "64", // TODO: rename this to large and update large to xlarge
large: "96",
} as const;
/**
* This attempts to loosely match the provided string to a key in a provided dictionary, returning the key if the
* provided string starts with the key or undefined otherwise.
*/
const matchLoosePrefixToKey = <T extends Record<string, unknown>>(
dict: T,
s: string
) => {
s = s.trim().toLowerCase();
if (!s) {
return undefined;
}
const match = Object.keys(dict).find((k) =>
s.startsWith(k.trim().toLowerCase())
);
export type SoftwareIconSizes = keyof typeof SOFTWARE_ICON_SIZES;
return match ? (match as keyof T) : undefined;
};
/**
* This strictly matches the provided name and source to a software icon, returning the icon if a match is found or
* null otherwise. It is intended to be used for special cases where a strict match is required
* (e.g. Zoom). The caller should handle null cases by falling back to loose matching on name prefixes.
*/
const matchStrictNameSourceToIcon = ({
name = "",
source = "",
}: Pick<ISoftware, "name" | "source">) => {
name = name.trim().toLowerCase();
source = source.trim().toLowerCase();
switch (true) {
case name === "zoom.us.app" && source === "apps":
return Zoom;
case name === "zoom":
return Zoom;
case name === "google chrome":
return ChromeApp;
default:
return null;
}
};
/**
* This returns the icon component for a given software name and source. If a strict match is found,
* it will be returned, otherwise it will fall back to loose matching on name and source prefixes.
* If no match is found, the default package icon will be returned.
*/
const getMatchedSoftwareIcon = ({
name = "",
source = "",
}: Pick<ISoftware, "name" | "source">) => {
// first, try strict matching on name and source
let Icon = matchStrictNameSourceToIcon({
name,
source,
});
// if no match, try loose matching on name prefixes
if (!Icon) {
const matchedName = matchLoosePrefixToKey(SOFTWARE_NAME_TO_ICON_MAP, name);
if (matchedName) {
Icon = SOFTWARE_NAME_TO_ICON_MAP[matchedName];
}
}
// if still no match, try loose matching on source prefixes
if (!Icon) {
const matchedSource = matchLoosePrefixToKey(
SOFTWARE_SOURCE_TO_ICON_MAP,
source
);
if (matchedSource) {
Icon = SOFTWARE_SOURCE_TO_ICON_MAP[matchedSource];
}
}
// if still no match, default to 'package'
if (!Icon) {
Icon = SOFTWARE_SOURCE_TO_ICON_MAP.package;
}
return Icon;
};
export default getMatchedSoftwareIcon;