File tree Expand file tree Collapse file tree 2 files changed +69
-0
lines changed Expand file tree Collapse file tree 2 files changed +69
-0
lines changed Original file line number Diff line number Diff line change @@ -633,6 +633,45 @@ class Overlay extends Component {
633
633
}
634
634
}
635
635
636
+ isInShadowDOM ( node ) {
637
+ return node . getRootNode ? node . getRootNode ( ) . nodeType === 11 : false ;
638
+ }
639
+
640
+ getEventPath ( event ) {
641
+ // 参考 https://github.com/spring-media/react-shadow-dom-retarget-events/blob/master/index.js#L29
642
+ return (
643
+ event . path ||
644
+ ( event . composedPath && event . composedPath ( ) ) ||
645
+ this . composedPath ( event . target )
646
+ ) ;
647
+ }
648
+
649
+ composedPath ( el ) {
650
+ const path = [ ] ;
651
+ while ( el ) {
652
+ path . push ( el ) ;
653
+ if ( el . tagName === 'HTML' ) {
654
+ path . push ( document ) ;
655
+ path . push ( window ) ;
656
+ return path ;
657
+ }
658
+ el = el . parentElement ;
659
+ }
660
+ }
661
+
662
+ matchInShadowDOM ( node , e ) {
663
+ if ( this . isInShadowDOM ( node ) ) {
664
+ // Shadow DOM 环境中,触发点击事件,监听 document click 事件获得的事件源
665
+ // 并非实际触发的 dom 节点,而是 Shadow DOM 的 host 节点
666
+ // 进而会导致如 Select 组件的下拉弹层打开后立即关闭等问题
667
+ // 因此额外增加 node 和 eventPath 的判断
668
+ const eventPath = this . getEventPath ( e ) ;
669
+ return node === eventPath [ 0 ] || node . contains ( eventPath [ 0 ] ) ;
670
+ }
671
+
672
+ return false ;
673
+ }
674
+
636
675
handleDocumentClick ( e ) {
637
676
if ( this . state . visible ) {
638
677
const { safeNode } = this . props ;
@@ -649,6 +688,7 @@ class Overlay extends Component {
649
688
node &&
650
689
( node === e . target ||
651
690
node . contains ( e . target ) ||
691
+ this . matchInShadowDOM ( node , e ) ||
652
692
( e . target !== document &&
653
693
! document . documentElement . contains ( e . target ) ) )
654
694
) {
Original file line number Diff line number Diff line change @@ -787,6 +787,35 @@ describe('Popup', () => {
787
787
} ) ;
788
788
} ) ;
789
789
790
+ it ( 'should support render in shadow dom' , ( ) => {
791
+ return co ( function * ( ) {
792
+ const host = document . createElement ( 'div' ) ;
793
+ const shadowRoot = host . attachShadow ( { mode : 'open' } ) ;
794
+ document . body . appendChild ( host ) ;
795
+
796
+ ReactDOM . render ( (
797
+ < Popup trigger = { < button > Open</ button > } triggerType = "click" >
798
+ < span className = "content" > Hello World From Popup!</ span >
799
+ </ Popup >
800
+ ) , shadowRoot ) ;
801
+
802
+ yield delay ( 300 ) ;
803
+ const btn = shadowRoot . querySelector ( 'button' ) ;
804
+ // NOTE: 此处不能使用 ReactTestUtils.Simulate.click(btn);
805
+ btn . click ( ) ;
806
+
807
+ yield delay ( 300 ) ;
808
+ assert ( document . querySelector ( '.next-overlay-wrapper' ) ) ;
809
+
810
+ btn . click ( ) ;
811
+ yield delay ( 300 ) ;
812
+ assert ( ! document . querySelector ( '.next-overlay-wrapper' ) ) ;
813
+
814
+ ReactDOM . unmountComponentAtNode ( shadowRoot ) ;
815
+ document . body . removeChild ( host ) ;
816
+ } ) ;
817
+ } ) ;
818
+
790
819
// https://riddle.alibaba-inc.com/riddles/b58b48a6
791
820
it ( 'should support container return a react component' , ( ) => {
792
821
class App extends React . Component {
You can’t perform that action at this time.
0 commit comments