@@ -87,6 +87,66 @@ describe('MentionsInput', () => {
8787 expect ( input . tagName ) . toBe ( 'INPUT' )
8888 } )
8989
90+ describe ( 'single-line versus multi-line modes' , ( ) => {
91+ it ( 'switches DOM structure and data attributes when toggling modes.' , ( ) => {
92+ const { container, rerender } = render (
93+ < MentionsInput value = "" >
94+ < Mention trigger = "@" data = { data } />
95+ </ MentionsInput >
96+ )
97+
98+ const initialRoot = container . firstElementChild as HTMLElement
99+ expect ( initialRoot ) . toHaveAttribute ( 'data-multi-line' , 'true' )
100+ expect ( initialRoot ) . not . toHaveAttribute ( 'data-single-line' )
101+
102+ let inputElement = container . querySelector ( '[data-slot="input"]' ) as HTMLElement
103+ expect ( inputElement . tagName ) . toBe ( 'TEXTAREA' )
104+ expect ( inputElement ) . toHaveAttribute ( 'data-multi-line' , 'true' )
105+ expect ( inputElement ) . not . toHaveAttribute ( 'data-single-line' )
106+
107+ rerender (
108+ < MentionsInput value = "" singleLine >
109+ < Mention trigger = "@" data = { data } />
110+ </ MentionsInput >
111+ )
112+
113+ const updatedRoot = container . firstElementChild as HTMLElement
114+ expect ( updatedRoot ) . toHaveAttribute ( 'data-single-line' , 'true' )
115+ expect ( updatedRoot ) . not . toHaveAttribute ( 'data-multi-line' )
116+
117+ inputElement = container . querySelector ( '[data-slot="input"]' ) as HTMLElement
118+ expect ( inputElement . tagName ) . toBe ( 'INPUT' )
119+ expect ( inputElement ) . toHaveAttribute ( 'data-single-line' , 'true' )
120+ expect ( inputElement ) . not . toHaveAttribute ( 'data-multi-line' )
121+ } )
122+
123+ it ( 'keeps the highlighter whitespace handling in sync with the singleLine prop.' , ( ) => {
124+ const { container, rerender } = render (
125+ < MentionsInput value = "Hello" >
126+ < Mention trigger = "@" data = { data } />
127+ </ MentionsInput >
128+ )
129+
130+ const control = container . querySelector ( '[data-slot="control"]' ) as HTMLElement
131+ const initialHighlighter = control . firstElementChild as HTMLElement
132+ expect ( initialHighlighter ) . toHaveClass ( 'whitespace-pre-wrap' )
133+ expect ( initialHighlighter ) . toHaveClass ( 'break-words' )
134+ expect ( initialHighlighter ) . not . toHaveClass ( 'break-normal' )
135+
136+ rerender (
137+ < MentionsInput value = "Hello" singleLine >
138+ < Mention trigger = "@" data = { data } />
139+ </ MentionsInput >
140+ )
141+
142+ const updatedControl = container . querySelector ( '[data-slot="control"]' ) as HTMLElement
143+ const updatedHighlighter = updatedControl . firstElementChild as HTMLElement
144+ expect ( updatedHighlighter ) . toHaveClass ( 'whitespace-pre' )
145+ expect ( updatedHighlighter ) . toHaveClass ( 'break-normal' )
146+ expect ( updatedHighlighter ) . not . toHaveClass ( 'whitespace-pre-wrap' )
147+ } )
148+ } )
149+
90150 describe ( 'validation' , ( ) => {
91151 let consoleError : jest . SpyInstance
92152
@@ -661,7 +721,7 @@ describe('MentionsInput', () => {
661721 </ MentionsInput >
662722 )
663723
664- const computed = window . getComputedStyle ( combobox )
724+ const computed = globalThis . getComputedStyle ( combobox )
665725 const borderTop = Number . parseFloat ( computed . borderTopWidth || '0' )
666726 const borderBottom = Number . parseFloat ( computed . borderBottomWidth || '0' )
667727 expect ( Number . parseFloat ( combobox . style . height ) ) . toBe ( scrollHeight + borderTop + borderBottom )
@@ -693,7 +753,7 @@ describe('MentionsInput', () => {
693753 </ MentionsInput >
694754 )
695755
696- const computed = window . getComputedStyle ( combobox )
756+ const computed = globalThis . getComputedStyle ( combobox )
697757 const borderTop = Number . parseFloat ( computed . borderTopWidth || '0' )
698758 const borderBottom = Number . parseFloat ( computed . borderBottomWidth || '0' )
699759 expect ( Number . parseFloat ( combobox . style . height ) ) . toBe ( scrollHeight + borderTop + borderBottom )
@@ -740,7 +800,7 @@ describe('MentionsInput', () => {
740800 } )
741801
742802 await waitFor ( ( ) => {
743- const computed = window . getComputedStyle ( combobox )
803+ const computed = globalThis . getComputedStyle ( combobox )
744804 const borderTop = Number . parseFloat ( computed . borderTopWidth || '0' )
745805 const borderBottom = Number . parseFloat ( computed . borderBottomWidth || '0' )
746806 expect ( Number . parseFloat ( combobox . style . height ) ) . toBe (
@@ -765,7 +825,7 @@ describe('MentionsInput', () => {
765825
766826 it ( 'adds border widths to the measured height' , ( ) => {
767827 const onMentionsChange = jest . fn ( )
768- const getComputedStyleSpy = jest . spyOn ( window , 'getComputedStyle' ) . mockReturnValue ( {
828+ const getComputedStyleSpy = jest . spyOn ( globalThis , 'getComputedStyle' ) . mockReturnValue ( {
769829 borderTopWidth : '4px' ,
770830 borderBottomWidth : '6px' ,
771831 } as unknown as CSSStyleDeclaration )
@@ -824,7 +884,7 @@ describe('MentionsInput', () => {
824884 </ MentionsInput >
825885 )
826886
827- const computed = window . getComputedStyle ( textarea )
887+ const computed = globalThis . getComputedStyle ( textarea )
828888 const borderTop = Number . parseFloat ( computed . borderTopWidth || '0' )
829889 const borderBottom = Number . parseFloat ( computed . borderBottomWidth || '0' )
830890 expect ( Number . parseFloat ( textarea . style . height ) ) . toBe ( scrollHeight + borderTop + borderBottom )
@@ -2032,16 +2092,28 @@ describe('MentionsInput', () => {
20322092 const originalRemove = window . removeEventListener
20332093 const handlers : Partial < Record < string , EventListener > > = { }
20342094 const addListener = jest
2035- . spyOn ( window , 'addEventListener' )
2036- . mockImplementation ( ( type : string , listener : EventListenerOrEventListenerObject , options ?: boolean | AddEventListenerOptions ) => {
2037- handlers [ type ] = listener as EventListener
2038- return originalAdd . call ( window , type , listener , options )
2039- } )
2095+ . spyOn ( globalThis , 'addEventListener' )
2096+ . mockImplementation (
2097+ (
2098+ type : string ,
2099+ listener : EventListenerOrEventListenerObject ,
2100+ options ?: boolean | AddEventListenerOptions
2101+ ) => {
2102+ handlers [ type ] = listener as EventListener
2103+ return originalAdd . call ( globalThis , type , listener , options )
2104+ }
2105+ )
20402106 const removeListener = jest
2041- . spyOn ( window , 'removeEventListener' )
2042- . mockImplementation ( ( type : string , listener : EventListenerOrEventListenerObject , options ?: boolean | EventListenerOptions ) => {
2043- return originalRemove . call ( window , type , listener , options )
2044- } )
2107+ . spyOn ( globalThis , 'removeEventListener' )
2108+ . mockImplementation (
2109+ (
2110+ type : string ,
2111+ listener : EventListenerOrEventListenerObject ,
2112+ options ?: boolean | EventListenerOptions
2113+ ) => {
2114+ return originalRemove . call ( globalThis , type , listener , options )
2115+ }
2116+ )
20452117
20462118 const bridgeElement = instance . renderMeasurementBridge ( ) as React . ReactElement
20472119 const { unmount : unmountBridge } = render ( bridgeElement )
@@ -2060,14 +2132,14 @@ describe('MentionsInput', () => {
20602132 expect ( updatePosition . mock . calls . length ) . toBe ( positionCalls + 1 )
20612133
20622134 act ( ( ) => {
2063- window . dispatchEvent ( new Event ( 'resize' ) )
2135+ globalThis . dispatchEvent ( new Event ( 'resize' ) )
20642136 } )
20652137
20662138 expect ( syncScroll . mock . calls . length ) . toBeGreaterThan ( syncCalls + 1 )
20672139 expect ( updatePosition . mock . calls . length ) . toBeGreaterThan ( positionCalls + 1 )
20682140
20692141 act ( ( ) => {
2070- window . dispatchEvent ( new Event ( 'orientationchange' ) )
2142+ globalThis . dispatchEvent ( new Event ( 'orientationchange' ) )
20712143 } )
20722144
20732145 expect ( syncScroll . mock . calls . length ) . toBeGreaterThan ( syncCalls + 2 )
0 commit comments