Building Reusable UI Elements with Vue Compound Components
Compound components are a powerful pattern in Vue.js for creating flexible and reusable UI elements. They allow you to break down complex components into smaller, manageable pieces that can be used independently or together. This challenge will test your understanding of how to implement and utilize this pattern effectively.
Problem Description
Your task is to create a set of compound components for a simple accordion interface. An accordion allows users to toggle the visibility of content panels. You need to implement this using Vue's compound component pattern, ensuring that the parent component manages the state (which panel is open) and exposes methods or slots for child components to interact with it.
Key Requirements:
AccordionComponent: This will be the parent component. It should manage the state of which accordion panel is currently open.AccordionItemComponent: This will represent an individual item within the accordion. It should contain a header and a content area.AccordionHeaderComponent: This component will display the clickable header for anAccordionItem. Clicking it should toggle the visibility of its associated content.AccordionContentComponent: This component will display the content of anAccordionItem. Its visibility should be controlled by theAccordioncomponent.- State Management: The
Accordioncomponent must be the sole source of truth for the currently openAccordionItem. - Interactivity: Clicking an
AccordionHeadershould update theAccordion's state, causing the correspondingAccordionContentto show or hide. - Flexibility: The
Accordionshould support multipleAccordionItems. - TypeScript: All components and their props/events must be defined using TypeScript.
Expected Behavior:
- When the
Accordioncomponent is rendered, by default, no items should be open. - Clicking an
AccordionHeadershould open its correspondingAccordionContentand close any previously open content. - Clicking an already open
AccordionHeadershould close its content. - The structure should be easily extensible, allowing you to add more
AccordionItems without modifying the coreAccordionlogic.
Edge Cases to Consider:
- What happens if there are no
AccordionItems within anAccordion? - How should the components handle rapid clicks? (Though for this challenge, basic toggling is sufficient).
Examples
Example 1: Basic Accordion Usage
<!-- Parent Component -->
<template>
<Accordion>
<AccordionItem>
<AccordionHeader>Section 1</AccordionHeader>
<AccordionContent>
Content for section 1.
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionHeader>Section 2</AccordionHeader>
<AccordionContent>
Content for section 2.
</AccordionContent>
</AccordionItem>
</Accordion>
</template>
Expected Rendered Output (Initial State):
[Section 1] (Clickable header)
[Content for section 1] (Hidden)
[Section 2] (Clickable header)
[Content for section 2] (Hidden)
Expected Rendered Output (After clicking "Section 1"):
[Section 1] (Clickable header)
[Content for section 1] (Visible)
[Section 2] (Clickable header)
[Content for section 2] (Hidden)
Explanation: The Accordion component manages which item is active. When "Section 1" header is clicked, the Accordion updates its internal state to mark "Section 1" as active. This state change then tells the AccordionContent for "Section 1" to become visible and the AccordionContent for "Section 2" to remain hidden (or become hidden if it was previously visible).
Example 2: Accordion with Single Item
<!-- Parent Component -->
<template>
<Accordion>
<AccordionItem>
<AccordionHeader>Just One Item</AccordionHeader>
<AccordionContent>
This is the only content.
</AccordionContent>
</AccordionItem>
</Accordion>
</template>
Expected Rendered Output (Initial State):
[Just One Item] (Clickable header)
[This is the only content.] (Hidden)
Expected Rendered Output (After clicking "Just One Item"):
[Just One Item] (Clickable header)
[This is the only content.] (Visible)
Explanation: Even with a single item, the compound component pattern holds. The Accordion manages the state, and the AccordionHeader interacts with it to toggle the AccordionContent.
Constraints
- Component Structure: You must adhere to the compound component structure using
provide/injector similar Vue patterns for inter-component communication. - State Management: The
Accordioncomponent must manage the state of the currently open item. Child components should not manage this state directly. - TypeScript Usage: All props, emits, and component definitions must be typed using TypeScript.
- No Third-Party Libraries: For the core accordion logic, do not use any external UI libraries or state management solutions.
Notes
- Think about how the
Accordioncomponent can communicate with its children about which item is currently active.provideandinjectis a common and effective way to achieve this in Vue 3. - Consider how
AccordionItemwill know if it's the active item to conditionally render itsAccordionContent. - The
AccordionHeaderwill need a way to signal to theAccordionthat it has been clicked. - You might want to consider using a unique identifier for each
AccordionItemto track its state within the parentAccordion.