Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: icon component TET-648 #111

Merged
merged 5 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/components/Icon/Icon.props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IconName } from '@virtuslab/tetrisly-icons';

import type { BaseProps } from '@/types/BaseProps';

export type IconProps = {
name: IconName;
color?: BaseProps['color'];
custom?: BaseProps;
};
41 changes: 41 additions & 0 deletions src/components/Icon/Icon.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Meta, StoryObj } from '@storybook/react';
import { icons } from '@virtuslab/tetrisly-icons';

import { Icon } from './Icon';

import { IconDocs } from '@/docs-components/IconDocs';
import { TetDocs } from '@/docs-components/TetDocs';

const meta = {
title: 'Foundations / Icon',
component: Icon,
tags: ['autodocs'],
args: {
name: '20-tetrisly',
},
argTypes: {
name: {
options: Object.keys(icons),
defaultValue: undefined,
control: { type: 'select' },
},
},
parameters: {
docs: {
description: {
component:
'A clean, consistent, and pixel-perfect icon library crafted especially for modern UI design.',
},
page: () => (
<TetDocs docs="https://docs.tetrisly.com/foundations/overview/icons">
<IconDocs />
</TetDocs>
),
},
},
} satisfies Meta<typeof Icon>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {};
22 changes: 22 additions & 0 deletions src/components/Icon/Icon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Icon } from './Icon';
import { render } from '../../tests/render';

const getIcon = (jsx: JSX.Element) => {
const { getByTestId } = render(jsx);

return getByTestId('icon');
};

describe('Loader', () => {
it('should render the component', () => {
const icon = getIcon(<Icon name="20-tetrisly" />);

expect(icon).toBeInTheDocument();
});

it('should render star with $color-raspberry-0 color', () => {
const icon = getIcon(<Icon name="20-star" color="$color-raspberry-0" />);

expect(icon).toHaveStyle('color: rgb(192, 48, 96);');
});
});
15 changes: 15 additions & 0 deletions src/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Icon as SVGIcon } from '@virtuslab/tetrisly-icons';
import { styled, system } from '@xstyled/styled-components';
import { FC } from 'react';

import type { IconProps } from './Icon.props';

import type { MarginProps } from '@/types/MarginProps';

const SVG = styled(SVGIcon)`
${system}
`;

export const Icon: FC<IconProps & MarginProps> = ({ custom, ...rest }) => (
<SVG data-testid="icon" {...rest} {...custom} />
);
2 changes: 2 additions & 0 deletions src/components/Icon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Icon } from './Icon';
export type { IconProps } from './Icon.props';
78 changes: 78 additions & 0 deletions src/docs-components/IconDocs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { icons, IconName } from '@virtuslab/tetrisly-icons';
import { FC } from 'react';

import { SectionHeader } from './common/SectionHeader';

import { Icon } from '@/components/Icon';
import { tet } from '@/tetrisly';
import { MarginProps } from '@/types';

const iconsNames = Object.keys(icons) as IconName[];

const icons20 = iconsNames.filter((iconName) => iconName.startsWith('20-'));
const icons16 = iconsNames.filter((iconName) => iconName.startsWith('16-'));

const IconsBoard: FC<{ heading: string; items: IconName[] } & MarginProps> = ({
heading,
items,
...rest
}) => (
<tet.div {...rest}>
<SectionHeader variant="H1" as="h3" mb="$dimension-300">
{heading}
</SectionHeader>
<tet.div
display="grid"
gridTemplateColumns="repeat(2, 1fr)"
ringInset
ring="$border-width-small"
ringColor="$color-border-default"
borderRadius="$border-radius-medium"
overflow="hidden"
>
{items.map((iconName) => (
<tet.div
display="flex"
alignItems="center"
py="$dimension-200"
px="$dimension-300"
borderBottom
borderRight
borderColor="$color-border-default"
key={iconName}
>
<tet.div
display="flex"
alignItems="center"
justifyContent="center"
w="$size-medium"
h="$size-medium"
borderRadius="$border-radius-medium"
backgroundColor="$color-background-neutral-subtle"
>
<Icon color="$color-content-primary" name={iconName as IconName} />
adrian-potepa marked this conversation as resolved.
Show resolved Hide resolved
</tet.div>
<tet.b
text="$typo-body-strong-medium"
color="$color-content-primary"
ml="$dimension-300"
>
{iconName}
</tet.b>
</tet.div>
))}
</tet.div>
</tet.div>
);

export const IconDocs = () => (
<tet.section>
<tet.div px="$dimension-1000">
<SectionHeader variant="Hero" as="h2">
Icons lists
</SectionHeader>
<IconsBoard heading="20x20" items={icons20} />
<IconsBoard heading="16x16" items={icons16} mt="$dimension-1000" />
</tet.div>
</tet.section>
);