Skip to content

Commit

Permalink
Wrap ActionIcon component to include automatically sized icon.
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesrkiger committed Jan 14, 2025
1 parent 7672111 commit a49c620
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 68 deletions.
30 changes: 30 additions & 0 deletions jsapp/js/components/common/ActionIcon.tsx
Original file line number Diff line number Diff line change
@@ -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<ActionIconPropsMantine, 'size'> {
iconName: IconName;
size: 'sm' | 'md' | 'lg';
}

const ActionIcon = forwardRef<HTMLButtonElement, ActionIconProps>(
({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 (
<ActionIconMantine {...props} ref={ref}>
<Icon name={iconName} size={iconSize} />
</ActionIconMantine>
);
}
);

export default createPolymorphicComponent<'button', ActionIconProps>(
ActionIcon
);
104 changes: 36 additions & 68 deletions jsapp/js/components/common/actionIcon.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,24 @@
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<ActionIconProps['variant']> = [
'filled',
'light',
// 'outline',
// 'subtle',
// 'default',
// 'gradient',

//// Custom:
'danger',
'danger-secondary',
'transparent',
];

const actionIconSizes: MantineSize[] = [
// 'xs',
const actionIconSizes: Array<ActionIconProps['size']> = [
'sm',
'md',
'lg',
// 'xl',
];

function generateSizedIconElementMap(size: IconSize) {
return Object.keys(IconNames)
.map((key) => [key, <Icon name={key as IconNames} size={size} />] as const)
.reduce((o, [k, v]) => {
return {...o, [k]: v};
}, {});
}

export default {
title: 'common/Action Icon',
component: ActionIcon,
Expand All @@ -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<typeof ActionIcon>;

const Template: StoryFn<typeof ActionIcon> = ({
iconS,
iconM,
iconL,
...args
}: any) => <ActionIcon {...{...args, children: iconS ?? iconM ?? iconL}} />;
type Story = StoryObj<typeof ActionIcon>;

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 = () => (
Expand All @@ -133,7 +101,7 @@ export const AllIconStyles = () => (
const actionIconProps: ActionIconProps = {
variant,
size: size,
children: <Icon name={'more'} size={size[0] as any} />,
iconName: 'more',
};
return (
<>
Expand Down

0 comments on commit a49c620

Please sign in to comment.