diff --git a/components/Accordion/Accordion.stories.tsx b/components/Accordion/Accordion.stories.tsx new file mode 100644 index 000000000000..95bbed4b5ed7 --- /dev/null +++ b/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,35 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import type { AccordionItemType } from '@/types/components/AccordionItemType'; + +import Accordion from './Accordion'; + +const meta: Meta = { + title: 'Components/Accordion', + component: Accordion +}; + +export default meta; + +type Story = StoryObj; + +const sampleAccordionItemList: AccordionItemType[] = [ + { + title: 'Accordion Item 1', + content: 'This is the content of accordion item 1.' + }, + { + title: 'Accordion Item 2', + content: 'This is the content of accordion item 2.' + }, + { + title: 'Accordion Item 3', + content: 'This is the content of accordion item 3.' + } +]; + +export const SampleAccordion: Story = { + args: { + accordionItems: sampleAccordionItemList + } +}; diff --git a/components/Accordion/Accordion.tsx b/components/Accordion/Accordion.tsx new file mode 100644 index 000000000000..aa5fbf536fb5 --- /dev/null +++ b/components/Accordion/Accordion.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import type { AccordionItemType } from '@/types/components/AccordionItemType'; + +import AccordionItem from './AccordionItem'; + +export interface AccordionProps { + // eslint-disable-next-line prettier/prettier + + /** List of accordian items objects each containing title and content. */ + accordionItems: AccordionItemType[]; +} + +/** + * This is the Accordion component. It displays a list of items that can be expanded or collapsed. + */ +export default function Accordion({ accordionItems = [] }: AccordionProps) { + const [activeIndex, setActiveIndex] = React.useState(null); + + return ( +
+ {accordionItems.map(({ title, content }, index) => ( + + ))} +
+ ); +} diff --git a/components/Accordion/AccordionItem.tsx b/components/Accordion/AccordionItem.tsx new file mode 100644 index 000000000000..b7c1f61d8296 --- /dev/null +++ b/components/Accordion/AccordionItem.tsx @@ -0,0 +1,67 @@ +import React from 'react'; + +export interface AccordionItemProps { + // eslint-disable-next-line prettier/prettier + + /** Index of the accordion item. */ + itemIndex: number; + + /** Title of the accordion item. */ + title: string; + + /** Content of the accordion item. */ + content: React.ReactNode; + + /** Whether the accordion item is active(open) or not. */ + isActive: boolean; + + /** Function to set the active index of the accordion item. */ + setActiveIndex: (index: number | null) => void; +} + +/** + * This is the AccordionItem component. It displays a single item that can be expanded or collapsed. + */ +export default function AccordionItem({ itemIndex, title, content, isActive, setActiveIndex }: AccordionItemProps) { + const handleClick = () => { + const nextIndex = isActive ? null : itemIndex; + + setActiveIndex(nextIndex); + }; + + return ( +
+ + {isActive && ( +
+ {content} +
+ )} +
+ ); +} diff --git a/components/MDX/MDX.tsx b/components/MDX/MDX.tsx index 36cddf7aa6a0..747345045045 100644 --- a/components/MDX/MDX.tsx +++ b/components/MDX/MDX.tsx @@ -29,6 +29,7 @@ import Caption from '../Caption'; import DocsCards from '../docs/DocsCards'; import Visualizer from '../docs/Visualizer'; import CodeBlock from '../editor/CodeBlock'; +import FAQ from '../faq/FAQ'; import Figure from '../Figure'; import GeneratorInstallation from '../GeneratorInstallation'; import Column from '../layout/Column'; @@ -308,6 +309,7 @@ export function getMDXComponents() { Text, Warning, Sponsors, + FAQ, Caption, Row, Column, diff --git a/components/faq/FAQ.tsx b/components/faq/FAQ.tsx new file mode 100644 index 000000000000..2bf8cf6a3368 --- /dev/null +++ b/components/faq/FAQ.tsx @@ -0,0 +1,10 @@ +import Accordion from '@/components/Accordion/Accordion'; + +import { faqList } from './FAQList'; + +/** + * This is the FAQ component. It tells the user about the frequently asked questions. It has been created to render FAQ list inside MDX file. + */ +export default function FAQ() { + return ; +} diff --git a/components/faq/FAQList.tsx b/components/faq/FAQList.tsx new file mode 100644 index 000000000000..eb9e3d90a1d6 --- /dev/null +++ b/components/faq/FAQList.tsx @@ -0,0 +1,88 @@ +import Link from 'next/link'; +import React from 'react'; + +import type { AccordionItemType as FAQ } from '@/types/components/AccordionItemType'; + +/** + * This is the FAQ list. It contains the frequently asked questions and their answers. + */ +export const faqList: FAQ[] = [ + { + title: 'What is the goal of the project?', + content:
To make asynchronous APIs as successful and mature as REST APIs.
+ }, + { + title: 'What protocols does it support?', + content: ( +
+ AsyncAPI is protocol - agnostic, so you can use it for APIs that work over any protocol(e.g., AMQP, MQTT, + WebSockets, Kafka, STOMP, HTTP, Mercure, etc). For more information, refer to the{' '} + + AsyncAPI specification documentation + + . +
+ ) + }, + { + title: 'Who are the users of AsyncAPI?', + content: ( +
+

+ AsyncAPI users are those who implement and maintain event - driven architecture. For example, people that + write backend API using WebSocket, or people that maintain communication between their microservices using + Kafka. +

+
+ ) + }, + { + title: 'What is the AsyncAPI Community?', + content: ( +
+ It’s the core of the initiative. The AsyncAPI community contributes to the development of the tool, it promotes + access and distribution of the specification allowing freedom of use, study, copying, modification, and + redistribution to anyone who wishes to do so. The cooperation between these people in all areas of software + production generates a substantial improvement in the quality of the software, as well as greater dissemination + and sustainability over time, and prioritizing the benefit of society over any other. +
+ ) + }, + { + title: 'Who can use it?', + content: ( +
+ Anyone. All projects under AsyncAPI Initiative are part of the Linux Foundation, licensed under the Apache 2.0 + license. It’s open to use and contribution. +
+ ) + }, + { + title: 'Where can I find more information?', + content: ( +
+ +
+ ) + } +]; diff --git a/components/navigation/BlogPostCard.stories.tsx b/components/navigation/BlogPostCard.stories.tsx new file mode 100644 index 000000000000..47d57094fa11 --- /dev/null +++ b/components/navigation/BlogPostCard.stories.tsx @@ -0,0 +1,74 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import BlogPostItem from './BlogPostItem'; + +const meta: Meta = { + title: 'Components/BlogPostCard', + component: BlogPostItem, + decorators: [ + (Story) => ( +
+ +
+ ) + ] +}; + +export default meta; + +type Story = StoryObj; + +export const BlogPostCard: Story = { + args: { + post: { + title: 'Blog Post Title', + date: '2024-07-28T06:30:00.000Z', + type: 'Blog Category', + tags: ['Tag 1', 'Tag 2'], + cover: '/img/posts/release-notes-3.0.0/cover.webp', + authors: [ + { + name: 'Author Name', + photo: '/favicon-194x194.png', + link: 'https://x.com/AsyncAPISpec', + byline: 'Author Byline' + } + ], + excerpt: 'This is a blog post excerpt.', + toc: [ + { + content: 'Table of Content 1', + slug: 'table-of-content-1', + lvl: 1, + i: 0, + seen: 0 + }, + { + content: 'Table of Content 2', + slug: 'table-of-content-2', + lvl: 2, + i: 1, + seen: 0 + }, + { + content: 'Table of Content 3', + slug: 'table-of-content-3', + lvl: 2, + i: 2, + seen: 0 + } + ], + readingTime: 22, + sectionSlug: '/blog-section-slug', + sectionWeight: 0, + id: 'pages/blog/blog-post-slug', + isIndex: false, + slug: '/blog-post-slug', + featured: false + } + } +}; diff --git a/components/navigation/BlogPostItem.tsx b/components/navigation/BlogPostItem.tsx index 9a2fb20076a1..09559f005238 100644 --- a/components/navigation/BlogPostItem.tsx +++ b/components/navigation/BlogPostItem.tsx @@ -14,22 +14,24 @@ import Heading from '../typography/Heading'; import Paragraph from '../typography/Paragraph'; interface BlogPostItemProps { + // eslint-disable-next-line prettier/prettier + + /** The blog post data. */ post: IBlogPost; + + /** Additional CSS classes for styling. */ className?: string; + + /** The HTML id attribute for the component. */ id?: string; } /** - * @description Functional component representing a single blog post item. - * @param {Object} props - Props for the BlogPostItem component. - * @param {IBlogPost} props.post - The blog post data. - * @param {string} [props.className=''] - Additional CSS classes for styling. - * @param {string} [props.id=''] - The HTML id attribute for the component. - * @param {Ref} ref - Reference object for the component. + * Functional component representing a single blog post item. */ export default forwardRef(function BlogPostItem( { post, className = '', id = '' }: BlogPostItemProps, - ref: Ref + ref: Ref /** Reference object for the component. */ ) { let typeColors: [string, string] = ['bg-indigo-100', 'text-indigo-800']; @@ -50,7 +52,7 @@ export default forwardRef(function BlogPostItem( } return ( -
  • +
  • diff --git a/types/components/AccordionItemType.ts b/types/components/AccordionItemType.ts new file mode 100644 index 000000000000..c32c246366ea --- /dev/null +++ b/types/components/AccordionItemType.ts @@ -0,0 +1,4 @@ +export interface AccordionItemType { + title: string; + content: React.ReactNode; +}