Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🪄 Adds type safety for CareIcon names + ✨ reduced index.css size by 95% + Refactor string interpolated tw classes #6034

Merged
merged 8 commits into from
Aug 9, 2023
109 changes: 39 additions & 70 deletions src/CAREUI/display/Chip.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,53 @@
import { MouseEventHandler, useEffect, useState } from "react";
import CareIcon, { IconName } from "../icons/CareIcon";
import { ButtonVariant } from "../../Components/Common/components/ButtonV2";
import { classNames } from "../../Utils/utils";

type iconType = "uil" | "fa";
interface Props {
size?: "small" | "medium" | "large";
hideBorder?: boolean;
color?: string;
startIcon?: string | JSX.Element;
startIconType?: iconType;
endIcon?: string | JSX.Element;
endIconType?: iconType;
variant?: ButtonVariant | "custom";
startIcon?: IconName;
endIcon?: IconName;
text: string;
onStartIconClick?: MouseEventHandler<HTMLElement>;
onEndIconClick?: MouseEventHandler<HTMLElement>;
tooltip?: string;
className?: string;
}

export default function Chip(props: Props) {
const [chipStyle, setChipStyle] = useState("");

useEffect(() => {
switch (props.size) {
case "small":
setChipStyle("px-2 py-1 rounded text-xs");
break;
export default function Chip({
size = "medium",
hideBorder = false,
variant = "primary",
...props
}: Props) {
return (
<span
className={classNames(
"inline-flex items-center gap-2 font-medium leading-4",

case "large":
setChipStyle("px-4 py-3 rounded-lg text-sm");
break;
{
small: "rounded px-2 py-1 text-xs",
medium: "rounded-lg px-3 py-2 text-xs",
large: "rounded-lg px-4 py-3 text-sm",
}[size],

default:
setChipStyle("px-3 py-2 rounded-lg text-xs");
break;
}
}, [props.size]);
!hideBorder && "border",
{
primary: "border-primary-300 bg-primary-100 text-primary-900",
secondary: "border-secondary-300 bg-secondary-100 text-secondary-900",
success: "border-success-300 bg-success-100 text-success-900",
danger: "border-danger-300 bg-danger-100 text-danger-900",
warning: "border-warning-300 bg-warning-100 text-warning-900",
alert: "border-alert-300 bg-alert-100 text-alert-900",
custom: "",
}[variant],

return (
<span
className={`inline-flex ${
!props.hideBorder && "border-ashsec-300 border"
} items-center gap-2 ${chipStyle} bg-${props.color}-100 text-${
props.color
}-900 font-medium leading-4 text-gray-800`}
title={props.text}
>
{props.startIcon && (
<button
className={`text-${props.color}-500`}
onClick={props.onStartIconClick}
>
{typeof props.startIcon === "string" ? (
<i
className={
(props.startIconType && props.startIconType === "uil"
? "uil uil-"
: "fas fa-") + props.startIcon
}
/>
) : (
props.startIcon
)}
</button>
)}
{props.text}
{props.endIcon && (
<button
className={`text-${props.color}-500`}
onClick={props.onEndIconClick}
>
{typeof props.endIcon === "string" ? (
<i
className={
(props.endIconType && props.endIconType === "uil"
? "uil uil-"
: "fas fa-") + props.endIcon
}
/>
) : (
props.endIcon
)}
</button>
props.className
)}
title={props.tooltip}
>
{props.startIcon && <CareIcon icon={props.startIcon} />}
<span>{props.text}</span>
{props.endIcon && <CareIcon icon={props.endIcon} />}
</span>
);
}
39 changes: 16 additions & 23 deletions src/CAREUI/display/Count.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,32 @@
import CareIcon from "../icons/CareIcon";
import { classNames } from "../../Utils/utils";
import CareIcon, { IconName } from "../icons/CareIcon";

export default function CountBlock(props: {
interface Props {
count: number;
text: string;
loading: boolean;
icon: string;
color?: string;
containerClass?: string;
}) {
const {
count,
text,
loading,
icon,
color = "primary",
containerClass,
} = props;
icon: IconName;
className?: string;
}

export default function CountBlock(props: Props) {
return (
<div className={"flex-1 rounded-lg bg-white p-4 shadow " + containerClass}>
<dl className="">
<div
className={`bg-${color}-100 flex h-10 w-10 items-center justify-center rounded-lg text-xl`}
>
<CareIcon className={`care-l-${icon} text-${color}-600`} />
<div
className={classNames("rounded-lg bg-white p-4 shadow", props.className)}
>
<dl>
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-100 text-xl">
<CareIcon icon={props.icon} className="text-primary-600" />
</div>
<div>
<dt className="my-2 truncate text-sm font-semibold text-gray-700">
{text}
{props.text}
</dt>
{loading ? (
{props.loading ? (
<dd className="h-10 w-full max-w-[100px] animate-pulse rounded-lg bg-gray-300" />
) : (
<dd id="count" className="text-5xl font-black leading-9">
{count}
{props.count}
</dd>
)}
</div>
Expand Down
17 changes: 13 additions & 4 deletions src/CAREUI/icons/CareIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { transformIcons } from "./icon";
import { useEffect } from "react";

import iconData from "./UniconPaths.json";

export type IconName = keyof typeof iconData;

export interface CareIconProps {
icon?: IconName;
className?: string | undefined;
onClick?: React.MouseEventHandler<HTMLSpanElement> | undefined;
}
Expand All @@ -14,11 +19,15 @@ export interface CareIconProps {
*
* @see [icon library](https://iconscout.com/unicons/)
*/
export default function CareIcon({ className, onClick }: CareIconProps) {
useEffect(() => transformIcons(), [className]);
export default function CareIcon({ icon, className, onClick }: CareIconProps) {
const effectiveClassName = icon
? `care-${icon} ${className ?? ""}`
: className;

useEffect(() => transformIcons(), [effectiveClassName]);
return (
<span onClick={onClick} key={className}>
<i className={`care ${className}`} />
<span onClick={onClick} key={effectiveClassName}>
<i className={`care ${effectiveClassName}`} />
</span>
);
}
19 changes: 12 additions & 7 deletions src/Common/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PatientCategory } from "../Components/Facility/models";
import { SortOption } from "../Components/Common/SortDropdown";
import { parsePhoneNumberFromString } from "libphonenumber-js";
import { dateQueryString } from "../Utils/utils";
import { IconName } from "../CAREUI/icons/CareIcon";

export const RESULTS_PER_PAGE_LIMIT = 14;
export const PAGINATION_LIMIT = 36;
Expand Down Expand Up @@ -828,36 +829,40 @@ export const getCameraPTZ: (precision: number) => CameraPTZ[] = (precision) => [
];

// in future, if you find Unicon equivalents of all these icons, please replace them. Only use the same iconset throughout.
export const FACILITY_FEATURE_TYPES = [
export const FACILITY_FEATURE_TYPES: {
id: number;
name: string;
icon: IconName;
}[] = [
{
id: 1,
name: "CT Scan",
icon: "compact-disc",
icon: "l-compact-disc",
},
{
id: 2,
name: "Maternity Care",
icon: "person-breastfeeding",
icon: "l-baby-carriage",
},
{
id: 3,
name: "X-Ray",
icon: "x-ray",
icon: "l-clipboard-alt",
},
{
id: 4,
name: "Neonatal Care",
icon: "baby-carriage",
icon: "l-baby-carriage",
},
{
id: 5,
name: "Operation Theater",
icon: "syringe",
icon: "l-syringe",
},
{
id: 6,
name: "Blood Bank",
icon: "droplet",
icon: "l-medical-drip",
},
];

Expand Down
14 changes: 9 additions & 5 deletions src/Components/Assets/AssetManage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,18 +274,22 @@ const AssetManage = (props: AssetManageProps) => {
</div>
<div className="flex flex-wrap gap-2">
{asset?.status === "ACTIVE" ? (
<Chip color="green" text="Active" startIcon="check" />
<Chip text="Active" startIcon="l-check" />
) : (
<Chip
color="yellow"
variant="warning"
text="Transfer in progress"
startIcon="exclamation"
startIcon="l-exclamation"
/>
)}
{asset?.is_working ? (
<Chip color="green" text="Working" startIcon="check" />
<Chip text="Working" startIcon="l-check" />
) : (
<Chip color="red" text="Not Working" startIcon="times" />
<Chip
variant="danger"
text="Not Working"
startIcon="l-times"
/>
)}
</div>
</div>
Expand Down
7 changes: 4 additions & 3 deletions src/Components/Assets/AssetsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,9 @@ const AssetsList = () => {

<div className="mt-2 flex flex-wrap gap-2">
{asset.is_working ? (
<Chip color="green" startIcon="cog" text="Working" />
<Chip startIcon="l-cog" text="Working" />
) : (
<Chip color="red" startIcon="cog" text="Not Working" />
<Chip variant="danger" startIcon="l-cog" text="Not Working" />
)}
</div>
</div>
Expand Down Expand Up @@ -350,7 +350,8 @@ const AssetsList = () => {
text="Total Assets"
count={totalCount}
loading={isLoading}
icon={"monitor-heart-rate"}
icon="l-monitor-heart-rate"
className="flex-1"
/>
<div className="flex-1">
<SearchInput
Expand Down
9 changes: 5 additions & 4 deletions src/Components/Common/PatientCategoryBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { PATIENT_CATEGORIES } from "../../Common/constants";
import { PatientCategory } from "../Facility/models";

const PatientCategoryBadge = (props: { category?: PatientCategory }) => {
const categoryClass = props.category
? PATIENT_CATEGORIES.find((c) => c.text === props.category)?.twClass
const PatientCategoryBadge = ({ category }: { category?: PatientCategory }) => {
const categoryClass = category
? PATIENT_CATEGORIES.find((c) => c.text === category)?.twClass
: "patient-unknown";

return (
<span
className={`rounded-full px-2 py-0.5 text-sm ${categoryClass} font-medium`}
>
{props.category}
{category}
</span>
);
};
Expand Down
6 changes: 1 addition & 5 deletions src/Components/Common/components/ButtonV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ export type ButtonProps = RawButtonProps &
* - `"alert"` is ideal for actions that require alert.
*/
variant?: ButtonVariant;
/** Specify text alignment. Defaults to `center` */
align?: "start" | "center" | "end" | "between" | "around" | "evenly";
/** If set, gives an elevated button with hover effects. */
shadow?: boolean | undefined;
/** If set, removes the background to give a simple text button. */
Expand Down Expand Up @@ -89,7 +87,6 @@ const ButtonV2 = ({
authorizeFor,
size = "default",
variant = "primary",
align = "center",
circle,
shadow,
ghost,
Expand All @@ -105,9 +102,8 @@ const ButtonV2 = ({
}: ButtonProps) => {
const className = classNames(
props.className,
"inline-flex h-min cursor-pointer items-center gap-2 whitespace-pre font-medium outline-offset-1 transition-all duration-200 ease-in-out disabled:cursor-not-allowed disabled:bg-gray-200 disabled:text-gray-500",
"inline-flex h-min cursor-pointer items-center justify-center gap-2 whitespace-pre font-medium outline-offset-1 transition-all duration-200 ease-in-out disabled:cursor-not-allowed disabled:bg-gray-200 disabled:text-gray-500",
`button-size-${size}`,
`justify-${align}`,
`button-shape-${circle ? "circle" : "square"}`,
ghost ? `button-${variant}-ghost` : `button-${variant}-default`,
border && `button-${variant}-border`,
Expand Down
16 changes: 15 additions & 1 deletion src/Components/Common/components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,21 @@ export function DropdownItem({
className
)}
>
<i className={`text-${variant}-500 text-lg`}>{icon}</i>
<i
className={classNames(
"text-lg",
{
primary: "text-primary-500",
secondary: "text-secondary-500",
success: "text-success-500",
warning: "text-warning-500",
danger: "text-danger-500",
alert: "text-alert-500",
}[variant]
)}
>
{icon}
</i>
{children}
</div>
</Menu.Item>
Expand Down
Loading