Skip to content

Commit

Permalink
feat(Icon): expose transformation properties
Browse files Browse the repository at this point in the history
  • Loading branch information
ajkl2533 committed May 13, 2024
1 parent 8311909 commit 2b825db
Show file tree
Hide file tree
Showing 15 changed files with 194 additions and 83 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
],
"scripts": {
"build": "rollup -c",
"storybook": "start-storybook -p 8008",
"storybook": "start-storybook -p 8008 --no-open",
"storybook:build": "build-storybook --quiet",
"test:storybook:visual": "./visual-regressions/bin/take-and-check-in-docker.sh",
"test:storybook:visual:update": "./visual-regressions/bin/take-and-update-in-docker.sh",
Expand Down
5 changes: 2 additions & 3 deletions src/components/DatatableV2/Datatable.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,9 @@ export type DatatableHeaderGroup<D> = Omit<HeaderGroup<D>, 'headers'> & {

export type DatatableRowAction<D> = null | {
label: string;
/* eslint-disable @typescript-eslint/ban-types */
// eslint-disable-next-line @typescript-eslint/ban-types
iconName: SSCIcons | (string & {});
iconType?: SSCIconTypes | (string & {});
/* eslint-enable @typescript-eslint/ban-types */
iconType?: SSCIconTypes;
onClick(props: {
row: DatatableRow<D>;
table: DatatableInstance<D>;
Expand Down
5 changes: 2 additions & 3 deletions src/components/DatatableV2/menus/menuElements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@ export const MenuItem = ({
className,
}: {
children: string;
/* eslint-disable @typescript-eslint/ban-types */
// eslint-disable-next-line @typescript-eslint/ban-types
iconName: SSCIcons | (string & {});
iconType?: SSCIconTypes | (string & {});
/* eslint-enable @typescript-eslint/ban-types */
iconType?: SSCIconTypes;
onClick: (event: Event) => void;
isDisabled?: boolean;
className?: string;
Expand Down
147 changes: 119 additions & 28 deletions src/components/Icon/Icon.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import React from 'react';
import styled from 'styled-components';
import { Meta, Story } from '@storybook/react/types-6-0';
import { head, pipe, sortBy, toPairs } from 'ramda';
import { ComponentMeta, ComponentStory } from '@storybook/react';

import Icon from './Icon';
import { IconProps } from './Icon.types';
import { IconTypes, SSCIconNames } from '../../theme/icons/icons.enums';
import { SSCIconNames } from '../../theme/icons/icons.enums';
import { generateControl } from '../../utils/tests/storybook';
import { Text } from '../typographyLegacy';
import { Grid, Inline } from '../layout';
import { Inline, Stack } from '../layout';

export default {
title: 'components/Icon',
Expand All @@ -18,33 +14,128 @@ export default {
...generateControl('select', SSCIconNames),
},
},
} as Meta;
} as ComponentMeta<typeof Icon>;

export const SscIcon: Story<IconProps> = ({ ...args }) => <Icon {...args} />;
SscIcon.args = {
name: SSCIconNames.wrench,
type Story = ComponentStory<typeof Icon>;

export const Playground: Story = (args) => <Icon {...args} />;
Playground.args = {
name: 'wrench',
color: 'neutral.900',
size: 'xl',
};
Playground.parameters = {
screenshot: { skip: true },
};

export const Color: Story = (args) => <Icon {...args} />;
Color.args = {
name: 'error-circle',
color: 'error.500',
size: 'xl',
};

export const Size: Story = (args) => (
<Inline gap="md">
<Icon size="sm" {...args} />
<Icon size="md" {...args} />
<Icon size="lg" {...args} />
<Icon size="xl" {...args} />
</Inline>
);
Size.args = {
name: 'eye',
};

export const Rotation: Story = (args) => (
<Inline gap="md">
<Icon {...args} />
<Icon rotation={90} {...args} />
<Icon rotation={180} {...args} />
<Icon rotation={270} {...args} />
</Inline>
);
Rotation.args = {
name: 'long-arrow-left',
size: 'xl',
};

export const Flip: Story = (args) => (
<Inline gap="md">
<Icon {...args} />
<Icon flip="horizontal" {...args} />
<Icon flip="vertical" {...args} />
<Icon flip="both" {...args} />
</Inline>
);
Flip.args = {
name: 'wrench',
size: 'xl',
};

SscIcon.storyName = 'SecurityScorecard Icon';
export const FreeTransform: Story = (args) => (
<Stack gap="lg">
<Inline gap="lg">
<Icon transform="shrink-8" {...args} />
<Icon {...args} />
<Icon transform="grow-8" {...args} />
</Inline>

<Inline gap="lg">
<Icon transform="shrink-8" {...args} />
<Icon transform="shrink-8 up-6" {...args} />
<Icon transform="shrink-8 up-6 right-6" {...args} />
<Icon transform="shrink-8 right-6" {...args} />
<Icon transform="shrink-8 down-6 right-6" {...args} />
<Icon transform="shrink-8 down-6" {...args} />
<Icon transform="shrink-8 down-6 left-6" {...args} />
<Icon transform="shrink-8 left-6" {...args} />
<Icon transform="shrink-8 up-6 left-6" {...args} />
</Inline>

const SIcon = styled(Icon)`
font-size: 2rem;
color: #4aba00;
`;
<Inline gap="lg">
<Icon {...args} />
<Icon transform="rotate-45" {...args} />
<Icon transform="rotate-90" {...args} />
<Icon transform="rotate-135" {...args} />
<Icon transform="rotate-180" {...args} />
<Icon transform="rotate--135" {...args} />
<Icon transform="rotate--90" {...args} />
<Icon transform="rotate--45" {...args} />
</Inline>

export const StyledIcon: Story<IconProps> = () => (
<SIcon name={SSCIconNames.wrench} type={IconTypes.ssc} />
<Inline gap="lg">
<Icon {...args} />
<Icon transform="flip-v" {...args} />
<Icon transform="flip-h" {...args} />
<Icon transform="flip-v flip-h" {...args} />
</Inline>
</Stack>
);
FreeTransform.args = {
name: 'filter',
size: 'xl',
style: { background: 'mistyrose' },
};

const sortedIconsList = pipe(toPairs, sortBy(head))(SSCIconNames);
export const IconsList = () => (
<Grid cols={4} gap="md">
{sortedIconsList.map(([key, value]) => (
<Inline key={key} align="center" gap="md">
<Icon name={value} hasFixedWidth />
<Text>{key}</Text>
</Inline>
))}
</Grid>
export const UnorderedList: Story = (args) => (
<Stack as="ul" className="fa-ul" gap="sm">
<li>
<Icon {...args} /> list item
</li>
<li>
<Icon {...args} /> list item
</li>
<li>
<Icon {...args} /> list item
</li>
<li>
<Icon {...args} /> list item
</li>
</Stack>
);
UnorderedList.args = {
name: 'check',
listItem: true,
color: 'success.500',
};
56 changes: 22 additions & 34 deletions src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import {
FontAwesomeIcon,
FontAwesomeIconProps,
} from '@fortawesome/react-fontawesome';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
IconName,
IconPrefix,
Expand All @@ -15,31 +11,39 @@ import { isNotUndefined } from 'ramda-adjunct';
import cls from 'classnames';

import { createSpacings, getColor } from '../../utils';
import { IconTypes, SSCIconNames } from '../../theme/icons/icons.enums';
import { ColorTypes } from '../../theme/colors.enums';
import { Color } from '../../theme/colors.types';
import { IconProps, SSCIcons, Types } from './Icon.types';
import { SpacingSizeValuePropType } from '../../types/spacing.types';
import { IconTypes } from '../../theme/icons/icons.enums';
import { IconProps } from './Icon.types';
import { CLX_COMPONENT } from '../../theme/constants';
import { useLogger } from '../../hooks/useLogger';

const StyledIcon = styled(FontAwesomeIcon).withConfig<{ color: Color }>({
shouldForwardProp: (property) => !includes(property, ['margin', 'color']),
const sizes: Record<IconProps['size'], string> = {
sm: '0.875rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
};

const StyledIcon = styled(FontAwesomeIcon).withConfig<{
$color: IconProps['color'];
$size: IconProps['size'];
}>({
shouldForwardProp: (property) => !includes(property, ['margin']),
})`
color: ${({ color, theme }) =>
isNotUndefined(color) ? getColor(color, { theme }) : 'inherit'};
color: ${({ $color, theme }) =>
isNotUndefined($color) ? getColor($color, { theme }) : 'inherit'};
height: ${({ $size }) => (isNotUndefined($size) ? sizes[$size] : undefined)};
${createSpacings};
`;

const Icon = ({
name,
type = IconTypes.ssc,
color,
size,
className = '',
hasFixedWidth = false,
...props
}: IconProps &
Omit<FontAwesomeIconProps, 'icon' | 'fixedWidth' | 'color' | 'size'>) => {
}: IconProps) => {
const { warn } = useLogger('Icon');
const ref = useRef<SVGSVGElement>(null);
const iconDefinition = findIconDefinition({
Expand All @@ -54,31 +58,15 @@ const Icon = ({
}
return (
<StyledIcon
// @ts-expect-error this passing ref through styled components is rabbit hole
ref={ref}
$color={color}
$size={size}
className={cls(CLX_COMPONENT, className)}
color={color}
fixedWidth={hasFixedWidth}
icon={iconDefinition}
{...props}
/>
);
};

Icon.propTypes = {
name: PropTypes.oneOfType([
PropTypes.oneOf<SSCIcons>(Object.values(SSCIconNames)),
PropTypes.string,
]).isRequired,
type: PropTypes.oneOfType([
PropTypes.oneOf<Types>(Object.values(IconTypes)),
PropTypes.string,
]),
color: PropTypes.oneOf([...Object.values(ColorTypes)]),
className: PropTypes.string,
hasFixedWidth: PropTypes.bool,
margin: SpacingSizeValuePropType,
padding: SpacingSizeValuePropType,
};

export default Icon;
38 changes: 35 additions & 3 deletions src/components/Icon/Icon.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
import { AriaAttributes, MouseEventHandler } from 'react';

import { Color } from '../../theme/colors.types';
import { IconTypes, SSCIconNames } from '../../theme/icons/icons.enums';
import { SpacingProps } from '../../types/spacing.types';
Expand All @@ -6,12 +9,41 @@ export type SSCIcons = typeof SSCIconNames[keyof typeof SSCIconNames];
export type Types = typeof IconTypes[keyof typeof IconTypes];

export interface BaseIconProps {
name: SSCIcons | string;
type?: Types | string;
// eslint-disable-next-line @typescript-eslint/ban-types
name: SSCIcons | (string & {});
type?: Types;
}

export interface IconProps extends BaseIconProps, SpacingProps {
export interface IconProps
extends BaseIconProps,
SpacingProps,
AriaAttributes,
Pick<
FontAwesomeIconProps,
| 'rotation'
| 'transform'
| 'flip'
| 'listItem'
| 'tabIndex'
| 'title'
| 'titleId'
| 'style'
> {
size?: 'sm' | 'md' | 'lg' | 'xl';
color?: Color;
className?: string;
hasFixedWidth?: boolean;
[index: `data-${string}`]: string;
/**
* **DEPRECATED** kept for backwards compatibility until we remove.
* The `size` or styled component should be use for setting size.
*
* @deprecated Use "size" or font size in styled-component */
fontSize?: number | string;
/**
* **DEPRECATED** kept for backwards compatibility until we remove.
* You should always use Button or Link component for interactions.
*
* @deprecated Always use Button or Link component for interactions */
onClick?: MouseEventHandler;
}
16 changes: 5 additions & 11 deletions src/components/_internal/BaseButton/BaseButton.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { To } from 'history';
import { DefaultTheme } from 'styled-components';

import { SpacingSizeValue } from '../../../types/spacing.types';
import { Types as IconTypes, SSCIcons } from '../../Icon/Icon.types';
import { BaseIconProps } from '../../Icon/Icon.types';
import {
BaseButtonColors,
BaseButtonSizes,
Expand All @@ -16,12 +16,6 @@ export type Variants =
export type Sizes = typeof BaseButtonSizes[keyof typeof BaseButtonSizes];
export type Colors = typeof BaseButtonColors[keyof typeof BaseButtonColors];

export interface BaseIcon {
// eslint-disable-next-line @typescript-eslint/ban-types
name: SSCIcons | (string & {});
type?: IconTypes;
}

export interface BaseButtonProps
extends Omit<React.HTMLProps<HTMLButtonElement>, 'size' | 'as'> {
variant?: Variants;
Expand All @@ -35,12 +29,12 @@ export interface BaseButtonProps
href?: string;
to?: To;
className?: string;
iconStart?: BaseIcon;
iconEnd?: BaseIcon;
iconStart?: BaseIconProps;
iconEnd?: BaseIconProps;
/** @deprecated Use iconStart property instead */
iconName?: SSCIcons | string;
iconName?: BaseIconProps['name'];
/** @deprecated Use iconStart property instead */
iconType?: IconTypes | string;
iconType?: BaseIconProps['type'];
loadingText?: string;
tabIndex?: number;
}
Expand Down
Loading

0 comments on commit 2b825db

Please sign in to comment.