diff options
Diffstat (limited to 'src/components/organisms/widgets')
4 files changed, 140 insertions, 0 deletions
diff --git a/src/components/organisms/widgets/social-media.module.scss b/src/components/organisms/widgets/social-media.module.scss new file mode 100644 index 0000000..01b6c0e --- /dev/null +++ b/src/components/organisms/widgets/social-media.module.scss @@ -0,0 +1,10 @@ +@use "@styles/abstracts/placeholders"; + +.list { + @extend %reset-list; + + display: flex; + flex-flow: row wrap; + gap: var(--spacing-xs); + padding: 0 var(--spacing-2xs); +} diff --git a/src/components/organisms/widgets/social-media.stories.tsx b/src/components/organisms/widgets/social-media.stories.tsx new file mode 100644 index 0000000..2b84012 --- /dev/null +++ b/src/components/organisms/widgets/social-media.stories.tsx @@ -0,0 +1,56 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { IntlProvider } from 'react-intl'; +import SocialMediaWidget, { Media } from './social-media'; + +export default { + title: 'Organisms/Widgets', + component: SocialMediaWidget, + argTypes: { + level: { + control: { + type: 'number', + }, + description: 'The heading level.', + type: { + name: 'number', + required: true, + }, + }, + media: { + description: 'The links data.', + type: { + name: 'object', + required: true, + value: {}, + }, + }, + title: { + control: { + type: 'text', + }, + description: 'The widget title.', + type: { + name: 'string', + required: true, + }, + }, + }, +} as ComponentMeta<typeof SocialMediaWidget>; + +const Template: ComponentStory<typeof SocialMediaWidget> = (args) => ( + <IntlProvider locale="en"> + <SocialMediaWidget {...args} /> + </IntlProvider> +); + +const media: Media[] = [ + { name: 'Github', url: '#' }, + { name: 'LinkedIn', url: '#' }, +]; + +export const SocialMedia = Template.bind({}); +SocialMedia.args = { + media, + title: 'Follow me', + level: 2, +}; diff --git a/src/components/organisms/widgets/social-media.test.tsx b/src/components/organisms/widgets/social-media.test.tsx new file mode 100644 index 0000000..e40db30 --- /dev/null +++ b/src/components/organisms/widgets/social-media.test.tsx @@ -0,0 +1,33 @@ +import { render, screen } from '@test-utils'; +import SocialMedia, { Media } from './social-media'; + +const media: Media[] = [ + { name: 'Github', url: '#' }, + { name: 'LinkedIn', url: '#' }, +]; +const title = 'Dolores ut ut'; +const titleLevel = 2; + +/** + * Next.js mock images with next/image component. So for now, I need to mock + * the svg files manually. + */ +jest.mock('@assets/images/social-media/github.svg', () => 'svg-file'); +jest.mock('@assets/images/social-media/linkedin.svg', () => 'svg-file'); + +describe('SocialMedia', () => { + it('renders the widget title', () => { + render(<SocialMedia media={media} title={title} level={titleLevel} />); + expect( + screen.getByRole('heading', { + level: titleLevel, + name: new RegExp(title, 'i'), + }) + ).toBeInTheDocument(); + }); + + it('renders the correct number of items', () => { + render(<SocialMedia media={media} title={title} level={titleLevel} />); + expect(screen.getAllByRole('listitem')).toHaveLength(media.length); + }); +}); diff --git a/src/components/organisms/widgets/social-media.tsx b/src/components/organisms/widgets/social-media.tsx new file mode 100644 index 0000000..58b2f73 --- /dev/null +++ b/src/components/organisms/widgets/social-media.tsx @@ -0,0 +1,41 @@ +import SocialLink, { + type SocialLinkProps, +} from '@components/atoms/links/social-link'; +import Widget, { type WidgetProps } from '@components/molecules/layout/widget'; +import { FC } from 'react'; +import styles from './social-media.module.scss'; + +export type Media = SocialLinkProps; + +export type SocialMediaProps = Pick<WidgetProps, 'level' | 'title'> & { + media: Media[]; +}; + +/** + * Social Media widget component + * + * Render a social media list with links. + */ +const SocialMedia: FC<SocialMediaProps> = ({ media, ...props }) => { + /** + * Retrieve the social media items. + * + * @param {SocialMedia[]} links - An array of social media name and url. + * @returns {JSX.Element[]} The social links. + */ + const getItems = (links: Media[]): JSX.Element[] => { + return links.map((link, index) => ( + <li key={`media-${index}`}> + <SocialLink name={link.name} url={link.url} /> + </li> + )); + }; + + return ( + <Widget expanded={true} {...props}> + <ul className={styles.list}>{getItems(media)}</ul> + </Widget> + ); +}; + +export default SocialMedia; |
