Skip to content

Commit 9bcd900

Browse files
authored
Merge pull request #177 from stormpath/fix/input-like-form-handling
Handling Input-Like Components
2 parents c87cb99 + cddbd93 commit 9bcd900

File tree

7 files changed

+53
-50
lines changed

7 files changed

+53
-50
lines changed

docs/api.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ Customize the form by providing your own markup.
264264
```
265265

266266
**Important:** To use a custom field component the name of the component must contain one of these three words: `text`, `input` or `field`.
267+
Alternatively, the component can have arbitrary name if it has the `spInputLike` (or `data-spInputLike`) property set. If you minify your code,
268+
prefer using this property to relying on the component name, as the latter may be mangled by the minifier.
267269
The component must also support the properties `name` and `onChange`. The property `name` should represent the name of the field, and the
268270
`onChange` property a handler for the field's `onChange` event.
269271

@@ -349,7 +351,7 @@ Specify `hideSocial` to hide the ability to register with a social provider.
349351
<RegistrationForm hideSocial={true} />
350352
```
351353

352-
Customize the form by providing your own markup.
354+
Customize the form by providing your own markup.
353355

354356
By default, the registration form will render these four fields, and they will be required by the user: `givenName`, `surname`, `email`, and `password`. Express.js users who want to make `givenName` and/or `surname` optional, or to add new required fields (like `username`), can refer to [Stormpath Express Library Guide](https://docs.stormpath.com/nodejs/express/latest/registration.html).
355357

src/components/ChangePasswordForm.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,7 @@ export default class ChangePasswordForm extends React.Component {
142142
}
143143
};
144144

145-
if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
146-
if (element.props && element.props.name) {
147-
tryMapFormField(element.props.name);
148-
}
149-
} else if (element.type === 'input' || element.type === 'textarea') {
150-
if (element.props.type !== 'submit') {
151-
tryMapFormField(element.props.name);
152-
}
153-
}
145+
utils.mapFormField(element, tryMapFormField);
154146
}
155147

156148
_spIfHandler(action, element) {

src/components/LoginForm.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,7 @@ export default class LoginForm extends React.Component {
248248
}
249249
};
250250

251-
if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
252-
if (element.props && element.props.name) {
253-
tryMapFormField(element.props.name);
254-
}
255-
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
256-
if (element.props.type !== 'submit') {
257-
tryMapFormField(element.props.name);
258-
}
259-
}
251+
utils.mapFormField(element, tryMapFormField);
260252
}
261253

262254
_spIfHandler(action, element) {

src/components/RegistrationForm.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -329,15 +329,7 @@ export default class RegistrationForm extends React.Component {
329329
}
330330
};
331331

332-
if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
333-
if (element.props && element.props.name) {
334-
tryMapFormField(element.props.name);
335-
}
336-
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
337-
if (element.props.type !== 'submit') {
338-
tryMapFormField(element.props.name);
339-
}
340-
}
332+
utils.mapFormField(element, tryMapFormField);
341333
}
342334

343335
_spIfHandler(action, element) {

src/components/ResetPasswordForm.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,7 @@ export default class ResetPasswordForm extends React.Component {
106106
}
107107
};
108108

109-
if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
110-
if (element.props && element.props.name) {
111-
tryMapFormField(element.props.name);
112-
}
113-
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
114-
if (element.props.type !== 'submit') {
115-
tryMapFormField(element.props.name);
116-
}
117-
}
109+
utils.mapFormField(element, tryMapFormField);
118110
}
119111

120112
_spIfHandler(action, element) {

src/components/UserProfileForm.js

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,17 +168,7 @@ export default class UserProfileForm extends React.Component {
168168
utils.getFieldValue(this.state.defaultFields, element.props.name) :
169169
undefined;
170170

171-
if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
172-
if (element.props && element.props.name) {
173-
tryMapField(element.props.name, defaultValue);
174-
}
175-
} else if (element.type === 'input') {
176-
if (element.props.type === 'submit') {
177-
return;
178-
}
179-
180-
tryMapField(element.props.name, defaultValue);
181-
}
171+
utils.mapFormField(element, tryMapField, defaultValue);
182172
}
183173

184174
_spIfHandler(action, element) {

src/utils.js

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,29 @@ class Utils {
1414
s4() + '-' + s4() + s4() + s4();
1515
}
1616

17+
functionName(f) {
18+
if (typeof f !== 'function') {
19+
return '';
20+
}
21+
22+
if (f.name) {
23+
return f.name;
24+
}
25+
26+
const parts = f.toString().match(/^function\s*([^\s(]+)/);
27+
28+
if (parts) {
29+
return parts[1];
30+
}
31+
32+
return '';
33+
}
34+
1735
containsWord(testWord, words) {
36+
if (typeof testWord !== 'string') {
37+
return false;
38+
}
39+
1840
testWord = testWord.toLowerCase();
1941

2042
for (let i = 0; i < words.length; i++) {
@@ -27,6 +49,15 @@ class Utils {
2749
return false;
2850
}
2951

52+
isInputLikeComponent(element, inputNames = ['input', 'field', 'text']) {
53+
if (typeof element.type === 'function') {
54+
const hasInputLikeName = this.containsWord(this.functionName(element.type), inputNames);
55+
const spInputLike = this.takeProp(element.props, 'spInputLike', 'data-spInputLike');
56+
57+
return spInputLike || hasInputLikeName;
58+
}
59+
}
60+
3061
takeProp(source, ...fields) {
3162
for (let i = 0; i < fields.length; i++) {
3263
let fieldName = fields[i];
@@ -118,6 +149,18 @@ class Utils {
118149
return React.cloneElement(newElement, newOptions, newChildren);
119150
}
120151

152+
mapFormField(element, mappingFn, defaultValue) {
153+
if (this.isInputLikeComponent(element)) {
154+
if (element.props && element.props.name) {
155+
mappingFn(element.props.name, defaultValue);
156+
}
157+
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
158+
if (element.props.type !== 'submit') {
159+
mappingFn(element.props.name, defaultValue);
160+
}
161+
}
162+
}
163+
121164
getFormFieldMap(root, handler) {
122165
var fields = {};
123166

@@ -130,7 +173,7 @@ class Utils {
130173
name = elements.props.fieldName;
131174
}
132175

133-
if (!('name' in fields)) {
176+
if (!(name in fields)) {
134177
fields[name] = {
135178
element: field,
136179
defaultValue: defaultValue
@@ -152,7 +195,7 @@ class Utils {
152195
for (var key in fields) {
153196
var field = fields[key];
154197
var element = field.element;
155-
var elementType = typeof element.type === 'function' ? element.type.name : element.type;
198+
var elementType = typeof element.type === 'function' ? this.functionName(element.type) : element.type;
156199

157200
if (!(elementType in inverseMap)) {
158201
inverseMap[elementType] = {};

0 commit comments

Comments
 (0)