From c90549a0cc91d1702d90a90c3237013ed45df50f Mon Sep 17 00:00:00 2001 From: Nathaniel Waldschmidt <77284592+NateWaldschmidt@users.noreply.github.com> Date: Wed, 10 Apr 2024 15:49:27 -0500 Subject: [PATCH] feat: add icon button (#470) --- CHANGELOG.md | 4 + package-lock.json | 4 +- package.json | 2 +- src/components/ConditionalWrapper.vue | 12 + src/components/Icon/svgs/BankAccount.svg | 2 +- src/components/IconButton/IconButton.mdx | 10 + .../IconButton/IconButton.stories.ts | 119 ++++++ src/components/IconButton/IconButton.vue | 355 ++++++++++++++++++ .../IconButton/__tests__/IconButton.spec.ts | 22 ++ src/components/IconButton/constants.ts | 28 ++ src/components/IconButton/index.ts | 2 + src/main.ts | 1 + 12 files changed, 557 insertions(+), 4 deletions(-) create mode 100644 src/components/ConditionalWrapper.vue create mode 100644 src/components/IconButton/IconButton.mdx create mode 100644 src/components/IconButton/IconButton.stories.ts create mode 100644 src/components/IconButton/IconButton.vue create mode 100644 src/components/IconButton/__tests__/IconButton.spec.ts create mode 100644 src/components/IconButton/constants.ts create mode 100644 src/components/IconButton/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b553ccf5..824a8289b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v2.0.32 + +- Add `` component + ## v2.0.31 - Adds the icons: diff --git a/package-lock.json b/package-lock.json index aae11f24e..68356b1db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lob/ui-components", - "version": "2.0.31", + "version": "2.0.32", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@lob/ui-components", - "version": "2.0.31", + "version": "2.0.32", "dependencies": { "date-fns": "^2.29.3", "date-fns-holiday-us": "^0.3.1", diff --git a/package.json b/package.json index 4583d76d8..3adf39c1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lob/ui-components", - "version": "2.0.31", + "version": "2.0.32", "engines": { "node": ">=20.2.0", "npm": ">=10.2.0" diff --git a/src/components/ConditionalWrapper.vue b/src/components/ConditionalWrapper.vue new file mode 100644 index 000000000..a3fc99457 --- /dev/null +++ b/src/components/ConditionalWrapper.vue @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/src/components/Icon/svgs/BankAccount.svg b/src/components/Icon/svgs/BankAccount.svg index 9b1870275..28358d27d 100644 --- a/src/components/Icon/svgs/BankAccount.svg +++ b/src/components/Icon/svgs/BankAccount.svg @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/src/components/IconButton/IconButton.mdx b/src/components/IconButton/IconButton.mdx new file mode 100644 index 000000000..f7b0f014e --- /dev/null +++ b/src/components/IconButton/IconButton.mdx @@ -0,0 +1,10 @@ +import { Canvas, ArgTypes, PRIMARY_STORY } from '@storybook/addon-docs'; +import { AllIconButtons, Primary } from './IconButton.stories'; + +# IconButton + + + +## Props + + diff --git a/src/components/IconButton/IconButton.stories.ts b/src/components/IconButton/IconButton.stories.ts new file mode 100644 index 000000000..1bc84f1ee --- /dev/null +++ b/src/components/IconButton/IconButton.stories.ts @@ -0,0 +1,119 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/vue3'; +import { + IconButtonColor, + IconButtonSize, + IconButtonVariant +} from './constants'; +import mdx from './IconButton.mdx'; +// @ts-ignore No types from Vue file +import IconButton from './IconButton.vue'; +import { IconName } from '../Icon/types'; +import routeDecorator, { + routeTemplate +} from '../../../.storybook/routeDecorator'; + +const meta: Meta = { + title: 'Components/IconButton', + component: IconButton, + decorators: [ + routeDecorator('/', [ + { + path: '/internal', + name: 'InternalLink', + component: { + template: routeTemplate('internal') + } + } + ]) + ], + parameters: { + docs: { + page: mdx + } + }, + argTypes: { + color: { + options: Object.values(IconButtonColor), + control: { + type: 'select' + }, + table: { + type: { + summary: Object.values(IconButtonColor).join(' | ') + } + } + }, + icon: { + options: Object.values(IconName), + control: { + type: 'select' + }, + table: { + type: { + summary: Object.values(IconName).join(' | ') + } + } + }, + variant: { + options: Object.values(IconButtonVariant), + control: { + type: 'select' + }, + table: { + type: { + summary: Object.values(IconButtonVariant).join(' | ') + } + } + }, + size: { + options: Object.values(IconButtonSize), + control: { + type: 'select' + }, + table: { + type: { + summary: Object.values(IconButtonSize).join(' | ') + } + } + }, + click: { + table: { + type: 'null' + } + } + } +}; + +export default meta; + +export const Primary: StoryObj = { + args: { + icon: IconName.ENVELOPE + } +}; + +export const AllIconButtons: StoryFn = () => ({ + components: { IconButton }, + template: ` + ${Object.values(IconButtonVariant) + .map( + (variant) => + ` + ${Object.values(IconButtonColor) + .map( + (color) => + ` + ${Object.values(IconButtonSize) + .map( + (size) => + `` + ) + .join('')} + ` + ) + .join('')} + ` + ) + .join('')} + ` +}); diff --git a/src/components/IconButton/IconButton.vue b/src/components/IconButton/IconButton.vue new file mode 100644 index 000000000..98c9bd010 --- /dev/null +++ b/src/components/IconButton/IconButton.vue @@ -0,0 +1,355 @@ + + + + + + + + + + + diff --git a/src/components/IconButton/__tests__/IconButton.spec.ts b/src/components/IconButton/__tests__/IconButton.spec.ts new file mode 100644 index 000000000..4df7a8167 --- /dev/null +++ b/src/components/IconButton/__tests__/IconButton.spec.ts @@ -0,0 +1,22 @@ +import '@testing-library/jest-dom'; +import { IconName } from '@/main'; +import { render } from '@testing-library/vue'; + +import IconButton from '../IconButton.vue'; + +describe('IconButton', () => { + it('renders', () => { + const { getByTestId } = render(IconButton, { + props: { icon: IconName.APP_WINDOWS } + }); + expect(getByTestId('uic-icon-button')).toBeVisible(); + }); + + it('emits click', () => { + const { getByTestId, emitted } = render(IconButton, { + props: { icon: IconName.APP_WINDOWS } + }); + getByTestId('uic-icon-button').click(); + expect(emitted()).toHaveProperty('click'); + }); +}); diff --git a/src/components/IconButton/constants.ts b/src/components/IconButton/constants.ts new file mode 100644 index 000000000..06a2357fc --- /dev/null +++ b/src/components/IconButton/constants.ts @@ -0,0 +1,28 @@ +import { Color, Size, Variant } from '@/types'; + +export const IconButtonColor = { + ERROR: Color.ERROR, + INFO: Color.INFO, + NEUTRAL: Color.NEUTRAL, + SUCCESS: Color.SUCCESS, + UPGRADE: Color.UPGRADE, + WARNING: Color.WARNING +} as const; +export type IconButtonColor = + (typeof IconButtonColor)[keyof typeof IconButtonColor]; + +export const IconButtonSize = { + SM: Size.SM, + MD: Size.MD, + LG: Size.LG, + XL: Size.XL +} as const; +export type IconButtonSize = + (typeof IconButtonSize)[keyof typeof IconButtonSize]; + +export const IconButtonVariant = { + OUTLINED: Variant.OUTLINED, + PRIMARY: Variant.PRIMARY +} as const; +export type IconButtonVariant = + (typeof IconButtonVariant)[keyof typeof IconButtonVariant]; diff --git a/src/components/IconButton/index.ts b/src/components/IconButton/index.ts new file mode 100644 index 000000000..28d619be9 --- /dev/null +++ b/src/components/IconButton/index.ts @@ -0,0 +1,2 @@ +export * from './constants'; +export { default as IconButton } from './IconButton.vue'; diff --git a/src/main.ts b/src/main.ts index 22230875d..e2e655275 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,6 +26,7 @@ const ComponentLibrary = { // TODO Utilize the components export but first we will have to remove global usage. export * from './components/Badge'; export * from './components/Icon'; +export * from './components/IconButton'; export * from './components/Modal'; export * from './components/ImageFileUpload'; export * from './components/ColorPicker';