Skip to content
This repository was archived by the owner on Mar 9, 2023. It is now read-only.

Commit d287836

Browse files
Merge pull request #60 from ljmotta/general-enhancements
2 parents 23ea396 + 86bd676 commit d287836

File tree

12 files changed

+147
-67
lines changed

12 files changed

+147
-67
lines changed

__tests__/DateField.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ test('<DateField> - renders an input with correct disabled state', () => {
5252
});
5353

5454
test('<DateField> - renders a input with correct label (specified)', () => {
55-
const element = <DateField name="x" label="DateFieldLabel" />;
55+
const element = <DateField required={false} name="x" label="DateFieldLabel" />;
5656
const wrapper = mount(element, createContext({ x: { type: Date } }));
5757

5858
expect(wrapper.find('label')).toHaveLength(1);
@@ -62,6 +62,17 @@ test('<DateField> - renders a input with correct label (specified)', () => {
6262
);
6363
});
6464

65+
test('<DateField> - renders a input with correct label (specified)', () => {
66+
const element = <DateField required={true} name="x" label="DateFieldLabel" />;
67+
const wrapper = mount(element, createContext({ x: { type: Date } }));
68+
69+
expect(wrapper.find('label')).toHaveLength(1);
70+
expect(wrapper.find('label').text()).toBe('DateFieldLabel *');
71+
expect(wrapper.find('label').prop('htmlFor')).toBe(
72+
wrapper.find('input').prop('id'),
73+
);
74+
});
75+
6576
test('<DateField> - renders a input with correct value (default)', () => {
6677
const element = <DateField name="x" />;
6778
const wrapper = mount(element, createContext({ x: { type: Date } }));

__tests__/NumField.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ test('<NumField> - renders an input which correctly reacts on change (zero)', ()
246246
});
247247

248248
test('<NumField> - renders a label', () => {
249-
const element = <NumField name="x" label="y" />;
249+
const element = <NumField required={false} name="x" label="y" />;
250250
const wrapper = mount(element, createContext({ x: { type: Number } }));
251251

252252
expect(wrapper.find('label')).toHaveLength(1);
@@ -256,6 +256,17 @@ test('<NumField> - renders a label', () => {
256256
);
257257
});
258258

259+
test('<NumField> - renders a label', () => {
260+
const element = <NumField required={true} name="x" label="y" />;
261+
const wrapper = mount(element, createContext({ x: { type: Number } }));
262+
263+
expect(wrapper.find('label')).toHaveLength(1);
264+
expect(wrapper.find('label').text()).toBe('y *');
265+
expect(wrapper.find('label').prop('htmlFor')).toBe(
266+
wrapper.find('input').prop('id'),
267+
);
268+
});
269+
259270
test('<NumField> - renders a wrapper with unknown props', () => {
260271
const element = <NumField name="x" data-x="x" data-y="y" data-z="z" />;
261272
const wrapper = mount(element, createContext({ x: { type: Number } }));

__tests__/RadioField.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ test('<RadioField> - renders a set of checkboxes which correctly reacts on chang
260260
});
261261

262262
test('<RadioField> - renders a label', () => {
263-
const element = <RadioField name="x" label="y" />;
263+
const element = <RadioField required={false} name="x" label="y" />;
264264
const wrapper = mount(
265265
element,
266266
createContext({ x: { type: String, allowedValues: ['a', 'b'] } }),
@@ -275,6 +275,22 @@ test('<RadioField> - renders a label', () => {
275275
).toBe('y');
276276
});
277277

278+
test('<RadioField> - renders a label', () => {
279+
const element = <RadioField required={true} name="x" label="y" />;
280+
const wrapper = mount(
281+
element,
282+
createContext({ x: { type: String, allowedValues: ['a', 'b'] } }),
283+
);
284+
285+
expect(wrapper.find('label')).toHaveLength(3);
286+
expect(
287+
wrapper
288+
.find('label')
289+
.at(0)
290+
.text(),
291+
).toBe('y *');
292+
});
293+
278294
test('<RadioField> - renders a wrapper with unknown props', () => {
279295
const element = <RadioField name="x" data-x="x" data-y="y" data-z="z" />;
280296
const wrapper = mount(

__tests__/SelectField.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ test('<SelectField> - renders a select which correctly reacts on change (empty)'
200200
});
201201

202202
expect(wrapper.find(Select)).toHaveLength(1);
203-
expect(onChange).toHaveBeenLastCalledWith('x', '');
203+
expect(onChange).not.toHaveBeenCalled()
204204
});
205205

206206
test('<SelectField> - renders a select which correctly reacts on change (same value)', () => {
@@ -225,7 +225,7 @@ test('<SelectField> - renders a select which correctly reacts on change (same va
225225
});
226226

227227
test('<SelectField> - renders a label', () => {
228-
const element = <SelectField name="x" label="y" />;
228+
const element = <SelectField required={false} name="x" label="y" />;
229229
const wrapper = mount(
230230
element,
231231
createContext({ x: { type: String, allowedValues: ['a', 'b'] } }),
@@ -238,6 +238,20 @@ test('<SelectField> - renders a label', () => {
238238
);
239239
});
240240

241+
test('<SelectField> - renders a label', () => {
242+
const element = <SelectField required={true} name="x" label="y" />;
243+
const wrapper = mount(
244+
element,
245+
createContext({ x: { type: String, allowedValues: ['a', 'b'] } }),
246+
);
247+
248+
expect(wrapper.find('label')).toHaveLength(1);
249+
expect(wrapper.find('label').text()).toBe('y *');
250+
expect(wrapper.find('label').prop('htmlFor')).toBe(
251+
wrapper.find(Select).prop('id'),
252+
);
253+
});
254+
241255
test('<SelectField> - renders a wrapper with unknown props', () => {
242256
const element = <SelectField name="x" data-x="x" data-y="y" data-z="z" />;
243257
const wrapper = mount(

__tests__/TextField.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ test('<TextField> - renders an input which correctly reacts on change (same valu
135135
});
136136

137137
test('<TextField> - renders a label', () => {
138-
const element = <TextField name="x" label="y" />;
138+
const element = <TextField required={false} name="x" label="y" />;
139139
const wrapper = mount(element, createContext({ x: { type: String } }));
140140

141141
expect(wrapper.find('label')).toHaveLength(1);
@@ -145,6 +145,17 @@ test('<TextField> - renders a label', () => {
145145
);
146146
});
147147

148+
test('<TextField> - renders a label', () => {
149+
const element = <TextField required={true} name="x" label="y" />;
150+
const wrapper = mount(element, createContext({ x: { type: String } }));
151+
152+
expect(wrapper.find('label')).toHaveLength(1);
153+
expect(wrapper.find('label').text()).toBe('y *');
154+
expect(wrapper.find('label').prop('htmlFor')).toBe(
155+
wrapper.find('input').prop('id'),
156+
);
157+
});
158+
148159
test('<TextField> - renders a wrapper with unknown props', () => {
149160
const element = <TextField name="x" data-x="x" data-y="y" data-z="z" />;
150161
const wrapper = mount(element, createContext({ x: { type: String } }));

src/BoolField.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import {
55
Switch,
66
SwitchProps,
77
} from '@patternfly/react-core';
8-
import { connectField, FieldProps, filterDOMProps } from 'uniforms/es5';
8+
import { connectField, FieldProps } from 'uniforms/es5';
9+
10+
import wrapField from './wrapField';
911

1012
enum ComponentType {
1113
checkbox = 'checkbox',
@@ -34,18 +36,17 @@ function Bool({
3436
...props
3537
}: BoolFieldProps) {
3638
const Component = appearance === ComponentType.switch ? Switch : Checkbox;
37-
return (
38-
<div {...filterDOMProps(props)}>
39-
<Component
40-
isChecked={value || false}
41-
isDisabled={disabled}
42-
id={id}
43-
name={name}
44-
onChange={() => disabled || onChange(!value)}
45-
ref={inputRef}
46-
label={label}
47-
/>
48-
</div>
39+
return wrapField(
40+
{ id, ...props },
41+
<Component
42+
isChecked={value || false}
43+
isDisabled={disabled}
44+
id={id}
45+
name={name}
46+
onChange={() => disabled || onChange(!value)}
47+
ref={inputRef}
48+
label={label}
49+
/>
4950
);
5051
}
5152

src/NestField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const Nest = ({
2727
...props
2828
}: NestFieldProps) => {
2929
return (
30-
<Card {...filterDOMProps(props)} style={{ marginBottom: '1.5rem' }}>
30+
<Card {...filterDOMProps(props)}>
3131
<CardBody className="pf-c-form">
3232
{label && (
3333
<label>

src/NumField.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const Num = (props: NumFieldProps) => {
3838
step={props.decimal ? 0.01 : 1}
3939
type="number"
4040
value={props.value ?? ''}
41+
validated={props.error ? 'error' : 'default'}
4142
/>
4243
);
4344
};

src/RadioField.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react';
22
import { Radio as RadioField, RadioProps } from '@patternfly/react-core';
33
import { connectField, filterDOMProps } from 'uniforms/es5';
44

5+
import wrapField from './wrapField';
6+
57
export type RadioFieldProps = {
68
transform?: (string?: string) => string;
79
allowedValues: string[];
@@ -12,13 +14,9 @@ export type RadioFieldProps = {
1214

1315
const Radio = (props: RadioFieldProps) => {
1416
filterDOMProps.register('checkboxes', 'decimal');
15-
return (
17+
return wrapField(
18+
props,
1619
<div {...filterDOMProps(props)}>
17-
{props.label && (
18-
<div>
19-
<label>{props.label}</label>
20-
</div>
21-
)}
2220
{props.allowedValues?.map((item) => (
2321
<React.Fragment key={item}>
2422
<RadioField

src/SelectField.tsx

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useCallback, useMemo, useState } from 'react';
22
import {
33
Checkbox,
44
CheckboxProps,
@@ -35,7 +35,9 @@ type CheckboxesProps = {
3535
filterDOMProps.register('autoValue');
3636

3737
function RenderCheckboxes(props: CheckboxesProps) {
38-
const Group = props.fieldType === Array ? Checkbox : Radio;
38+
const Group = useMemo(() => (props.fieldType === Array ? Checkbox : Radio), [
39+
props,
40+
]);
3941

4042
return (
4143
<div {...filterDOMProps(props)}>
@@ -91,57 +93,70 @@ function isSelectOptionObject(
9193
}
9294

9395
function RenderSelect(props: SelectInputProps) {
94-
const selectDefault = props.fieldType === Array ? [] : props.placeholder;
95-
9696
const [expanded, setExpanded] = useState<boolean>(false);
97-
const [selected, setSelected] = useState<string | string[]>(selectDefault);
98-
99-
const handleSelect = (
100-
event: React.MouseEvent | React.ChangeEvent,
101-
selection: string | SelectOptionObject
102-
) => {
103-
const items = parseInput(selection, props.fieldType);
104-
props.onChange(items);
105-
setSelected(items);
106-
setExpanded(false);
107-
};
108-
109-
const parseInput = (
110-
selection: string | SelectOptionObject,
111-
fieldType: typeof Array | any
112-
): string | string[] => {
113-
const parsedSelection = isSelectOptionObject(selection)
114-
? selection.toString()
115-
: selection;
116-
117-
if (fieldType !== Array) {
118-
return parsedSelection !== '' ? parsedSelection : '';
119-
}
120-
121-
if (Array.isArray(selected)) {
122-
if (selected.includes(parsedSelection)) {
123-
return selected.filter((s) => s !== parsedSelection);
97+
const [selected, setSelected] = useState<string | string[]>([]);
98+
99+
const parseInput = useCallback(
100+
(
101+
selection: string | SelectOptionObject,
102+
fieldType: typeof Array | any
103+
): string | string[] => {
104+
const parsedSelection = isSelectOptionObject(selection)
105+
? selection.toString()
106+
: selection;
107+
108+
if (fieldType !== Array) {
109+
return parsedSelection !== '' ? parsedSelection : '';
110+
}
111+
112+
if (Array.isArray(selected)) {
113+
if (selected.includes(parsedSelection)) {
114+
return selected.filter((s) => s !== parsedSelection);
115+
}
116+
return [parsedSelection, ...selected];
117+
}
118+
return [parsedSelection, selected];
119+
},
120+
[selected]
121+
);
122+
123+
const handleSelect = useCallback(
124+
(
125+
event: React.MouseEvent | React.ChangeEvent,
126+
selection: string | SelectOptionObject
127+
) => {
128+
if (selection === props.placeholder) {
129+
setSelected([]);
130+
setExpanded(false);
131+
} else {
132+
const items = parseInput(selection, props.fieldType);
133+
props.onChange(items);
134+
setSelected(items);
135+
setExpanded(false);
124136
}
125-
return [parsedSelection, ...selected];
126-
}
127-
return [parsedSelection, selected];
128-
};
137+
},
138+
[parseInput, props]
139+
);
129140

130-
const selectedOptions = props.allowedValues!.map((value) => (
131-
<SelectOption key={value} value={value}>
132-
{props.transform ? props.transform(value) : value}
133-
</SelectOption>
134-
));
141+
const selectedOptions = useMemo(
142+
() =>
143+
props.allowedValues!.map((value) => (
144+
<SelectOption key={value} value={value}>
145+
{props.transform ? props.transform(value) : value}
146+
</SelectOption>
147+
)),
148+
[props]
149+
);
135150

136151
if (props.placeholder)
137152
selectedOptions.unshift(
138153
<SelectOption
139154
key={props.allowedValues!.length}
140-
isDisabled
141155
isPlaceholder
142156
value={props.placeholder}
143157
/>
144158
);
159+
145160
return wrapField(
146161
props,
147162
<Select

0 commit comments

Comments
 (0)