aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/organisms/widgets/social-media.module.scss10
-rw-r--r--src/components/organisms/widgets/social-media.stories.tsx56
-rw-r--r--src/components/organisms/widgets/social-media.test.tsx33
-rw-r--r--src/components/organisms/widgets/social-media.tsx41
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;