CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-collapsible

React component to wrap content in Collapsible element with trigger to open and close.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

events.mddocs/

Event Handling

Comprehensive callback system for responding to state changes and user interactions throughout the collapsible component lifecycle.

Capabilities

Trigger Click Handling

Custom control over trigger click behavior and state management.

/**
 * Custom handler for trigger click events
 * When provided, overrides default open/close behavior
 * Receives accordionPosition if specified
 * @param accordionPosition - The accordionPosition prop value if provided
 */
handleTriggerClick?: (accordionPosition?: string | number) => void;

Usage Examples:

// Basic controlled usage
const [isOpen, setIsOpen] = useState(false);

<Collapsible
  trigger="Custom click handler"
  open={isOpen}
  handleTriggerClick={() => {
    console.log('Trigger clicked!');
    setIsOpen(!isOpen);
  }}
>
  <p>Controlled content</p>
</Collapsible>

// Accordion pattern with position tracking
const [openSections, setOpenSections] = useState(new Set());

const handleAccordionClick = (position) => {
  const newOpenSections = new Set(openSections);
  if (newOpenSections.has(position)) {
    newOpenSections.delete(position);
  } else {
    newOpenSections.add(position);
  }
  setOpenSections(newOpenSections);
};

{items.map((item, index) => (
  <Collapsible
    key={item.id}
    trigger={item.title}
    open={openSections.has(index)}
    accordionPosition={index}
    handleTriggerClick={handleAccordionClick}
  >
    <div>{item.content}</div>
  </Collapsible>
))}

Animation Lifecycle Events

Events that fire at different stages of the opening and closing animations.

/**
 * Called when the opening animation begins
 * Fires immediately when user clicks to open or open prop becomes true
 */
onOpening?: () => void;

/**
 * Called when the opening animation completes
 * Fires after the component is fully expanded and animation finished
 */
onOpen?: () => void;

/**
 * Called when the closing animation begins
 * Fires immediately when user clicks to close or open prop becomes false
 */
onClosing?: () => void;

/**
 * Called when the closing animation completes
 * Fires after the component is fully collapsed and animation finished
 */
onClose?: () => void;

Animation Timeline:

  1. User clicks trigger or open prop changes
  2. onOpening or onClosing fires immediately
  3. CSS animation runs (duration: transitionTime or transitionCloseTime)
  4. onOpen or onClose fires when animation completes

Usage Examples:

// Track animation state
const [animationState, setAnimationState] = useState('closed');

<Collapsible
  trigger="Animation tracking"
  onOpening={() => {
    console.log('Starting to open...');
    setAnimationState('opening');
  }}
  onOpen={() => {
    console.log('Fully opened!');
    setAnimationState('open');
  }}
  onClosing={() => {
    console.log('Starting to close...');
    setAnimationState('closing');
  }}
  onClose={() => {
    console.log('Fully closed!');
    setAnimationState('closed');
  }}
>
  <div>Animation state: {animationState}</div>
</Collapsible>

// Analytics tracking
<Collapsible
  trigger="Tracked content"
  onOpen={() => {
    analytics.track('collapsible_opened', {
      section: 'faq-section-1'
    });
  }}
  onClose={() => {
    analytics.track('collapsible_closed', {
      section: 'faq-section-1'
    });
  }}
>
  <p>Content with analytics tracking</p>
</Collapsible>

Trigger-Specific Events

Events that fire specifically in response to user clicking the trigger element.

/**
 * Called when user clicks trigger to open the collapsible
 * Only fires for actual user clicks, not programmatic state changes
 */
onTriggerOpening?: () => void;

/**
 * Called when user clicks trigger to close the collapsible
 * Only fires for actual user clicks, not programmatic state changes
 */
onTriggerClosing?: () => void;

Difference from Animation Events:

  • Animation events (onOpening, onClosing) fire for both user clicks AND programmatic open prop changes
  • Trigger events (onTriggerOpening, onTriggerClosing) fire ONLY when user actually clicks the trigger

Usage Examples:

// Distinguish between user actions and programmatic changes
const [userInteractions, setUserInteractions] = useState(0);

<Collapsible
  trigger="User interaction tracking"
  onOpening={() => console.log('Opening (any cause)')}
  onTriggerOpening={() => {
    console.log('User clicked to open');
    setUserInteractions(prev => prev + 1);
  }}
  onClosing={() => console.log('Closing (any cause)')}
  onTriggerClosing={() => {
    console.log('User clicked to close');
    setUserInteractions(prev => prev + 1);
  }}
>
  <p>User interactions: {userInteractions}</p>
</Collapsible>

// Auto-close with user action detection
const [autoCloseTimer, setAutoCloseTimer] = useState(null);

<Collapsible
  trigger="Auto-closing content"
  onTriggerOpening={() => {
    // Clear any existing auto-close timer when user opens
    if (autoCloseTimer) {
      clearTimeout(autoCloseTimer);
      setAutoCloseTimer(null);
    }
  }}
  onOpen={() => {
    // Set auto-close timer after opening completes
    const timer = setTimeout(() => {
      // Programmatically close (won't trigger onTriggerClosing)
      setIsOpen(false);
    }, 5000);
    setAutoCloseTimer(timer);
  }}
>
  <p>This will auto-close in 5 seconds unless you interact with it</p>
</Collapsible>

Event Combinations and Patterns

Complete Event Flow Example

const EventLogger = () => {
  const [logs, setLogs] = useState([]);
  const [isOpen, setIsOpen] = useState(false);
  
  const addLog = (message) => {
    setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`]);
  };

  return (
    <div>
      <Collapsible
        trigger="Complete event logging"
        open={isOpen}
        handleTriggerClick={() => {
          addLog('handleTriggerClick called');
          setIsOpen(!isOpen);
        }}
        onTriggerOpening={() => addLog('onTriggerOpening: User clicked to open')}
        onOpening={() => addLog('onOpening: Opening animation started')}
        onOpen={() => addLog('onOpen: Opening animation completed')}
        onTriggerClosing={() => addLog('onTriggerClosing: User clicked to close')}
        onClosing={() => addLog('onClosing: Closing animation started')}
        onClose={() => addLog('onClose: Closing animation completed')}
        transitionTime={1000} // Longer animation to see the sequence
      >
        <div>
          <h4>Event Log:</h4>
          <ul>
            {logs.map((log, index) => (
              <li key={index}>{log}</li>
            ))}
          </ul>
        </div>
      </Collapsible>
      
      <button onClick={() => setIsOpen(!isOpen)}>
        Programmatic Toggle (won't trigger onTrigger* events)
      </button>
    </div>
  );
};

Accordion with Event Coordination

const AccordionWithEvents = () => {
  const [openItems, setOpenItems] = useState(new Set());
  const [lastAction, setLastAction] = useState('');

  const handleItemClick = (position) => {
    const newOpenItems = new Set(openItems);
    const wasOpen = newOpenItems.has(position);
    
    if (wasOpen) {
      newOpenItems.delete(position);
      setLastAction(`Closed item ${position}`);
    } else {
      newOpenItems.add(position);
      setLastAction(`Opened item ${position}`);
    }
    
    setOpenItems(newOpenItems);
  };

  const items = [
    { title: 'Section 1', content: 'Content for section 1' },
    { title: 'Section 2', content: 'Content for section 2' },
    { title: 'Section 3', content: 'Content for section 3' }
  ];

  return (
    <div>
      <p>Last action: {lastAction}</p>
      {items.map((item, index) => (
        <Collapsible
          key={index}
          trigger={item.title}
          open={openItems.has(index)}
          accordionPosition={index}
          handleTriggerClick={handleItemClick}
          onOpen={() => console.log(`Section ${index} finished opening`)}
          onClose={() => console.log(`Section ${index} finished closing`)}
        >
          <div>{item.content}</div>
        </Collapsible>
      ))}
    </div>
  );
};

Install with Tessl CLI

npx tessl i tessl/npm-react-collapsible

docs

animation.md

configuration.md

events.md

index.md

tile.json