|
1 | 1 | import { expect } from 'vitest'; |
| 2 | +import * as React from 'react'; |
2 | 3 | import { Field } from '@base-ui/react/field'; |
3 | 4 | import { screen } from '@mui/internal-test-utils'; |
4 | 5 | import { createRenderer, describeConformance } from '#test-utils'; |
@@ -74,55 +75,81 @@ describe('<Field.Label />', () => { |
74 | 75 | errorSpy.mockRestore(); |
75 | 76 | }); |
76 | 77 |
|
77 | | - it('errors if nativeLabel=true but ref is not a label', async () => { |
| 78 | + it('does not warn when the render function returns no element', async () => { |
78 | 79 | const errorSpy = vi |
79 | 80 | .spyOn(console, 'error') |
80 | 81 | .mockName('console.error') |
81 | 82 | .mockImplementation(() => {}); |
82 | 83 |
|
| 84 | + const EmptyLabel = React.forwardRef(function EmptyLabel() { |
| 85 | + return null; |
| 86 | + }); |
| 87 | + |
83 | 88 | await render( |
84 | 89 | <Field.Root> |
85 | | - <Field.Control /> |
86 | | - <Field.Label nativeLabel render={<div />}> |
87 | | - Label |
88 | | - </Field.Label> |
| 90 | + <Field.Label render={<EmptyLabel />}>Label</Field.Label> |
89 | 91 | </Field.Root>, |
90 | 92 | ); |
91 | 93 |
|
92 | | - expect(errorSpy).toHaveBeenCalledTimes(1); |
93 | | - expect(errorSpy).toHaveBeenCalledWith( |
94 | | - expect.stringContaining( |
95 | | - 'Base UI: <Field.Label> expected a <label> element because the `nativeLabel` prop is true. ' + |
96 | | - 'Rendering a non-<label> disables native label association, so `htmlFor` will not ' + |
97 | | - 'work. Use a real <label> in the `render` prop, or set `nativeLabel` to `false`.', |
98 | | - ), |
99 | | - ); |
| 94 | + expect(errorSpy).not.toHaveBeenCalled(); |
100 | 95 | errorSpy.mockRestore(); |
101 | 96 | }); |
102 | 97 |
|
103 | | - it('errors if nativeLabel=false but ref is a label', async () => { |
| 98 | + it('errors if nativeLabel=true but ref is not a label', async () => { |
104 | 99 | const errorSpy = vi |
105 | 100 | .spyOn(console, 'error') |
106 | 101 | .mockName('console.error') |
107 | 102 | .mockImplementation(() => {}); |
108 | 103 |
|
109 | | - await render( |
110 | | - <Field.Root> |
111 | | - <Field.Control /> |
112 | | - <Field.Label nativeLabel={false}>Label</Field.Label> |
113 | | - </Field.Root>, |
114 | | - ); |
| 104 | + try { |
| 105 | + await render( |
| 106 | + <Field.Root> |
| 107 | + <Field.Control /> |
| 108 | + <Field.Label nativeLabel render={<div />}> |
| 109 | + Label |
| 110 | + </Field.Label> |
| 111 | + </Field.Root>, |
| 112 | + ); |
| 113 | + |
| 114 | + expect(errorSpy).toHaveBeenCalledTimes(1); |
| 115 | + expect(errorSpy).toHaveBeenCalledWith( |
| 116 | + expect.stringContaining( |
| 117 | + 'Base UI: <Field.Label> expected a <label> element because the `nativeLabel` prop is true. ' + |
| 118 | + 'Rendering a non-<label> disables native label association, so `htmlFor` will not ' + |
| 119 | + 'work. Use a real <label> in the `render` prop, or set `nativeLabel` to `false`.', |
| 120 | + ), |
| 121 | + ); |
| 122 | + } finally { |
| 123 | + errorSpy.mockRestore(); |
| 124 | + } |
| 125 | + }); |
115 | 126 |
|
116 | | - expect(errorSpy).toHaveBeenCalledTimes(1); |
117 | | - expect(errorSpy).toHaveBeenCalledWith( |
118 | | - expect.stringContaining( |
119 | | - 'Base UI: <Field.Label> expected a non-<label> element because the `nativeLabel` prop is false. ' + |
120 | | - 'Rendering a <label> assumes native label behavior while Base UI treats it as ' + |
121 | | - 'non-native, which can cause unexpected pointer behavior. Use a non-<label> in the ' + |
122 | | - '`render` prop, or set `nativeLabel` to `true`.', |
123 | | - ), |
124 | | - ); |
125 | | - errorSpy.mockRestore(); |
| 127 | + it('errors if nativeLabel=false but ref is a label', async () => { |
| 128 | + const errorSpy = vi |
| 129 | + .spyOn(console, 'error') |
| 130 | + .mockName('console.error') |
| 131 | + .mockImplementation(() => {}); |
| 132 | + |
| 133 | + try { |
| 134 | + await render( |
| 135 | + <Field.Root> |
| 136 | + <Field.Control /> |
| 137 | + <Field.Label nativeLabel={false}>Label</Field.Label> |
| 138 | + </Field.Root>, |
| 139 | + ); |
| 140 | + |
| 141 | + expect(errorSpy).toHaveBeenCalledTimes(1); |
| 142 | + expect(errorSpy).toHaveBeenCalledWith( |
| 143 | + expect.stringContaining( |
| 144 | + 'Base UI: <Field.Label> expected a non-<label> element because the `nativeLabel` prop is false. ' + |
| 145 | + 'Rendering a <label> assumes native label behavior while Base UI treats it as ' + |
| 146 | + 'non-native, which can cause unexpected pointer behavior. Use a non-<label> in the ' + |
| 147 | + '`render` prop, or set `nativeLabel` to `true`.', |
| 148 | + ), |
| 149 | + ); |
| 150 | + } finally { |
| 151 | + errorSpy.mockRestore(); |
| 152 | + } |
126 | 153 | }); |
127 | 154 | }); |
128 | 155 | }); |
0 commit comments