From a49c620ed6b4c0885cac86f649213e63eeb9825d Mon Sep 17 00:00:00 2001 From: James Kiger Date: Tue, 14 Jan 2025 09:48:25 -0500 Subject: [PATCH] Wrap ActionIcon component to include automatically sized icon. --- jsapp/js/components/common/ActionIcon.tsx | 30 +++++ .../components/common/actionIcon.stories.tsx | 104 ++++++------------ 2 files changed, 66 insertions(+), 68 deletions(-) create mode 100644 jsapp/js/components/common/ActionIcon.tsx diff --git a/jsapp/js/components/common/ActionIcon.tsx b/jsapp/js/components/common/ActionIcon.tsx new file mode 100644 index 0000000000..376fddd69c --- /dev/null +++ b/jsapp/js/components/common/ActionIcon.tsx @@ -0,0 +1,30 @@ +import { + ActionIcon as ActionIconMantine, + createPolymorphicComponent, +} from '@mantine/core'; +import type {ActionIconProps as ActionIconPropsMantine} from '@mantine/core/lib/components'; +import Icon, { IconSize} from './icon'; +import type {IconName} from 'jsapp/fonts/k-icons'; +import {forwardRef} from 'react'; + +export interface ActionIconProps extends Omit { + iconName: IconName; + size: 'sm' | 'md' | 'lg'; +} + +const ActionIcon = forwardRef( + ({iconName, ...props}, ref) => { + // Currently, our icon sizes only use a single letter instead of + // Mantine's 'sm', 'md', etc. So here we grab the first letter. + const iconSize = props.size[0] as IconSize; + return ( + + + + ); + } +); + +export default createPolymorphicComponent<'button', ActionIconProps>( + ActionIcon +); diff --git a/jsapp/js/components/common/actionIcon.stories.tsx b/jsapp/js/components/common/actionIcon.stories.tsx index 3b10c3b4be..b9bd117d85 100644 --- a/jsapp/js/components/common/actionIcon.stories.tsx +++ b/jsapp/js/components/common/actionIcon.stories.tsx @@ -1,17 +1,11 @@ import React from 'react'; -import type {Meta, StoryFn} from '@storybook/react'; +import type {Meta, StoryObj} from '@storybook/react'; import {IconNames} from 'jsapp/fonts/k-icons'; -import type {IconName} from 'jsapp/fonts/k-icons'; -import {ActionIcon, ActionIconProps, MantineSize} from '@mantine/core'; -import Icon, {IconSize} from './icon'; +import ActionIcon, {type ActionIconProps} from './ActionIcon'; const actionIconVariants: Array = [ 'filled', 'light', - // 'outline', - // 'subtle', - // 'default', - // 'gradient', //// Custom: 'danger', @@ -19,22 +13,12 @@ const actionIconVariants: Array = [ 'transparent', ]; -const actionIconSizes: MantineSize[] = [ - // 'xs', +const actionIconSizes: Array = [ 'sm', 'md', 'lg', - // 'xl', ]; -function generateSizedIconElementMap(size: IconSize) { - return Object.keys(IconNames) - .map((key) => [key, ] as const) - .reduce((o, [k, v]) => { - return {...o, [k]: v}; - }, {}); -} - export default { title: 'common/Action Icon', component: ActionIcon, @@ -49,72 +33,56 @@ export default { options: actionIconSizes, control: 'radio', }, - iconS: { - description: 'Icon', - options: Object.keys(IconNames), - mapping: generateSizedIconElementMap('s'), - control: {type: 'select'}, - if: {arg: 'size', eq: 'sm'}, - }, - iconM: { - description: 'Icon', - options: Object.keys(IconNames), - mapping: generateSizedIconElementMap('m'), - control: {type: 'select'}, - if: {arg: 'size', eq: 'md'}, - }, - iconL: { + iconName: { description: 'Icon', options: Object.keys(IconNames), - mapping: generateSizedIconElementMap('l'), control: {type: 'select'}, - if: {arg: 'size', eq: 'lg'}, }, disabled: {control: 'boolean'}, loading: {control: 'boolean'}, }, } as Meta; -const Template: StoryFn = ({ - iconS, - iconM, - iconL, - ...args -}: any) => ; +type Story = StoryObj; -export const Filled = Template.bind({}); -Filled.args = { - variant: 'filled', - size: 'md', - iconM: 'edit', +export const Filled: Story = { + args: { + variant: 'filled', + size: 'md', + iconName: 'edit', + }, }; -export const Light = Template.bind({}); -Light.args = { - variant: 'light', - size: 'md', - iconM: 'edit', +export const Light: Story = { + args: { + variant: 'light', + size: 'md', + iconName: 'edit', + }, }; -export const Transparent = Template.bind({}); -Transparent.args = { - variant: 'transparent', - size: 'md', - iconM: 'more', +export const Transparent: Story = { + args: { + variant: 'transparent', + size: 'md', + iconName: 'more', + }, }; -export const Danger = Template.bind({}); -Danger.args = { - variant: 'danger', - size: 'md', - iconM: 'trash', +export const Danger: Story = { + args: { + variant: 'danger', + size: 'md', + iconName: 'trash', + }, }; -export const DangerSecondary = Template.bind({}); -DangerSecondary.args = { - variant: 'danger-secondary', - size: 'lg', - iconL: 'trash', +export const DangerSecondary: Story = { + args: { + variant: 'danger-secondary', + size: 'lg', + iconName: 'trash', + }, }; export const AllIconStyles = () => ( @@ -133,7 +101,7 @@ export const AllIconStyles = () => ( const actionIconProps: ActionIconProps = { variant, size: size, - children: , + iconName: 'more', }; return ( <>