Skip to content
Snippets Groups Projects
Commit f951e4aa authored by Klaas Kliffen's avatar Klaas Kliffen :satellite:
Browse files

Merge branch 'empty-states' into 'main'

make icons great again

See merge request !40
parents d4e1298a 848ae258
No related branches found
No related tags found
1 merge request!40make icons great again
Pipeline #77278 passed
...@@ -42,6 +42,7 @@ module.exports = { ...@@ -42,6 +42,7 @@ module.exports = {
"warn", "warn",
{ allowConstantExport: true }, { allowConstantExport: true },
], ],
"import/namespace": ["error", { allowComputed: true }],
}, },
settings: { settings: {
"import/parsers": { "import/parsers": {
......
...@@ -68,11 +68,16 @@ This is the current workflow: ...@@ -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`. 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. 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 an _export_ entry to the `src/assets/icons/index.ts`:
Add `"your-file-name"` to `iconNameOptions` array and the `iconMap` dict of `Icon.tsx`. Then, you can use your icon like this:
```tsx ```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 ### Classnames
......
import AttentionCircle from "./attention-circle.svg"; export { default as 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 { export { default as CheckmarkCircle } from "./checkmark-circle.svg";
AttentionCircle, export { default as Checkmark } from "./checkmark.svg";
CheckmarkCircle, export { default as CrossCircle } from "./cross-circle.svg";
Checkmark, export { default as Cross } from "./cross.svg";
CrossCircle, export { default as Datepicker } from "./datepicker.svg";
Cross, export { default as Download } from "./download.svg";
Datepicker, export { default as Dropdown } from "./dropdown.svg";
Download, export { default as Edit } from "./edit.svg";
Dropdown, export { default as Expand } from "./expand.svg";
Edit, export { default as Eye } from "./eye.svg";
Expand, export { default as InfoCircle } from "./info-circle.svg";
Eye, export { default as Layer } from "./layer.svg";
InfoCircle, export { default as Magnifier } from "./magnifier.svg";
Layer, export { default as Minus } from "./minus.svg";
Magnifier, export { default as Next } from "./next.svg";
Minus, export { default as Plus } from "./plus.svg";
Next, export { default as Previous } from "./previous.svg";
Plus, export { default as Up } from "./up.svg";
Previous,
Up,
};
...@@ -20,10 +20,10 @@ type AlertProps = { ...@@ -20,10 +20,10 @@ type AlertProps = {
*/ */
const Alert = ({ title, message, alertType = "primary" }: AlertProps) => { const Alert = ({ title, message, alertType = "primary" }: AlertProps) => {
const icon = { const icon = {
primary: "info-circle", primary: "InfoCircle",
warning: "attention-circle", warning: "AttentionCircle",
positive: "checkmark-circle", positive: "CheckmarkCircle",
negative: "cross-circle", negative: "CrossCircle",
}; };
const baseClass = 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"; "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) => { ...@@ -40,7 +40,7 @@ const Alert = ({ title, message, alertType = "primary" }: AlertProps) => {
<Typography text={message} variant="paragraph" /> <Typography text={message} variant="paragraph" />
</div> </div>
{/* if used more often than below and in Modal, consider making it a CloseIcon */} {/* 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> </div>
); );
}; };
......
...@@ -17,7 +17,7 @@ const Breadcrumb = ({ items }: BreadcrumbProps) => { ...@@ -17,7 +17,7 @@ const Breadcrumb = ({ items }: BreadcrumbProps) => {
itemsBeforeCollapse={1} itemsBeforeCollapse={1}
itemsAfterCollapse={2} itemsAfterCollapse={2}
separator={ separator={
<Icon name="next" color="body" customClass="w-[7px] h-[7px]" /> <Icon name="Next" color="body" customClass="w-[7px] h-[7px]" />
} }
itemClasses={{ itemClasses={{
item: clsx( item: clsx(
......
...@@ -30,7 +30,7 @@ const Checkbox = ({ ...@@ -30,7 +30,7 @@ const Checkbox = ({
isDisabled={isDisabled} isDisabled={isDisabled}
isSelected={isSelected} isSelected={isSelected}
onValueChange={setIsSelected} 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 // 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 // that's why the color "default" is set to "transparent" in tailwind.config.js
color="default" color="default"
......
...@@ -77,7 +77,7 @@ const DateInput = ({ ...@@ -77,7 +77,7 @@ const DateInput = ({
labelCustomColor={labelCustomColor} labelCustomColor={labelCustomColor}
endContent={ endContent={
<Icon <Icon
name="datepicker" name="Datepicker"
// Note: textColor("body") has a different grey, hence the below is used // Note: textColor("body") has a different grey, hence the below is used
customClass="text-grey dark:text-baseWhite" customClass="text-grey dark:text-baseWhite"
/> />
...@@ -106,7 +106,7 @@ const DateInput = ({ ...@@ -106,7 +106,7 @@ const DateInput = ({
}: PaginationProps) => ( }: PaginationProps) => (
<div className="flex flex-row justify-between items-center px-2 h-[30px] border-b-1 border-grey"> <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]"> <button onClick={onClickPrev} className="w-[16px] h-[16px]">
<Icon name="previous" color="body" /> <Icon name="Previous" color="body" />
</button> </button>
<Typography <Typography
text={text} text={text}
...@@ -115,7 +115,7 @@ const DateInput = ({ ...@@ -115,7 +115,7 @@ const DateInput = ({
customClass={textColor("body")} customClass={textColor("body")}
/> />
<button onClick={onClickNext} className="w-[16px] h-[16px]"> <button onClick={onClickNext} className="w-[16px] h-[16px]">
<Icon name="next" color="body" /> <Icon name="Next" color="body" />
</button> </button>
</div> </div>
); );
......
...@@ -58,7 +58,7 @@ const Dropdown = ({ ...@@ -58,7 +58,7 @@ const Dropdown = ({
variant: "bordered", variant: "bordered",
isRequired: isRequired, isRequired: isRequired,
}} // use the same props as BaseInput }} // use the same props as BaseInput
selectorIcon={<Icon name="dropdown" />} selectorIcon={<Icon name="Dropdown" />}
isClearable={false} // so the "cross" button does not show isClearable={false} // so the "cross" button does not show
classNames={{ classNames={{
selectorButton: clsx( selectorButton: clsx(
......
import { ReactSVG } from "react-svg"; import { ReactSVG } from "react-svg";
import { clsx } from "clsx"; import { clsx } from "clsx";
import Icons from "src/assets/icons"; import * as Icons from "src/assets/icons";
import { TextColorType } from "./utils/colors.ts"; import { TextColorType } from "./utils/colors.ts";
import { textColor } from "./utils/classes.ts"; import { textColor } from "./utils/classes.ts";
export const iconNameOptions = [ export type IconName = keyof typeof Icons;
"attention-circle", export const iconNameOptions = Object.keys(Icons) as IconName[]; // for storybook dropdown
"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,
};
type IconProps = { type IconProps = {
name: IconName; name: IconName;
...@@ -64,7 +21,7 @@ type IconProps = { ...@@ -64,7 +21,7 @@ type IconProps = {
* @param customClass: string | undefined * @param customClass: string | undefined
*/ */
const Icon = ({ name, color = null, customClass = "" }: IconProps) => { const Icon = ({ name, color = null, customClass = "" }: IconProps) => {
const src = iconMap[name]; const src = Icons[name];
if (!color && !customClass) { if (!color && !customClass) {
return <ReactSVG src={src} style={{ color: "currentColor" }} />; return <ReactSVG src={src} style={{ color: "currentColor" }} />;
......
...@@ -90,7 +90,7 @@ export const ActionMenu = ({ label, items, onAction }: MenuProps) => { ...@@ -90,7 +90,7 @@ export const ActionMenu = ({ label, items, onAction }: MenuProps) => {
onAction={onAction} onAction={onAction}
menuTrigger={ menuTrigger={
<NextButton <NextButton
startContent={<Icon name="dropdown" />} startContent={<Icon name="Dropdown" />}
className={buttonBaseClass} // same as Button component (src/components/Button.tsx) className={buttonBaseClass} // same as Button component (src/components/Button.tsx)
type="button" type="button"
color="primary" color="primary"
...@@ -133,7 +133,7 @@ export const ProfileMenu = ({ label, items, onAction }: MenuProps) => { ...@@ -133,7 +133,7 @@ export const ProfileMenu = ({ label, items, onAction }: MenuProps) => {
/> />
} }
/> />
<Icon name="dropdown" color="primary" /> <Icon name="Dropdown" color="primary" />
</div> </div>
} }
/> />
......
...@@ -36,7 +36,7 @@ const Modal = ({ ...@@ -36,7 +36,7 @@ const Modal = ({
}: ModalProps) => { }: ModalProps) => {
const closeButton = ( const closeButton = (
<button> <button>
<Icon name="cross" /> <Icon name="Cross" />
</button> </button>
); );
return ( return (
......
...@@ -29,7 +29,7 @@ export const IconButton: StoryObj = { ...@@ -29,7 +29,7 @@ export const IconButton: StoryObj = {
args: { args: {
label: "Download", label: "Download",
color: "primary", color: "primary",
icon: "download", icon: "Download",
}, },
argTypes: { argTypes: {
icon: { control: "select", options: iconNameOptions }, icon: { control: "select", options: iconNameOptions },
......
import { Meta, StoryObj } from "@storybook/react"; 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"; 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. */ /** 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 = { ...@@ -7,6 +7,7 @@ const meta = {
title: "Components/Icon", title: "Components/Icon",
component: Icon, component: Icon,
argTypes: { argTypes: {
name: { control: "select", options: iconNameOptions },
color: { control: "select", options: textColorOptions }, color: { control: "select", options: textColorOptions },
}, },
} satisfies Meta; } satisfies Meta;
...@@ -15,7 +16,7 @@ export default meta; ...@@ -15,7 +16,7 @@ export default meta;
export const SimpleIcon: StoryObj = { export const SimpleIcon: StoryObj = {
args: { args: {
name: "attention-circle", name: "AttentionCircle",
color: "body", color: "body",
customClass: "w-[16px] h-[16px]", customClass: "w-[16px] h-[16px]",
}, },
......
...@@ -14,9 +14,9 @@ const meta = { ...@@ -14,9 +14,9 @@ const meta = {
export default meta; export default meta;
const items = [ const items = [
{ key: "expand", itemName: "Expand", iconName: "expand" }, { key: "expand", itemName: "Expand", iconName: "Expand" },
{ key: "add", itemName: "Add", iconName: "plus" }, { key: "add", itemName: "Add", iconName: "Plus" },
{ key: "remove", itemName: "Remove", iconName: "cross", danger: true }, { key: "remove", itemName: "Remove", iconName: "Cross", danger: true },
]; ];
export const ActionMenuWithoutIcons = () => { export const ActionMenuWithoutIcons = () => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment