From 848ae2585e96cb5fca52cf61ac770c8dbd9c8c29 Mon Sep 17 00:00:00 2001 From: Alissa Cheng <cheng@astron.nl> Date: Wed, 27 Mar 2024 08:43:22 +0000 Subject: [PATCH] feat!: make icons great again BREAKING CHANGE: changed icon names to PascalCase --- .eslintrc.cjs | 1 + README.md | 11 +++++-- src/assets/icons/index.ts | 59 +++++++++++----------------------- src/components/Alert.tsx | 10 +++--- src/components/Breadcrumb.tsx | 2 +- src/components/Checkbox.tsx | 2 +- src/components/DateInput.tsx | 6 ++-- src/components/Dropdown.tsx | 2 +- src/components/Icon.tsx | 51 +++-------------------------- src/components/Menu.tsx | 4 +-- src/components/Modal.tsx | 2 +- src/stories/Button.stories.tsx | 2 +- src/stories/Icon.stories.ts | 5 +-- src/stories/Menu.stories.tsx | 6 ++-- 14 files changed, 53 insertions(+), 110 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 738aab6..5a0fa6c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -42,6 +42,7 @@ module.exports = { "warn", { allowConstantExport: true }, ], + "import/namespace": ["error", { allowComputed: true }], }, settings: { "import/parsers": { diff --git a/README.md b/README.md index 9466338..2f63c71 100644 --- a/README.md +++ b/README.md @@ -68,11 +68,16 @@ This is the current workflow: Download or copy the SVG code from Figma. Save it into `src/assets/icons/your-file-name.svg`. In the file, change the stroke color to `currentColor`. This is to enable dynamic stroke colors. -Add an entry to the `intex.ts` where you import the svg file, since this allows the bundler to pick-up the file correctly. -Add `"your-file-name"` to `iconNameOptions` array and the `iconMap` dict of `Icon.tsx`. Then, you can use your icon like this: +Add an _export_ entry to the `src/assets/icons/index.ts`: ```tsx -<Icon name="your-file-name" color="secondary" /> +export { default as YourFileName } from "./your-file-name.svg"; +``` + +Then, you can use your icon like this (note the casing): + +```tsx +<Icon name="YourFileName" color="secondary" /> ``` ### Classnames diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 7151d96..34be31e 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -1,41 +1,20 @@ -import AttentionCircle from "./attention-circle.svg"; -import CheckmarkCircle from "./checkmark-circle.svg"; -import Checkmark from "./checkmark.svg"; -import CrossCircle from "./cross-circle.svg"; -import Cross from "./cross.svg"; -import Datepicker from "./datepicker.svg"; -import Download from "./download.svg"; -import Dropdown from "./dropdown.svg"; -import Edit from "./edit.svg"; -import Expand from "./expand.svg"; -import Eye from "./eye.svg"; -import InfoCircle from "./info-circle.svg"; -import Layer from "./layer.svg"; -import Magnifier from "./magnifier.svg"; -import Minus from "./minus.svg"; -import Next from "./next.svg"; -import Plus from "./plus.svg"; -import Previous from "./previous.svg"; -import Up from "./up.svg"; +export { default as AttentionCircle } from "./attention-circle.svg"; -export default { - AttentionCircle, - CheckmarkCircle, - Checkmark, - CrossCircle, - Cross, - Datepicker, - Download, - Dropdown, - Edit, - Expand, - Eye, - InfoCircle, - Layer, - Magnifier, - Minus, - Next, - Plus, - Previous, - Up, -}; +export { default as CheckmarkCircle } from "./checkmark-circle.svg"; +export { default as Checkmark } from "./checkmark.svg"; +export { default as CrossCircle } from "./cross-circle.svg"; +export { default as Cross } from "./cross.svg"; +export { default as Datepicker } from "./datepicker.svg"; +export { default as Download } from "./download.svg"; +export { default as Dropdown } from "./dropdown.svg"; +export { default as Edit } from "./edit.svg"; +export { default as Expand } from "./expand.svg"; +export { default as Eye } from "./eye.svg"; +export { default as InfoCircle } from "./info-circle.svg"; +export { default as Layer } from "./layer.svg"; +export { default as Magnifier } from "./magnifier.svg"; +export { default as Minus } from "./minus.svg"; +export { default as Next } from "./next.svg"; +export { default as Plus } from "./plus.svg"; +export { default as Previous } from "./previous.svg"; +export { default as Up } from "./up.svg"; diff --git a/src/components/Alert.tsx b/src/components/Alert.tsx index b5a3bc1..b4a339d 100644 --- a/src/components/Alert.tsx +++ b/src/components/Alert.tsx @@ -20,10 +20,10 @@ type AlertProps = { */ const Alert = ({ title, message, alertType = "primary" }: AlertProps) => { const icon = { - primary: "info-circle", - warning: "attention-circle", - positive: "checkmark-circle", - negative: "cross-circle", + primary: "InfoCircle", + warning: "AttentionCircle", + positive: "CheckmarkCircle", + negative: "CrossCircle", }; const baseClass = "flex flex-row items-start py-[13px] px-[15px] rounded-[8px] w-min border-l-8 border-y-0 border-r-0 bg-baseWhite dark:bg-mediumGrey shadow-2xl"; @@ -40,7 +40,7 @@ const Alert = ({ title, message, alertType = "primary" }: AlertProps) => { <Typography text={message} variant="paragraph" /> </div> {/* if used more often than below and in Modal, consider making it a CloseIcon */} - <Icon name="cross" color="body" customClass="cursor-pointer" /> + <Icon name="Cross" color="body" customClass="cursor-pointer" /> </div> ); }; diff --git a/src/components/Breadcrumb.tsx b/src/components/Breadcrumb.tsx index 6528138..36e243a 100644 --- a/src/components/Breadcrumb.tsx +++ b/src/components/Breadcrumb.tsx @@ -17,7 +17,7 @@ const Breadcrumb = ({ items }: BreadcrumbProps) => { itemsBeforeCollapse={1} itemsAfterCollapse={2} separator={ - <Icon name="next" color="body" customClass="w-[7px] h-[7px]" /> + <Icon name="Next" color="body" customClass="w-[7px] h-[7px]" /> } itemClasses={{ item: clsx( diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx index 78f3505..b687b53 100644 --- a/src/components/Checkbox.tsx +++ b/src/components/Checkbox.tsx @@ -30,7 +30,7 @@ const Checkbox = ({ isDisabled={isDisabled} isSelected={isSelected} onValueChange={setIsSelected} - icon={isSelected ? <Icon name="checkmark" color="body" /> : <></>} + icon={isSelected ? <Icon name="Checkmark" color="body" /> : <></>} // This seems to be the only way to enable customization of background and border color // that's why the color "default" is set to "transparent" in tailwind.config.js color="default" diff --git a/src/components/DateInput.tsx b/src/components/DateInput.tsx index 024a44d..8c8acb3 100644 --- a/src/components/DateInput.tsx +++ b/src/components/DateInput.tsx @@ -77,7 +77,7 @@ const DateInput = ({ labelCustomColor={labelCustomColor} endContent={ <Icon - name="datepicker" + name="Datepicker" // Note: textColor("body") has a different grey, hence the below is used customClass="text-grey dark:text-baseWhite" /> @@ -106,7 +106,7 @@ const DateInput = ({ }: PaginationProps) => ( <div className="flex flex-row justify-between items-center px-2 h-[30px] border-b-1 border-grey"> <button onClick={onClickPrev} className="w-[16px] h-[16px]"> - <Icon name="previous" color="body" /> + <Icon name="Previous" color="body" /> </button> <Typography text={text} @@ -115,7 +115,7 @@ const DateInput = ({ customClass={textColor("body")} /> <button onClick={onClickNext} className="w-[16px] h-[16px]"> - <Icon name="next" color="body" /> + <Icon name="Next" color="body" /> </button> </div> ); diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx index b1362f0..df16f8e 100644 --- a/src/components/Dropdown.tsx +++ b/src/components/Dropdown.tsx @@ -58,7 +58,7 @@ const Dropdown = ({ variant: "bordered", isRequired: isRequired, }} // use the same props as BaseInput - selectorIcon={<Icon name="dropdown" />} + selectorIcon={<Icon name="Dropdown" />} isClearable={false} // so the "cross" button does not show classNames={{ selectorButton: clsx( diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index f46879e..a6f71b3 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -1,54 +1,11 @@ import { ReactSVG } from "react-svg"; import { clsx } from "clsx"; -import Icons from "src/assets/icons"; +import * as Icons from "src/assets/icons"; import { TextColorType } from "./utils/colors.ts"; import { textColor } from "./utils/classes.ts"; -export const iconNameOptions = [ - "attention-circle", - "checkmark", - "checkmark-circle", - "cross", - "cross-circle", - "datepicker", - "download", - "dropdown", - "edit", - "expand", - "eye", - "info-circle", - "layer", - "magnifier", - "minus", - "next", - "plus", - "previous", - "up", -] as const; // for storybook dropdown - -export type IconName = (typeof iconNameOptions)[number]; // for storybook dropdown - -const iconMap = { - "attention-circle": Icons.AttentionCircle, - checkmark: Icons.Checkmark, - "checkmark-circle": Icons.CheckmarkCircle, - cross: Icons.Cross, - "cross-circle": Icons.CrossCircle, - datepicker: Icons.Datepicker, - download: Icons.Download, - dropdown: Icons.Dropdown, - expand: Icons.Expand, - edit: Icons.Edit, - eye: Icons.Eye, - "info-circle": Icons.InfoCircle, - layer: Icons.Layer, - magnifier: Icons.Magnifier, - minus: Icons.Minus, - next: Icons.Next, - plus: Icons.Plus, - previous: Icons.Previous, - up: Icons.Up, -}; +export type IconName = keyof typeof Icons; +export const iconNameOptions = Object.keys(Icons) as IconName[]; // for storybook dropdown type IconProps = { name: IconName; @@ -64,7 +21,7 @@ type IconProps = { * @param customClass: string | undefined */ const Icon = ({ name, color = null, customClass = "" }: IconProps) => { - const src = iconMap[name]; + const src = Icons[name]; if (!color && !customClass) { return <ReactSVG src={src} style={{ color: "currentColor" }} />; diff --git a/src/components/Menu.tsx b/src/components/Menu.tsx index 30aae91..efa513e 100644 --- a/src/components/Menu.tsx +++ b/src/components/Menu.tsx @@ -90,7 +90,7 @@ export const ActionMenu = ({ label, items, onAction }: MenuProps) => { onAction={onAction} menuTrigger={ <NextButton - startContent={<Icon name="dropdown" />} + startContent={<Icon name="Dropdown" />} className={buttonBaseClass} // same as Button component (src/components/Button.tsx) type="button" color="primary" @@ -133,7 +133,7 @@ export const ProfileMenu = ({ label, items, onAction }: MenuProps) => { /> } /> - <Icon name="dropdown" color="primary" /> + <Icon name="Dropdown" color="primary" /> </div> } /> diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index d28cc3a..ad64622 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -36,7 +36,7 @@ const Modal = ({ }: ModalProps) => { const closeButton = ( <button> - <Icon name="cross" /> + <Icon name="Cross" /> </button> ); return ( diff --git a/src/stories/Button.stories.tsx b/src/stories/Button.stories.tsx index dff794e..276f500 100644 --- a/src/stories/Button.stories.tsx +++ b/src/stories/Button.stories.tsx @@ -29,7 +29,7 @@ export const IconButton: StoryObj = { args: { label: "Download", color: "primary", - icon: "download", + icon: "Download", }, argTypes: { icon: { control: "select", options: iconNameOptions }, diff --git a/src/stories/Icon.stories.ts b/src/stories/Icon.stories.ts index 4d6294a..9065a47 100644 --- a/src/stories/Icon.stories.ts +++ b/src/stories/Icon.stories.ts @@ -1,5 +1,5 @@ import { Meta, StoryObj } from "@storybook/react"; -import Icon from "src/components/Icon.tsx"; +import Icon, { iconNameOptions } from "src/components/Icon.tsx"; import { textColorOptions } from "src/components/utils/colors.ts"; /** Icons are visual symbols used to represent ideas, objects, or actions. Icons can also be used in buttons. They communicate messages at a glance, afford interactivity, and draw attention to important information. */ @@ -7,6 +7,7 @@ const meta = { title: "Components/Icon", component: Icon, argTypes: { + name: { control: "select", options: iconNameOptions }, color: { control: "select", options: textColorOptions }, }, } satisfies Meta; @@ -15,7 +16,7 @@ export default meta; export const SimpleIcon: StoryObj = { args: { - name: "attention-circle", + name: "AttentionCircle", color: "body", customClass: "w-[16px] h-[16px]", }, diff --git a/src/stories/Menu.stories.tsx b/src/stories/Menu.stories.tsx index 2a44ff1..624737f 100644 --- a/src/stories/Menu.stories.tsx +++ b/src/stories/Menu.stories.tsx @@ -14,9 +14,9 @@ const meta = { export default meta; const items = [ - { key: "expand", itemName: "Expand", iconName: "expand" }, - { key: "add", itemName: "Add", iconName: "plus" }, - { key: "remove", itemName: "Remove", iconName: "cross", danger: true }, + { key: "expand", itemName: "Expand", iconName: "Expand" }, + { key: "add", itemName: "Add", iconName: "Plus" }, + { key: "remove", itemName: "Remove", iconName: "Cross", danger: true }, ]; export const ActionMenuWithoutIcons = () => { -- GitLab