Skip to content

Commit c24ba86

Browse files
Fix error when transition classes contain new lines (#2871)
* fix DOMException when remove class with '\n' character in react 'transition' component * Split classes on all whitespace * Revert "fix DOMException when remove class with '\n' character in react 'transition' component" This reverts commit 76e8354. * fix typo * Add test * Fix CS * Update changelog --------- Co-authored-by: Jordan Pittman <[email protected]>
1 parent c25e2e6 commit c24ba86

File tree

5 files changed

+69
-2
lines changed

5 files changed

+69
-2
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- Fix infinite render-loop for `<Disclosure.Panel>` and `<Popover.Panel>` when `as={Fragment}` ([#2760](https://github.com/tailwindlabs/headlessui/pull/2760))
2020
- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))
2121
- Fix outside click detection when component is mounted in the Shadow DOM ([#2866](https://github.com/tailwindlabs/headlessui/pull/2866))
22+
- Fix error when transition classes contain new lines ([#2871](https://github.com/tailwindlabs/headlessui/pull/2871))
2223

2324
### Added
2425

packages/@headlessui-react/src/components/transitions/transition.test.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { act as _act, fireEvent, render } from '@testing-library/react'
22
import React, { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'
3+
import { getByText } from '../../test-utils/accessibility-assertions'
34
import { executeTimeline } from '../../test-utils/execute-timeline'
5+
import { click } from '../../test-utils/interactions'
46
import { createSnapshot } from '../../test-utils/snapshot'
57
import { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'
68
import { Transition } from './transition'
@@ -402,6 +404,57 @@ describe('Setup API', () => {
402404
})
403405

404406
describe('transition classes', () => {
407+
it('should support new lines in class lists', async () => {
408+
function Example() {
409+
let [show, setShow] = useState(true)
410+
411+
return (
412+
<div>
413+
<button onClick={() => setShow((v) => !v)}>toggle</button>
414+
415+
<Transition show={show} as="div" className={`foo1\nfoo2`} enter="enter" leave="leave">
416+
Children
417+
</Transition>
418+
</div>
419+
)
420+
}
421+
422+
let { container } = await act(() => render(<Example />))
423+
424+
expect(container.firstChild).toMatchInlineSnapshot(`
425+
<div>
426+
<button>
427+
toggle
428+
</button>
429+
<div
430+
class="foo1
431+
foo2"
432+
>
433+
Children
434+
</div>
435+
</div>
436+
`)
437+
438+
await click(getByText('toggle'))
439+
440+
// TODO: This is not quite right
441+
// The `foo1\nfoo2` should be gone
442+
// I think this is a qurk of JSDOM
443+
expect(container.firstChild).toMatchInlineSnapshot(`
444+
<div>
445+
<button>
446+
toggle
447+
</button>
448+
<div
449+
class="foo1
450+
foo2 foo1 foo2 leave"
451+
>
452+
Children
453+
</div>
454+
</div>
455+
`)
456+
})
457+
405458
it('should be possible to passthrough the transition classes', () => {
406459
let { container } = render(
407460
<Transition

packages/@headlessui-react/src/components/transitions/transition.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,14 @@ type ContainerElement = MutableRefObject<HTMLElement | null>
3737

3838
type TransitionDirection = 'enter' | 'leave' | 'idle'
3939

40+
/**
41+
* Split class lists by whitespace
42+
*
43+
* We can't check for just spaces as all whitespace characters are
44+
* invalid in a class name, so we have to split on ANY whitespace.
45+
*/
4046
function splitClasses(classes: string = '') {
41-
return classes.split(' ').filter((className) => className.trim().length > 1)
47+
return classes.split(/\s+/).filter((className) => className.length > 1)
4248
}
4349

4450
interface TransitionContextValues {

packages/@headlessui-vue/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- Fix missing `data-headlessui-state` attribute when `as="template"` ([#2787](https://github.com/tailwindlabs/headlessui/pull/2787))
2020
- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))
2121
- Fix outside click detection when component is mounted in the Shadow DOM ([6846231](https://github.com/tailwindlabs/headlessui/commit/684623131b99d9e75dfc1c1f6d27244c334a95d9))
22+
- Fix error when transition classes contain new lines ([#2871](https://github.com/tailwindlabs/headlessui/pull/2871))
2223

2324
### Added
2425

packages/@headlessui-vue/src/components/transitions/transition.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,14 @@ import { Reason, transition } from './utils/transition'
2929

3030
type ID = ReturnType<typeof useId>
3131

32+
/**
33+
* Split class lists by whitespace
34+
*
35+
* We can't check for just spaces as all whitespace characters are
36+
* invalid in a class name, so we have to split on ANY whitespace.
37+
*/
3238
function splitClasses(classes: string = '') {
33-
return classes.split(' ').filter((className) => className.trim().length > 1)
39+
return classes.split(/\s+/).filter((className) => className.length > 1)
3440
}
3541

3642
interface TransitionContextValues {

0 commit comments

Comments
 (0)