Skip to content

Commit fec6207

Browse files
committed
fix: map falsy boolean properties as undefined
1 parent 04ece1e commit fec6207

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

src/utils/createComponent.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
type WebComponentProps as _WebComponentProps,
55
} from '@lit-labs/react';
66
import type { ThemePropertyMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
7-
import type React from 'react';
7+
import React from 'react';
88
import type { RefAttributes } from 'react';
99
import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
1010

@@ -81,7 +81,7 @@ export function createComponent<I extends HTMLElement, E extends EventNames = {}
8181
export function createComponent<I extends HTMLElement, E extends EventNames = {}>(options: Options<I, E>): any {
8282
const { elementClass } = options;
8383

84-
return _createComponent(
84+
const component = _createComponent(
8585
'_properties' in elementClass
8686
? {
8787
...options,
@@ -98,4 +98,18 @@ export function createComponent<I extends HTMLElement, E extends EventNames = {}
9898
}
9999
: options,
100100
);
101+
102+
return React.forwardRef<typeof component, Parameters<typeof component>[0]>((props, ref) => {
103+
// Map falsy boolean properties as `undefined` to avoid them from rendering with the
104+
// value "false" in the attribute, for example `<vaadin-button hidden="false">`,
105+
// which would actually evaluate as `hidden` being `true`.
106+
const booleanProps = Object.entries(props).reduce((acc, [key, value]) => {
107+
if (typeof value === 'boolean') {
108+
return { ...acc, [key]: value || undefined };
109+
}
110+
return acc;
111+
}, {});
112+
113+
return React.createElement(component, { ...props, ...booleanProps, ref });
114+
});
101115
}

test/Select.spec.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import chaiDom from 'chai-dom';
66
import type { ReactElement } from 'react';
77
import { ListBox } from '../src/ListBox.js';
88
import { Item } from '../src/Item.js';
9-
import { Select } from '../src/Select.js';
9+
import { Select, SelectElement } from '../src/Select.js';
1010
import { findByQuerySelector } from './utils/findByQuerySelector.js';
1111

1212
useChaiPlugin(chaiDom);
@@ -143,4 +143,29 @@ describe('Select', () => {
143143
await expect(findByQuerySelector('div[slot="prefix"]')).to.eventually.have.text('Value:');
144144
});
145145
});
146+
147+
describe('boolean property', () => {
148+
const booleanProperties: Array<keyof typeof SelectElement.prototype & string> = [
149+
'disabled',
150+
'hidden',
151+
'opened',
152+
'draggable',
153+
];
154+
155+
booleanProperties.forEach((property) => {
156+
describe(property, () => {
157+
it(`should be true in the element if ${property} prop is true`, async () => {
158+
render(<Select items={[{ label: 'foo', value: 'foo' }]} {...{ [property]: true }} />);
159+
const select = await findByQuerySelector('vaadin-select');
160+
expect(select[property]).to.be.ok;
161+
});
162+
163+
it(`should be false in the element if ${property} prop is false`, async () => {
164+
render(<Select items={[{ label: 'foo', value: 'foo' }]} {...{ [property]: false }} />);
165+
const select = await findByQuerySelector('vaadin-select');
166+
expect(select[property]).not.to.be.ok;
167+
});
168+
});
169+
});
170+
});
146171
});

test/SideNav.spec.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { expect, use as useChaiPlugin } from '@esm-bundle/chai';
2+
import { render } from '@testing-library/react';
3+
import chaiDom from 'chai-dom';
4+
import { SideNav, SideNavElement } from '../src/SideNav.js';
5+
import { findByQuerySelector } from './utils/findByQuerySelector.js';
6+
7+
useChaiPlugin(chaiDom);
8+
9+
describe('SideNav', () => {
10+
describe('boolean property', () => {
11+
const booleanProperties: Array<keyof typeof SideNavElement.prototype & string> = [
12+
'hidden',
13+
'collapsed',
14+
'draggable',
15+
];
16+
17+
booleanProperties.forEach((property) => {
18+
describe(property, () => {
19+
it(`should be true in the element if ${property} prop is true`, async () => {
20+
render(<SideNav {...{ [property]: true }} />);
21+
const sideNav = await findByQuerySelector('vaadin-side-nav');
22+
expect(sideNav[property]).to.be.ok;
23+
});
24+
25+
it(`should be false in the element if ${property} prop is false`, async () => {
26+
render(<SideNav {...{ [property]: false }} />);
27+
const sideNav = await findByQuerySelector('vaadin-side-nav');
28+
expect(sideNav[property]).not.to.be.ok;
29+
});
30+
});
31+
});
32+
});
33+
});

0 commit comments

Comments
 (0)