diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 738aab668ab1bc0c3864a6051be0311b82d42aaf..5a0fa6c888b99fdd70cf920cc4cee53f66997da2 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 94663386e2cc02c92b75a70541b1e64aa1a4eabd..2f63c71067a1ede498d378052ff056bf2ecc89bc 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 7151d96be7b9e32c69909e9f22c4621ac203f88d..34be31ea4e5f1ff5924aa55be85c143e6ddbbab8 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 b5a3bc1fe2026a2c39cec6b8aeccb99096852045..b4a339d04e85670c8baa7ea0818dec2777606dda 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 652813852145975881c0e75a7a1f0055c608b2fd..36e243a8e5c79d92e6dd34b16aa149d25a67ccb9 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 78f350520d3ad4c78df2fb3f787274971a7920b3..b687b53c56afdeb261091131874dc735e2243624 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 024a44d48e73be9bd81b0cc1738cf0fc8e70c03d..8c8acb39c3d30eebd0299b91b38c2a86191583b4 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 b1362f018c1ded99a50eb5c6a54ea18a918906e7..df16f8e4bba922e2c52aacbb0c4a26162707b281 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 f46879e2239fa5cb6413ee2d5f0297c11ce2d5b3..a6f71b3cce60adfebf330a5dcd927c2a698e5a90 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 30aae9157fdb81245f85d754c47c186347da33f6..efa513eb03fb4a2523240d5b7c0f12fd48283eca 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 d28cc3a9f9e2acc0b6b368820032f273c9299e62..ad64622c7112404c2f07f879346ad96e9d33b9e1 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 dff794e922f918c28a7dd1801345ffaa4af682f1..276f5009bbb15d89551468d443bfd18cc67cbdbb 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 4d6294a1c7ad55bbc2fddd66ef94fc91a763fc44..9065a47bf4bc7fb7c3e2886f98edc92d680773a4 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 2a44ff1f7047b259f70a8d965868ebda07fdc300..624737f75d1d9701c0493ff9b503afb6dd231278 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 = () => {