aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms/lists
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-04-07 19:00:13 +0200
committerArmand Philippot <git@armandphilippot.com>2022-04-07 19:05:09 +0200
commit06396f8e942c58254ee4e87f610d3e33197e0d73 (patch)
tree30e003c15c9d6b3b5428d4af833a967e72843f09 /src/components/atoms/lists
parentff2b6c55cc691f0b62396d9ba481c75fc870cd6a (diff)
chore: add a DescriptionList component
Diffstat (limited to 'src/components/atoms/lists')
-rw-r--r--src/components/atoms/lists/description-list.module.scss42
-rw-r--r--src/components/atoms/lists/description-list.stories.tsx55
-rw-r--r--src/components/atoms/lists/description-list.test.tsx20
-rw-r--r--src/components/atoms/lists/description-list.tsx60
4 files changed, 177 insertions, 0 deletions
diff --git a/src/components/atoms/lists/description-list.module.scss b/src/components/atoms/lists/description-list.module.scss
new file mode 100644
index 0000000..4758816
--- /dev/null
+++ b/src/components/atoms/lists/description-list.module.scss
@@ -0,0 +1,42 @@
+@use "@styles/abstracts/mixins" as mix;
+
+.list {
+ display: flex;
+ flex-flow: column wrap;
+ gap: var(--spacing-2xs);
+ margin: 0;
+
+ &__item {
+ display: flex;
+ flex-flow: column wrap;
+ gap: var(--spacing-2xs);
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ flex-flow: row wrap;
+ }
+ }
+ }
+
+ &__term {
+ flex: 0 0 max-content;
+ color: var(--color-fg-light);
+ font-weight: 600;
+ }
+
+ &__description {
+ flex: 0 0 auto;
+ margin: 0;
+
+ @include mix.media("screen") {
+ @include mix.dimensions("sm") {
+ &:not(:first-of-type) {
+ &::before {
+ content: "/";
+ margin: 0 var(--spacing-2xs);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/components/atoms/lists/description-list.stories.tsx b/src/components/atoms/lists/description-list.stories.tsx
new file mode 100644
index 0000000..e9a60cf
--- /dev/null
+++ b/src/components/atoms/lists/description-list.stories.tsx
@@ -0,0 +1,55 @@
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import DescriptionListComponent, {
+ DescriptionListItem,
+} from './description-list';
+
+export default {
+ title: 'Atoms/Lists',
+ component: DescriptionListComponent,
+ argTypes: {
+ classes: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classes to the list wrapper.',
+ table: {
+ category: 'Options',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
+ items: {
+ control: {
+ type: null,
+ },
+ description: 'The list items.',
+ type: {
+ name: 'object',
+ required: true,
+ value: {},
+ },
+ },
+ },
+} as ComponentMeta<typeof DescriptionListComponent>;
+
+const Template: ComponentStory<typeof DescriptionListComponent> = (args) => (
+ <DescriptionListComponent {...args} />
+);
+
+const items: DescriptionListItem[] = [
+ { id: 'term-1', term: 'Term 1:', value: ['Value for term 1'] },
+ { id: 'term-2', term: 'Term 2:', value: ['Value for term 2'] },
+ {
+ id: 'term-3',
+ term: 'Term 3:',
+ value: ['Value 1 for term 3', 'Value 2 for term 3', 'Value 3 for term 3'],
+ },
+ { id: 'term-4', term: 'Term 4:', value: ['Value for term 4'] },
+];
+
+export const DescriptionList = Template.bind({});
+DescriptionList.args = {
+ items,
+};
diff --git a/src/components/atoms/lists/description-list.test.tsx b/src/components/atoms/lists/description-list.test.tsx
new file mode 100644
index 0000000..d3f7045
--- /dev/null
+++ b/src/components/atoms/lists/description-list.test.tsx
@@ -0,0 +1,20 @@
+import { render } from '@test-utils';
+import DescriptionList, { DescriptionListItem } from './description-list';
+
+const items: DescriptionListItem[] = [
+ { id: 'term-1', term: 'Term 1:', value: ['Value for term 1'] },
+ { id: 'term-2', term: 'Term 2:', value: ['Value for term 2'] },
+ {
+ id: 'term-3',
+ term: 'Term 3:',
+ value: ['Value 1 for term 3', 'Value 2 for term 3', 'Value 3 for term 3'],
+ },
+ { id: 'term-4', term: 'Term 4:', value: ['Value for term 4'] },
+];
+
+describe('DescriptionList', () => {
+ it('renders a list of terms and description', () => {
+ const { container } = render(<DescriptionList items={items} />);
+ expect(container).toBeDefined();
+ });
+});
diff --git a/src/components/atoms/lists/description-list.tsx b/src/components/atoms/lists/description-list.tsx
new file mode 100644
index 0000000..df2880f
--- /dev/null
+++ b/src/components/atoms/lists/description-list.tsx
@@ -0,0 +1,60 @@
+import { FC } from 'react';
+import styles from './description-list.module.scss';
+
+export type DescriptionListItem = {
+ /**
+ * The item id.
+ */
+ id: string;
+ /**
+ * A list term.
+ */
+ term: string;
+ /**
+ * An array of values for the list term.
+ */
+ value: any[];
+};
+
+export type DescriptionListProps = {
+ /**
+ * Set additional classes to the list wrapper.
+ */
+ classes?: string;
+ /**
+ * The list items.
+ */
+ items: DescriptionListItem[];
+};
+
+/**
+ * DescriptionList component
+ *
+ * Render a description list.
+ */
+const DescriptionList: FC<DescriptionListProps> = ({ classes = '', items }) => {
+ /**
+ * Retrieve the description list items wrapped in a div element.
+ *
+ * @param {DescriptionListItem[]} listItems - An array of term and description couples.
+ * @returns {JSX.Element[]} The description list items.
+ */
+ const getItems = (listItems: DescriptionListItem[]): JSX.Element[] => {
+ return listItems.map(({ id, term, value }) => {
+ return (
+ <div key={id} className={styles.list__item}>
+ <dt className={styles.list__term}>{term}</dt>
+ {value.map((currentValue, index) => (
+ <dd key={`${id}-${index}`} className={styles.list__description}>
+ {currentValue}
+ </dd>
+ ))}
+ </div>
+ );
+ });
+ };
+
+ return <dl className={`${styles.list} ${classes}`}>{getItems(items)}</dl>;
+};
+
+export default DescriptionList;