|
1 | 1 | /** |
2 | | - * Copyright IBM Corp. 2016, 2025 |
| 2 | + * Copyright IBM Corp. 2016, 2026 |
3 | 3 | * |
4 | 4 | * This source code is licensed under the Apache-2.0 license found in the |
5 | 5 | * LICENSE file in the root directory of this source tree. |
@@ -514,6 +514,132 @@ describe('Toggletip', () => { |
514 | 514 | expect(onKeyDown).toHaveBeenCalledTimes(1); |
515 | 515 | }); |
516 | 516 | }); |
| 517 | + |
| 518 | + describe('Shadow DOM Support', () => { |
| 519 | + it('should not close when clicking inside the toggletip in Shadow DOM context', async () => { |
| 520 | + const shadowHost = document.createElement('div'); |
| 521 | + document.body.appendChild(shadowHost); |
| 522 | + |
| 523 | + const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); |
| 524 | + |
| 525 | + const shadowContainer = document.createElement('div'); |
| 526 | + shadowRoot.appendChild(shadowContainer); |
| 527 | + |
| 528 | + const { container, getByTestId, getByRole } = render( |
| 529 | + <Toggletip data-testid="toggletip" defaultOpen> |
| 530 | + <ToggletipButton label="Show information">test</ToggletipButton> |
| 531 | + <ToggletipContent> |
| 532 | + <div data-testid="inner-content">Content</div> |
| 533 | + </ToggletipContent> |
| 534 | + </Toggletip>, |
| 535 | + { container: shadowContainer } |
| 536 | + ); |
| 537 | + |
| 538 | + const innerContent = getByTestId('inner-content'); |
| 539 | + const toggletip = getByTestId('toggletip'); |
| 540 | + |
| 541 | + const mockEvent = new MouseEvent('mousedown', { bubbles: true }); |
| 542 | + Object.defineProperty(mockEvent, 'composedPath', { |
| 543 | + value: () => [ |
| 544 | + innerContent, |
| 545 | + toggletip, |
| 546 | + shadowContainer, |
| 547 | + shadowRoot, |
| 548 | + shadowHost, |
| 549 | + document.body, |
| 550 | + ], |
| 551 | + }); |
| 552 | + Object.defineProperty(mockEvent, 'target', { |
| 553 | + value: shadowHost, |
| 554 | + }); |
| 555 | + |
| 556 | + fireEvent(document, mockEvent); |
| 557 | + |
| 558 | + expect(getByRole('button')).toHaveAttribute('aria-expanded', 'true'); |
| 559 | + expect(toggletip).toHaveClass(`${prefix}--toggletip--open`); |
| 560 | + |
| 561 | + document.body.removeChild(shadowHost); |
| 562 | + }); |
| 563 | + |
| 564 | + it('should close when clicking outside the toggletip in Shadow DOM context', async () => { |
| 565 | + const shadowHost = document.createElement('div'); |
| 566 | + document.body.appendChild(shadowHost); |
| 567 | + |
| 568 | + const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); |
| 569 | + |
| 570 | + const shadowContainer = document.createElement('div'); |
| 571 | + shadowRoot.appendChild(shadowContainer); |
| 572 | + |
| 573 | + const { getByTestId, getByRole } = render( |
| 574 | + <Toggletip data-testid="toggletip" defaultOpen> |
| 575 | + <ToggletipButton label="Show information">test</ToggletipButton> |
| 576 | + <ToggletipContent> |
| 577 | + <div data-testid="inner-content">Content</div> |
| 578 | + </ToggletipContent> |
| 579 | + </Toggletip>, |
| 580 | + { container: shadowContainer } |
| 581 | + ); |
| 582 | + |
| 583 | + const toggletip = getByTestId('toggletip'); |
| 584 | + |
| 585 | + const outsideElement = document.createElement('div'); |
| 586 | + document.body.appendChild(outsideElement); |
| 587 | + |
| 588 | + const mockEvent = new MouseEvent('mousedown', { bubbles: true }); |
| 589 | + Object.defineProperty(mockEvent, 'composedPath', { |
| 590 | + value: () => [outsideElement, document.body], |
| 591 | + }); |
| 592 | + Object.defineProperty(mockEvent, 'target', { |
| 593 | + value: outsideElement, |
| 594 | + }); |
| 595 | + |
| 596 | + fireEvent(document, mockEvent); |
| 597 | + |
| 598 | + expect(getByRole('button')).toHaveAttribute('aria-expanded', 'false'); |
| 599 | + expect(toggletip).not.toHaveClass(`${prefix}--toggletip--open`); |
| 600 | + |
| 601 | + document.body.removeChild(outsideElement); |
| 602 | + document.body.removeChild(shadowHost); |
| 603 | + }); |
| 604 | + |
| 605 | + it('should handle clicks when composedPath is not available (fallback to event.target)', async () => { |
| 606 | + const shadowHost = document.createElement('div'); |
| 607 | + document.body.appendChild(shadowHost); |
| 608 | + |
| 609 | + const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); |
| 610 | + |
| 611 | + const shadowContainer = document.createElement('div'); |
| 612 | + shadowRoot.appendChild(shadowContainer); |
| 613 | + |
| 614 | + const { getByTestId, getByRole } = render( |
| 615 | + <Toggletip data-testid="toggletip" defaultOpen> |
| 616 | + <ToggletipButton label="Show information">test</ToggletipButton> |
| 617 | + <ToggletipContent> |
| 618 | + <div data-testid="inner-content">Content</div> |
| 619 | + </ToggletipContent> |
| 620 | + </Toggletip>, |
| 621 | + { container: shadowContainer } |
| 622 | + ); |
| 623 | + |
| 624 | + const innerContent = getByTestId('inner-content'); |
| 625 | + const toggletip = getByTestId('toggletip'); |
| 626 | + |
| 627 | + const mockEvent = new MouseEvent('mousedown', { bubbles: true }); |
| 628 | + Object.defineProperty(mockEvent, 'composedPath', { |
| 629 | + value: undefined, |
| 630 | + }); |
| 631 | + Object.defineProperty(mockEvent, 'target', { |
| 632 | + value: innerContent, |
| 633 | + }); |
| 634 | + |
| 635 | + fireEvent(document, mockEvent); |
| 636 | + |
| 637 | + expect(getByRole('button')).toHaveAttribute('aria-expanded', 'true'); |
| 638 | + expect(toggletip).toHaveClass(`${prefix}--toggletip--open`); |
| 639 | + |
| 640 | + document.body.removeChild(shadowHost); |
| 641 | + }); |
| 642 | + }); |
517 | 643 | }); |
518 | 644 | }); |
519 | 645 |
|
|
0 commit comments