Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ Customize the form by providing your own markup.
```

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

Expand Down Expand Up @@ -330,7 +332,7 @@ Specify `hideSocial` to hide the ability to register with a social provider.
<RegistrationForm hideSocial={true} />
```

Customize the form by providing your own markup.
Customize the form by providing your own markup.

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).

Expand Down
10 changes: 1 addition & 9 deletions src/components/ChangePasswordForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,7 @@ export default class ChangePasswordForm extends React.Component {
}
};

if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
if (element.props && element.props.name) {
tryMapFormField(element.props.name);
}
} else if (element.type === 'input' || element.type === 'textarea') {
if (element.props.type !== 'submit') {
tryMapFormField(element.props.name);
}
}
utils.mapFormField(element, tryMapFormField);
}

_spIfHandler(action, element) {
Expand Down
10 changes: 1 addition & 9 deletions src/components/LoginForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,7 @@ export default class LoginForm extends React.Component {
}
};

if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
if (element.props && element.props.name) {
tryMapFormField(element.props.name);
}
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
if (element.props.type !== 'submit') {
tryMapFormField(element.props.name);
}
}
utils.mapFormField(element, tryMapFormField);
}

_spIfHandler(action, element) {
Expand Down
10 changes: 1 addition & 9 deletions src/components/RegistrationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,15 +325,7 @@ export default class RegistrationForm extends React.Component {
}
};

if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
if (element.props && element.props.name) {
tryMapFormField(element.props.name);
}
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
if (element.props.type !== 'submit') {
tryMapFormField(element.props.name);
}
}
utils.mapFormField(element, tryMapFormField);
}

_spIfHandler(action, element) {
Expand Down
10 changes: 1 addition & 9 deletions src/components/ResetPasswordForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,7 @@ export default class ResetPasswordForm extends React.Component {
}
};

if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
if (element.props && element.props.name) {
tryMapFormField(element.props.name);
}
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
if (element.props.type !== 'submit') {
tryMapFormField(element.props.name);
}
}
utils.mapFormField(element, tryMapFormField);
}

_spIfHandler(action, element) {
Expand Down
12 changes: 1 addition & 11 deletions src/components/UserProfileForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,7 @@ export default class UserProfileForm extends React.Component {
utils.getFieldValue(this.state.defaultFields, element.props.name) :
undefined;

if (typeof element.type === 'function' && utils.containsWord(element.type.name, ['input', 'field', 'text'])) {
if (element.props && element.props.name) {
tryMapField(element.props.name, defaultValue);
}
} else if (element.type === 'input') {
if (element.props.type === 'submit') {
return;
}

tryMapField(element.props.name, defaultValue);
}
utils.mapFormField(element, tryMapField, defaultValue);
}

_spIfHandler(action, element) {
Expand Down
47 changes: 45 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,29 @@ class Utils {
s4() + '-' + s4() + s4() + s4();
}

functionName(f) {
if (typeof f !== 'function') {
return '';
}

if (f.name) {
return f.name;
}

const parts = f.toString().match(/^function\s*([^\s(]+)/);

if (parts) {
return parts[1];
}

return '';
}

containsWord(testWord, words) {
if (typeof testWord !== 'string') {
return false;
}

testWord = testWord.toLowerCase();

for (let i = 0; i < words.length; i++) {
Expand All @@ -25,6 +47,15 @@ class Utils {
return false;
}

isInputLikeComponent(element, inputNames = ['input', 'field', 'text']) {
if (typeof element.type === 'function') {
const hasInputLikeName = this.containsWord(this.functionName(element.type), inputNames);
const spInputLike = this.takeProp(element.props, 'spInputLike', 'data-spInputLike');

return spInputLike || hasInputLikeName;
}
}

takeProp(source, ...fields) {
for (let i = 0; i < fields.length; i++) {
let fieldName = fields[i];
Expand Down Expand Up @@ -116,6 +147,18 @@ class Utils {
return React.cloneElement(newElement, newOptions, newChildren);
}

mapFormField(element, mappingFn, defaultValue) {
if (this.isInputLikeComponent(element)) {
if (element.props && element.props.name) {
mappingFn(element.props.name, defaultValue);
}
} else if (['input', 'textarea'].indexOf(element.type) > -1) {
if (element.props.type !== 'submit') {
mappingFn(element.props.name, defaultValue);
}
}
}

getFormFieldMap(root, handler) {
var fields = {};

Expand All @@ -128,7 +171,7 @@ class Utils {
name = elements.props.fieldName;
}

if (!('name' in fields)) {
if (!(name in fields)) {
fields[name] = {
element: field,
defaultValue: defaultValue
Expand All @@ -150,7 +193,7 @@ class Utils {
for (var key in fields) {
var field = fields[key];
var element = field.element;
var elementType = typeof element.type === 'function' ? element.type.name : element.type;
var elementType = typeof element.type === 'function' ? this.functionName(element.type) : element.type;

if (!(elementType in inverseMap)) {
inverseMap[elementType] = {};
Expand Down