From 805cdefc3c06ce624d0be86f2c0c39771f9b6adf Mon Sep 17 00:00:00 2001 From: Danijel Malinovic <35777482+malinovic@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:46:48 +0100 Subject: [PATCH] feat: add icon button (#64) This pull request adds the icon button component to the designs system. --- .../icon-button/icon-button.stories.tsx | 48 +++++++++++++++++++ src/components/icon-button/icon-button.tsx | 45 +++++++++++++++++ src/components/icon-button/index.ts | 1 + src/components/icons/iconUtils.ts | 6 ++- src/components/index.ts | 1 + tailwind.config.js | 18 +++---- 6 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 src/components/icon-button/icon-button.stories.tsx create mode 100644 src/components/icon-button/icon-button.tsx create mode 100644 src/components/icon-button/index.ts diff --git a/src/components/icon-button/icon-button.stories.tsx b/src/components/icon-button/icon-button.stories.tsx new file mode 100644 index 0000000..afd5b24 --- /dev/null +++ b/src/components/icon-button/icon-button.stories.tsx @@ -0,0 +1,48 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { IconButton } from './icon-button'; +import { CalendarIcon, LocationIcon, ProfileIcon, TimeIcon } from '../icons'; + +const meta: Meta = { + title: 'Components/Icon Button', + component: IconButton, + tags: ['autodocs'], +}; + +type Story = StoryObj; + +export default meta; + +export const Default: Story = { + args: { + href: '#', + children: 'Username', + Icon: ProfileIcon, + }, +}; + +export const BaseColor: Story = { + args: { + href: '#', + children: 'Timestamp', + Icon: TimeIcon, + variant: 'base', + }, +}; + +export const Location: Story = { + args: { + href: '#', + children: 'Location', + Icon: LocationIcon, + variant: 'base', + }, +}; + +export const Calendar: Story = { + args: { + href: '#', + children: 'Joined', + Icon: CalendarIcon, + variant: 'base', + }, +}; diff --git a/src/components/icon-button/icon-button.tsx b/src/components/icon-button/icon-button.tsx new file mode 100644 index 0000000..7b1ee97 --- /dev/null +++ b/src/components/icon-button/icon-button.tsx @@ -0,0 +1,45 @@ +import React, { ComponentType, FC, LinkHTMLAttributes } from 'react'; +import classnames from 'classnames'; +import { IconProps } from '../icons/iconUtils'; + +export type IconButton = { + href: string; + children: string; + variant?: 'primary' | 'base'; + linkComponent?: FC; + Icon: ComponentType; +} & Omit; + +export const IconButton = < + T extends { + className?: string; + rel?: string; + target?: string; + } = LinkHTMLAttributes, +>({ + children, + linkComponent, + variant = 'primary', + Icon, + ...props +}: IconButton) => { + const LinkComponent: FC | 'a' = linkComponent || 'a'; + const colorClasses = classnames({ + 'text-primary-600 hover:text-primary-900': variant === 'primary', + 'text-base-600 hover:text-base-900 hover:fill-error': variant === 'base', + }); + + return ( + & AnchorHTMLAttributes + * It should have no effect on typings for the user and therefor we consider it as best to keep the code simple. + */ + /* eslint-disable-next-line */ + {...(props as any)} + className={classnames(' flex items-center gap-xxs transition-colors duration-300 mb-font-label-s', colorClasses)}> + {} + {children} + + ); +}; diff --git a/src/components/icon-button/index.ts b/src/components/icon-button/index.ts new file mode 100644 index 0000000..13aaf00 --- /dev/null +++ b/src/components/icon-button/index.ts @@ -0,0 +1 @@ +export { IconButton } from './icon-button'; diff --git a/src/components/icons/iconUtils.ts b/src/components/icons/iconUtils.ts index 78fc243..117b03b 100644 --- a/src/components/icons/iconUtils.ts +++ b/src/components/icons/iconUtils.ts @@ -11,7 +11,7 @@ export const getSizeClass = (size: 's' | 'm' | 'l') => { } }; -export const getColorClass = (color: 'base' | 'white' | 'primary' | 'secondary' | 'error') => { +export const getColorClass = (color: 'base' | 'white' | 'primary' | 'secondary' | 'error' | 'inherit') => { switch (color) { case 'base': return 'fill-base-600'; @@ -23,6 +23,8 @@ export const getColorClass = (color: 'base' | 'white' | 'primary' | 'secondary' return 'fill-white'; case 'error': return 'fill-error'; + case 'inherit': + return 'fill-current'; default: return 'fill-slate-600'; } @@ -30,7 +32,7 @@ export const getColorClass = (color: 'base' | 'white' | 'primary' | 'secondary' export interface IconProps { size: 's' | 'm' | 'l'; - color: 'base' | 'white' | 'primary' | 'secondary' | 'error'; + color: 'base' | 'white' | 'primary' | 'secondary' | 'error' | 'inherit'; } export interface FillableIconProps extends IconProps { diff --git a/src/components/index.ts b/src/components/index.ts index e66c570..f89fefb 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -23,3 +23,4 @@ export { Label } from './label'; export { Link } from './link'; export { Textarea } from './textarea'; export { Logo } from './logo'; +export { IconButton } from './icon-button'; diff --git a/tailwind.config.js b/tailwind.config.js index b8d71d2..4b5664d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -6,15 +6,6 @@ import plugin from 'tailwindcss/plugin'; module.exports = { content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], theme: { - colors: { - 'transparent': 'transparent', - white: '#ffffff', - black: '#000000', - error: '#FF0000', - primary: colors.violet, - secondary: colors.pink, - base: colors.slate, - }, fontFamily: { default: ['Poppins', 'Arial', 'sans-serif'], }, @@ -54,6 +45,15 @@ module.exports = { full: '9999px', }, extend: { + colors: { + transparent: 'transparent', + white: '#ffffff', + black: '#000000', + error: '#FF0000', + primary: colors.violet, + secondary: colors.pink, + base: colors.slate, + }, spacing: { xxs: '4px', xs: '8px',