Skip to content

Test and minor fixes for Accordion, Button, and Progress components#668

Closed
chromium-52 wants to merge 13 commits intorad-ui:mainfrom
chromium-52:test-components
Closed

Test and minor fixes for Accordion, Button, and Progress components#668
chromium-52 wants to merge 13 commits intorad-ui:mainfrom
chromium-52:test-components

Conversation

@chromium-52
Copy link
Copy Markdown
Contributor

@chromium-52 chromium-52 commented Dec 23, 2024

Test and minor fixes for the following three components:

  1. <Accordion />
    a. TypeScript fixes
    b. Fix a bug when an item's title or content is invalid/missing (and add a new function that checks if the given value is a React.ReactNode)
  2. <Button />
  3. <Progress />
    a. Remove unnecessary props

Summary by CodeRabbit

  • New Features

    • Enhanced Accordion component with improved type safety for item properties.
    • Introduced context types for better structure and clarity in Accordion contexts.
    • Simplified props for AccordionContent and AccordionTrigger, improving accessibility.
    • Added comprehensive unit tests for Accordion and Progress components.
    • Introduced new validation utility functions for React nodes.
  • Bug Fixes

    • Improved error handling in the Accordion component for invalid item types.
  • Documentation

    • Updated test cases for Progress component to ensure correct functionality under various conditions.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 23, 2024

Walkthrough

This pull request introduces comprehensive improvements to the Accordion component and related UI components. The changes focus on enhancing type safety, refactoring context management, and improving component rendering logic. Key modifications include updating prop types, introducing context type definitions, removing unnecessary props, and adding validation for React nodes. The changes also include new test suites for the Accordion and Progress components, ensuring robust functionality and error handling.

Changes

File Change Summary
src/components/ui/Accordion/* Major refactoring of Accordion component structure, type safety improvements, and context management
src/components/ui/Progress/* Modifications to Progress component props, added support for children, and new test suite
src/core/types.tsx Added validateReactNode and isReactNode utility functions for React node validation
src/components/ui/Button/tests/Button.test.tsx New comprehensive test suite for Button component
src/components/ui/Progress/tests/Progress.test.tsx New test suite for Progress component

Possibly related PRs

Suggested Labels

automerge

Suggested Reviewers

  • kotAPI

Poem

🐰 Hop, hop, through the code we go,
Accordion's dance, a type-safe show!
Contexts refined, props trimmed with care,
Validation leaps through the React air!
A rabbit's refactor, precise and bright,
Making components shine ever so light! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Comment thread src/core/types.ts Outdated
@@ -0,0 +1,13 @@
import React from 'react';

export const isReactNode = (value: any): value is React.ReactNode => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what will this be used for? is it like a util function for us to use and check if its a React node?

consider leaving some comments explaining what it does and how it's expected to be used :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup sounds good! I think what the function does is fairly straightforward given its name and signature (this is also a somewhat common pattern) but its expected usage isn't and I'll document this (I just like to be a little careful with comments because code can change quickly and comments can become outdated :) )

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
src/components/ui/Accordion/contexts/AccordionItemContext.tsx (2)

1-1: Ignore false positive lint warning about 'FocusEvent'.
Although the static analysis flags 'FocusEvent' as unused, it's clearly referenced in line 6. Safe to disregard this particular lint warning.

🧰 Tools
🪛 GitHub Check: lint

[warning] 1-1:
'FocusEvent' is defined but never used


11-17: Default no-op handlers may hide errors.
Empty arrow functions work as fallbacks, but they can make debugging harder if an expected handler is never set. Consider logging a warning or throwing an error in development to avoid silent failures.

src/components/ui/Progress/fragments/ProgressRoot.tsx (1)

9-9: Ensure children are valid nodes if needed.
While typically safe, consider optionally leveraging the new isReactNode utility if there's a risk of receiving non-React content here.

src/components/ui/Accordion/fragments/AccordionItem.tsx (3)

1-1: Consider verifying the linter’s warning about FocusEvent
Your usage of FocusEvent as a type parameter in handleBlurEvent should be recognized by the compiler. If the linter continues to complain about "defined but never used," you could consider aliasing FocusEvent to _FocusEvent or removing the import if it’s truly unnecessary.

🧰 Tools
🪛 GitHub Check: lint

[warning] 1-1:
'FocusEvent' is defined but never used


13-14: Reassess necessity of 'null' in itemValue
Since 'value' is required as a number, retaining a 'null' possibility in state may be unnecessary unless there's a valid reason to reset itemValue to null. If so, consider documenting that case.


42-42: Event parameter is currently unused
handleBlurEvent doesn’t reference the event object. If the event isn’t needed, omit it or prefix it with an underscore to silence lint warnings and clarify intent.

src/components/ui/Accordion/tests/Accordion.test.tsx (1)

5-5: AccordionProps lint warning likely a false positive
The type is used at line 7 for defining items. If the linter continues to flag it, consider referencing the type more directly (e.g., in a function signature) or disabling the misleading rule.

🧰 Tools
🪛 GitHub Check: lint

[warning] 5-5:
'AccordionProps' is defined but never used

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5d0d43 and 5a63ee0.

📒 Files selected for processing (14)
  • src/components/ui/Accordion/Accordion.tsx (2 hunks)
  • src/components/ui/Accordion/contexts/AccordionContext.tsx (1 hunks)
  • src/components/ui/Accordion/contexts/AccordionItemContext.tsx (1 hunks)
  • src/components/ui/Accordion/fragments/AccordionContent.tsx (1 hunks)
  • src/components/ui/Accordion/fragments/AccordionItem.tsx (2 hunks)
  • src/components/ui/Accordion/fragments/AccordionRoot.tsx (4 hunks)
  • src/components/ui/Accordion/fragments/AccordionTrigger.tsx (2 hunks)
  • src/components/ui/Accordion/tests/Accordion.test.tsx (1 hunks)
  • src/components/ui/Button/tests/Button.test.tsx (1 hunks)
  • src/components/ui/Progress/Progress.tsx (1 hunks)
  • src/components/ui/Progress/fragments/ProgressIndicator.tsx (1 hunks)
  • src/components/ui/Progress/fragments/ProgressRoot.tsx (1 hunks)
  • src/components/ui/Progress/tests/Progress.test.tsx (1 hunks)
  • src/core/types.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: lint
src/components/ui/Accordion/contexts/AccordionItemContext.tsx

[warning] 1-1:
'FocusEvent' is defined but never used

src/components/ui/Accordion/tests/Accordion.test.tsx

[warning] 5-5:
'AccordionProps' is defined but never used

src/components/ui/Accordion/fragments/AccordionItem.tsx

[warning] 1-1:
'FocusEvent' is defined but never used

🔇 Additional comments (47)
src/core/types.ts (1)

3-13: Well-structured type guard for ReactNode.
The recursive check for array elements is particularly handy. This robust approach ensures reliable detection of any valid React node. Good job!

src/components/ui/Accordion/contexts/AccordionItemContext.tsx (1)

3-9: Concise but explicit context type definition.
Defining the context shape with distinct handler signatures is a strong move for maintainability and clarity.

src/components/ui/Accordion/contexts/AccordionContext.tsx (2)

3-11: Accurate, self-documenting type structure.
Separating these properties into a typed context fosters clarity in usage throughout the Accordion. Great for readability.


13-21: Set-based default values are a good fallback.
Your default context reduces the risk of runtime errors in unwrapped components. This also helps reveal places where no provider is used inadvertently.

src/components/ui/Progress/fragments/ProgressRoot.tsx (1)

2-2: Nice addition of PropsWithChildren.
Explicitly extending the props for children clarifies that this component can wrap nested elements.

src/components/ui/Progress/Progress.tsx (2)

1-1: Removing unused imports looks clean!
This change decreases import overhead.


8-8: Confirm removal of children support.
By removing the inheritance from PropsWithChildren, this component no longer supports children directly. Ensure that this change aligns with your design requirements before merging.

src/components/ui/Accordion/fragments/AccordionContent.tsx (4)

8-8: Props streamlined.
The removal of index-related props simplifies the API. Good move for making the component more focused.


11-11: Use of default className is consistent.
The default value for className helps ensure consistent styling.


19-19: Improved accessibility ID reference.
Using content-${itemValue} ensures uniqueness and better aligns with item context.


21-21: Aria label alignment with item value.
Binding the aria-labelledby to section-${itemValue} is a clean approach for accessibility, correlating labels with content seamlessly.

src/components/ui/Progress/fragments/ProgressIndicator.tsx (2)

3-3: Importing COMPONENT_NAME centralizes naming.
This avoids hardcoding the component name in multiple places.


8-10: Refined IndicatorProps.
The interface no longer extends the ProgressProps, keeping the contract simpler and more component-specific.

src/components/ui/Accordion/Accordion.tsx (5)

7-7: Utility import isReactNode.
This is a great approach for validating React nodes and preventing rendering issues.


10-10: Stricter type for items.
Enforcing {title: string, content: React.ReactNode} ensures strong prop validation.


19-19: Custom trigger child.
This refactor ensures the displayed title is either a valid React node or an error message, improving resilience.


20-20: Check for valid string.
Since title is typed as string, consider removing the isReactNode check or re-check the type. If it can truly be a React node, the type annotation may need to be broader.


24-24: Check for valid content node.
This fallback helps avoid runtime crashes if any non-React node is passed. Good defensive programming.

src/components/ui/Progress/tests/Progress.test.tsx (6)

1-2: Imports look good.
The import statements correctly bring in React and testing-library utilities. No issues here.


4-4: Importing Progress component is straightforward.
The relative path import seems correct and consistent with the folder structure.


6-10: Good basic render test.
Testing that the component renders with a role of “progressbar” ensures basic accessibility coverage.


12-18: Effective clamping checks.
These tests properly verify the clamping behavior for values outside 0–100. Very thorough approach.


20-23: Validation of inverted minValue/maxValue scenario.
This test confirms that the component gracefully defaults to 0 when minValue > maxValue, adhering to robust error handling.


25-31: Re-render coverage.
Verifying that the component updates aria-valuenow upon prop changes is essential for dynamic scenarios. Great job covering this.

src/components/ui/Button/tests/Button.test.tsx (8)

1-3: Imports are standard.
Everything is correctly set up for React and testing-library usage, plus userEvent for interactive testing.


5-5: Button import is consistent.
No issues with importing the Button component.


7-11: Basic rendering test.
Ensures the Button displays child content. Nicely confirms the presence of the expected text.


13-17: Type attribute test.
Verifies the ‘type’ attribute is correctly mapped on the button. Good coverage for form usage.


19-23: Accent color attribute coverage.
Testing the data-accent-color attribute is a nice detail for theming checks.


25-29: Variant styling verification.
Ensuring the correct variant class is rendered helps maintain UI consistency.


31-35: Size API test.
Confirms the data-size attribute is accepted and correctly applied to the Button.


37-45: Click handler test.
Good coverage ensuring user interaction triggers the click function exactly once.

src/components/ui/Accordion/fragments/AccordionTrigger.tsx (1)

Line range hint 11-49: Transparent and accessible approach using itemValue.
Switching from index-based controls to itemValue-based logic improves clarity and accessibility. The code correctly toggles active states, updates context, and sets the aria attributes. Make sure itemValue is always defined in upstream data.

If desired, you can confirm usage by scanning all references to itemValue to ensure it’s never undefined:

✅ Verification successful

Let me analyze the usage of itemValue further by checking how it's initialized and passed down.


itemValue is properly initialized and managed throughout the accordion components

The verification confirms that itemValue is well-implemented:

  • It's initialized from a required value prop in AccordionItem (passed as index from Accordion)
  • It's properly typed as number | null in the context
  • It's consistently used in AccordionTrigger and AccordionContent for aria attributes
  • The default context value safely handles the null case
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Searching for itemValue references for verification
rg -A 2 'itemValue'

Length of output: 3827


Script:

#!/bin/bash
# Check how value prop is passed to AccordionItem
ast-grep --pattern 'const AccordionItem: React.FC<$_> = ({ value, $$ }) => {'

# Check for AccordionItem usage to see how value is passed
rg -A 2 '<AccordionItem'

Length of output: 1456

src/components/ui/Accordion/fragments/AccordionRoot.tsx (6)

1-1: Removed unused imports.
The removal of useEffect and sticks to basic React hooks is tidy.


15-15: Explicit ref typing.
Using useRef<HTMLDivElement | null>(null) clarifies the type and helps prevent accidental misuse.


18-19: State type clarity.
Defining activeItem as number | null and focusItem as Element | null is a best practice, preventing inadvertent type mismatches.


22-25: Null checks on accordionRef.
Running these checks early is crucial to avoid runtime errors if the ref is detached. Excellent defensive coding.


37-40: Consistent null checks for focusPrevItem.
Good consistency with focusNextItem to ensure safe DOM interactions.


60-60: AccordionContext provider property usage.
All relevant props (activeItem, setActiveItem, focusNextItem, focusPrevItem, etc.) are correctly exposed, supporting robust consumption by child components.

Also applies to: 62-62

src/components/ui/Accordion/fragments/AccordionItem.tsx (1)

8-8: Ensure all consumers provide a valid numeric value
Now that 'value' is required and typed as number, confirm that any usage of AccordionItem in the codebase guarantees a valid number. Otherwise, consider adding a fallback or default value.

src/components/ui/Accordion/tests/Accordion.test.tsx (7)

1-4: Imports are appropriate for testing
All necessary libraries for rendering and user interaction are correctly included.


7-11: Comprehensive test data
Providing multiple items with varied content ensures robust coverage for expansions and toggles.


13-18: Empty items scenario test
Verifying that the accordion renders correctly with zero items helps ensure stability.


19-27: Initial hidden content check
Ensures that all panels start closed by default, aligning with typical accordion behavior.


29-40: Toggle behavior validation
Verifies correct show/hide functionality when clicking the same header twice, confirming proper accordion state management.


42-56: Single-open rule test
Checks that opening a new item closes the previously open one, confirming the intended "one open at a time" design.


58-72: Graceful handling of invalid props
The tests confirm that invalid headers/content produce warning messages, reinforcing the reliability of your prop validation logic.

Comment thread src/components/ui/Accordion/Accordion.tsx
Comment thread src/components/ui/Accordion/tests/Accordion.test.tsx
Comment thread src/components/ui/Button/tests/Button.test.tsx Outdated
Comment thread src/components/ui/Progress/tests/Progress.test.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/core/types.tsx (3)

3-10: Use consistent docstring format for clarity.

While the doc comments are thorough, it's helpful to adopt a consistent docstring style (e.g., JSDoc/TSDoc) to standardize documentation across the codebase.


11-18: Question the choice of returning false.

Returning false when encountering an invalid node may obscure logical flow, especially if other callers of this function expect a React node or null. Consider returning null or throwing a more descriptive error, so it’s clearer that rendering is intentionally halted.


23-33: Ensure array validation performance remains acceptable.

Recursively validating large arrays of nodes might be expensive. Keep an eye on performance if the function is called frequently with large data structures. You may want to memoize or short-circuit checks when you encounter an invalid member.

src/components/ui/Progress/tests/Progress.test.tsx (1)

25-31: Home in on dynamic binding.

These tests confirm that the component properly updates when props change, reflecting a reactive UI design. Consider adding random or boundary value updates to broaden coverage.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a63ee0 and 0ae19d6.

📒 Files selected for processing (5)
  • src/components/ui/Accordion/Accordion.tsx (2 hunks)
  • src/components/ui/Accordion/tests/Accordion.test.tsx (1 hunks)
  • src/components/ui/Button/tests/Button.test.tsx (1 hunks)
  • src/components/ui/Progress/tests/Progress.test.tsx (1 hunks)
  • src/core/types.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/ui/Accordion/Accordion.tsx
  • src/components/ui/Button/tests/Button.test.tsx
🧰 Additional context used
🪛 GitHub Check: lint
src/components/ui/Accordion/tests/Accordion.test.tsx

[warning] 5-5:
'AccordionProps' is defined but never used

🔇 Additional comments (9)
src/components/ui/Progress/tests/Progress.test.tsx (3)

6-10: Good coverage of the basic render test.

Tests for the initial rendering and accessibility attributes provide confidence. Including more negative path testing, such as missing or invalid props, could further bolster reliability.


12-19: Commendable test for value clamping.

Verifying that the component clamps the value to valid bounds ensures correctness, preventing out-of-range states. Great job!


20-23: Edge-case coverage is beneficial.

Testing scenarios where minValue > maxValue ensures the component handles unexpected configurations gracefully. This increases robustness.

src/components/ui/Accordion/tests/Accordion.test.tsx (6)

7-11: Types help clarify prop structure.

Using AccordionProps['items'] is a promising approach for ensuring type-safety. If the linter complains that AccordionProps is unused, that may be a false positive, so you can safely ignore it unless it blocks builds.


13-17: Empty items scenario is well-tested.

Validating that the accordion renders even with an empty array ensures no unexpected crashes or rendering issues.


19-27: Initial state checks add robustness.

Ensuring that all content is hidden initially aligns with an intuitive default behavior for accordions.


29-40: User interaction test is effective.

Simulating clicks on headers to toggle visibility confirms correct interactive behavior. Great coverage of state changes.


42-56: Multiple open sections scenario.

Testing how the accordion handles transitions between open headers is essential for multi-item UIs. This approach comprehensively validates user expectations.


58-78: Good error handling tests.

The checks for invalid types and missing properties effectively confirm that console errors are triggered and that the component adapts gracefully. This ensures robustness against improper usage.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/core/types.tsx (3)

9-9: Fix incorrect JSDoc return description

The JSDoc states it returns "false" for invalid nodes, but the function actually returns null.

-* @returns value if it's a valid React Node and false otherwise
+* @returns value if it's a valid React Node and null otherwise

11-11: Consider using unknown instead of any for better type safety

Using any bypasses TypeScript's type checking. Using unknown would force explicit type checking while maintaining flexibility.

-export const validateReactNode = (value: any, name?: string): React.ReactNode => {
+export const validateReactNode = (value: unknown, name?: string): React.ReactNode => {

16-16: Improve error handling strategy

Using console.error in production code could leak implementation details. Consider:

  1. Using a proper logging service
  2. Making error handling configurable
  3. Adding error boundaries for React components
-    console.error(`${name || value} is not a valid React node`);
+    if (process.env.NODE_ENV !== 'production') {
+        console.error(`${name || value} is not a valid React node`);
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ae19d6 and f111c61.

📒 Files selected for processing (1)
  • src/core/types.tsx (1 hunks)
🔇 Additional comments (2)
src/core/types.tsx (2)

1-1: LGTM!

The React import is necessary and correctly implemented.


23-33: Enhance isReactNode implementation for completeness and performance

The implementation could be improved in several ways:

  1. Add support for object type checking (valid ReactNode can be an object)
  2. Optimize performance by checking array last (most expensive operation)
  3. Add unit tests for various scenarios
 const isReactNode = (value: any): value is React.ReactNode => {
     return (
         typeof value === 'string' ||
         typeof value === 'number' ||
         typeof value === 'boolean' ||
         value === null ||
         value === undefined ||
-        React.isValidElement(value) ||
-        (Array.isArray(value) && value.every(isReactNode))
+        React.isValidElement(value) ||
+        (typeof value === 'object' && !Array.isArray(value) && value !== null) ||
+        (Array.isArray(value) && value.every(isReactNode))
     );
 };

Let's verify the test coverage:

@kotAPI
Copy link
Copy Markdown
Collaborator

kotAPI commented Jan 1, 2025

@chromium-52 will you be working on this? seems to be getting stale 😅

@chromium-52
Copy link
Copy Markdown
Contributor Author

@kotAPI oh sorry forgot to mention that this PR is ready for review 😅. I started resolving the conflicts but noticed that for the Accordion component, there are conflicts in the code but more importantly in the logic as well. Since the accordion accounts for most of the changes, I think it'll be better if I just close this PR and open a new one with just the changes for the Button and Progress components. Will do this today or tomorrow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants