import React from 'react'; import styled from '@emotion/styled'; import { filterChildren } from './filterChildren'; // Test components const Foo = React.forwardRef( ({ text }, ref) =>
{text}
, ); Foo.displayName = 'Foo'; const Bar = React.forwardRef( ({ text }, ref) =>
{text}
, ); Bar.displayName = 'Bar'; const Baz = React.forwardRef( ({ text }, ref) =>
{text}
, ); Baz.displayName = 'Baz'; // Add static properties to test components with type assertion (Foo as any).isFoo = true; (Bar as any).isBar = true; (Baz as any).isBaz = true; describe('filterChildren', () => { test('should filter out children with matching static properties', () => { const children = ( <>
regular-div
); const filtered = filterChildren(children, ['isFoo', 'isBaz']); const filteredArray = React.Children.toArray(filtered); expect(filteredArray).toHaveLength(2); expect((filteredArray[0] as React.ReactElement).props.text).toBe( 'should-remain', ); expect((filteredArray[1] as React.ReactElement).type).toBe('div'); }); test('should return all children when no properties match', () => { const children = ( <> ); const filtered = filterChildren(children, ['isNonExistent']); const filteredArray = React.Children.toArray(filtered); expect(filteredArray).toHaveLength(2); expect((filteredArray[0] as React.ReactElement).props.text).toBe('first'); expect((filteredArray[1] as React.ReactElement).props.text).toBe('second'); }); test('should handle empty children', () => { const filtered = filterChildren(null, ['isFoo']); expect(filtered).toEqual([]); }); test('should handle array children', () => { const children = [ , , ]; const filtered = filterChildren(children, ['isFoo']); const filteredArray = React.Children.toArray(filtered); expect(filteredArray).toHaveLength(1); expect((filteredArray[0] as React.ReactElement).props.text).toBe('keep-me'); }); test('should filter children inside fragments', () => { const children = ( ); const filtered = filterChildren(children, ['isFoo']); const filteredArray = React.Children.toArray(filtered); // Fragment children get flattened, so we should have 1 Bar component directly expect(filteredArray).toHaveLength(1); expect((filteredArray[0] as React.ReactElement).props.text).toBe( 'keep-from-fragment', ); }); test('should work with styled components from @emotion/styled', () => { // Create a real styled component const StyledFoo = styled(Foo)` background-color: blue; `; const children = ( <> ); const filtered = filterChildren(children, ['isFoo']); const filteredArray = React.Children.toArray(filtered); // Should only have Bar remaining (fragment gets flattened) expect(filteredArray).toHaveLength(1); expect((filteredArray[0] as React.ReactElement).props.text).toBe( 'regular-should-remain', ); }); test('should NOT filter deeply nested components (search depth limitation)', () => { const children = ( <> {/* Nested fragment - should NOT be filtered */} {/* Component inside div - should NOT be filtered */}
{/* Direct Foo - SHOULD be filtered */} ); const filtered = filterChildren(children, ['isFoo']); const filteredArray = React.Children.toArray(filtered); // Should have 3 items: nested fragment content, div with Foo inside, and Bar // (fragments get flattened, so nested fragment becomes its children) expect(filteredArray).toHaveLength(3); // The direct Foo should be filtered out, but nested ones should remain const barChild = filteredArray.find( child => React.isValidElement(child) && (child as React.ReactElement).props.text === 'direct-bar', ); expect(barChild).toBeDefined(); }); test('should handle multiple properties to filter', () => { const children = ( <>
keep-div
); const filtered = filterChildren(children, ['isFoo', 'isBar']); const filteredArray = React.Children.toArray(filtered); expect(filteredArray).toHaveLength(2); expect((filteredArray[0] as React.ReactElement).props.text).toBe( 'keep-baz', ); expect((filteredArray[1] as React.ReactElement).type).toBe('div'); }); });