@@ -6,7 +6,7 @@ import { Portal } from './portal'
6
6
import { click } from '../../test-utils/interactions'
7
7
8
8
function getPortalRoot ( ) {
9
- return document . getElementById ( 'headlessui-portal-root' )
9
+ return document . getElementById ( 'headlessui-portal-root' ) !
10
10
}
11
11
12
12
beforeEach ( ( ) => {
@@ -140,3 +140,136 @@ it('should cleanup the Portal root when the last Portal is unmounted', async ()
140
140
expect ( getPortalRoot ( ) ) . not . toBe ( null )
141
141
expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 1 )
142
142
} )
143
+
144
+ it ( 'should be possible to render multiple portals at the same time' , async ( ) => {
145
+ expect ( getPortalRoot ( ) ) . toBe ( null )
146
+
147
+ function Example ( ) {
148
+ let [ renderA , setRenderA ] = useState ( true )
149
+ let [ renderB , setRenderB ] = useState ( true )
150
+ let [ renderC , setRenderC ] = useState ( true )
151
+
152
+ return (
153
+ < main id = "parent" >
154
+ < button id = "a" onClick = { ( ) => setRenderA ( v => ! v ) } >
155
+ Toggle A
156
+ </ button >
157
+ < button id = "b" onClick = { ( ) => setRenderB ( v => ! v ) } >
158
+ Toggle B
159
+ </ button >
160
+ < button id = "c" onClick = { ( ) => setRenderC ( v => ! v ) } >
161
+ Toggle C
162
+ </ button >
163
+
164
+ < button
165
+ id = "double"
166
+ onClick = { ( ) => {
167
+ setRenderA ( v => ! v )
168
+ setRenderB ( v => ! v )
169
+ } }
170
+ >
171
+ Toggle A & B { ' ' }
172
+ </ button >
173
+
174
+ { renderA && (
175
+ < Portal >
176
+ < p id = "content1" > Contents 1 ...</ p >
177
+ </ Portal >
178
+ ) }
179
+
180
+ { renderB && (
181
+ < Portal >
182
+ < p id = "content2" > Contents 2 ...</ p >
183
+ </ Portal >
184
+ ) }
185
+
186
+ { renderC && (
187
+ < Portal >
188
+ < p id = "content3" > Contents 3 ...</ p >
189
+ </ Portal >
190
+ ) }
191
+ </ main >
192
+ )
193
+ }
194
+
195
+ render ( < Example /> )
196
+
197
+ expect ( getPortalRoot ( ) ) . not . toBe ( null )
198
+ expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 3 )
199
+
200
+ // Remove Portal 1
201
+ await click ( document . getElementById ( 'a' ) )
202
+ expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 2 )
203
+
204
+ // Remove Portal 2
205
+ await click ( document . getElementById ( 'b' ) )
206
+ expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 1 )
207
+
208
+ // Re-add Portal 1
209
+ await click ( document . getElementById ( 'a' ) )
210
+ expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 2 )
211
+
212
+ // Remove Portal 3
213
+ await click ( document . getElementById ( 'c' ) )
214
+ expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 1 )
215
+
216
+ // Remove Portal 1
217
+ await click ( document . getElementById ( 'a' ) )
218
+ expect ( getPortalRoot ( ) ) . toBe ( null )
219
+
220
+ // Render A and B at the same time!
221
+ await click ( document . getElementById ( 'double' ) )
222
+ expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 2 )
223
+ } )
224
+
225
+ it ( 'should be possible to tamper with the modal root and restore correctly' , async ( ) => {
226
+ expect ( getPortalRoot ( ) ) . toBe ( null )
227
+
228
+ function Example ( ) {
229
+ let [ renderA , setRenderA ] = useState ( true )
230
+ let [ renderB , setRenderB ] = useState ( true )
231
+
232
+ return (
233
+ < main id = "parent" >
234
+ < button id = "a" onClick = { ( ) => setRenderA ( v => ! v ) } >
235
+ Toggle A
236
+ </ button >
237
+ < button id = "b" onClick = { ( ) => setRenderB ( v => ! v ) } >
238
+ Toggle B
239
+ </ button >
240
+
241
+ { renderA && (
242
+ < Portal >
243
+ < p id = "content1" > Contents 1 ...</ p >
244
+ </ Portal >
245
+ ) }
246
+
247
+ { renderB && (
248
+ < Portal >
249
+ < p id = "content2" > Contents 2 ...</ p >
250
+ </ Portal >
251
+ ) }
252
+ </ main >
253
+ )
254
+ }
255
+
256
+ render ( < Example /> )
257
+
258
+ expect ( getPortalRoot ( ) ) . not . toBe ( null )
259
+
260
+ // Tamper tamper
261
+ document . body . removeChild ( document . getElementById ( 'headlessui-portal-root' ) ! )
262
+
263
+ // Hide Portal 1 and 2
264
+ await click ( document . getElementById ( 'a' ) )
265
+ await click ( document . getElementById ( 'b' ) )
266
+
267
+ expect ( getPortalRoot ( ) ) . toBe ( null )
268
+
269
+ // Re-show Portal 1 and 2
270
+ await click ( document . getElementById ( 'a' ) )
271
+ await click ( document . getElementById ( 'b' ) )
272
+
273
+ expect ( getPortalRoot ( ) ) . not . toBe ( null )
274
+ expect ( getPortalRoot ( ) . childNodes ) . toHaveLength ( 2 )
275
+ } )
0 commit comments