I'm having a strange problem that I have not encountered before while using React.
I'm creating a Button component that needs to be backwards compatible with a legacy Button component. This is requiring me to do some unfortunate things in my implementation...
One of the issues with the legacy Button is that there is a typo for passing in a tabIndex. The legacy Button's property for tabIndex is tapIndex.
This should be easy to map using something like...
<Button tabIndex={this.props.tabIndex || this.props.tapIndex || 0}></Button>
But for some reason this is not the case...
If the tabIndex attribute of the child is mapped to this.props.tapIndex the rendered element will not have a tabIndex set. In short:
This works:
<Button
...
tabIndex={this.props.tabIndex}
...
>Hello</Button>
This does not:
<Button
...
tabIndex={this.props.tapIndex}
...
>Hello</Button>
I have debugged the execution of my component and this.props.tapIndex is indeed set correctly and equal to the intended value. But again, the tabIndex attribute does not exist on the rendered element in this case.
Button.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import {
stylesToStyled,
cleanClassNames,
extractProps
} from '@ns/utils';
import getKind, {
KINDS,
KIND_DICT,
PRESENTATION_DICT,
BOOLEAN_PROPS
} from './utils';
import PrimaryButton from './components/PrimaryButton';
import SecondaryButton from './components/SecondaryButton';
import TertiaryButton from './components/TertiaryButton';
const TYPES = ['button', 'submit', 'reset'];
const COMPONENT_DICT = {
[KIND_DICT.PRIMARY]: PrimaryButton,
[KIND_DICT.SECONDARY]: SecondaryButton,
[KIND_DICT.TERTIARY]: TertiaryButton,
};
class Button extends Component {
componentDidMount() {
this.setAnalyticsMessageOnDOM();
}
componentDidUpdate() {
this.setAnalyticsMessageOnDOM();
}
get StyledButton() {
// Get the appropriate Component
const kind = getKind(this.props, extractProps(
this.props.className,
BOOLEAN_PROPS
));
// Add passed in styles
const styles = stylesToStyled(this.props.style);
return styled(COMPONENT_DICT[kind])(
styles.properties,
...styles.values
);
}
get tabIndex() {
if (Number.isInteger(this.props.tabIndex)) {
return this.props.tabIndex;
}
if (Number.isInteger(this.props.tapIndex)) {
return this.props.tapIndex;
}
return 0;
}
setAnalyticsMessageOnDOM() {
// Terrible Hack
this.buttonRef.setAttribute(
'analyticstrack',
this.props.analyticstrackmessage
);
}
render () {
const {
className,
children,
...props
} = this.props;
const { StyledButton } = this;
return (
<StyledButton
className={cleanClassNames(className, BOOLEAN_PROPS)}
tabIndex={this.tabIndex}
innerRef={el => {this.buttonRef = el}}
{...props}
>
{children}
</StyledButton>
);
}
}
Button.propTypes = {
[KIND_DICT.PRIMARY]: PropTypes.bool,
[KIND_DICT.SECONDARY]: PropTypes.bool,
[KIND_DICT.TERTIARY]: PropTypes.bool,
[PRESENTATION_DICT.DISABLED]: PropTypes.bool,
[PRESENTATION_DICT.INVERTED]: PropTypes.bool,
children: PropTypes.node.isRequired,
className: PropTypes.string,
style: PropTypes.object,
onClick: PropTypes.func,
tabIndex: PropTypes.number,
// Deprectation Warning: Typo that is being used in production, use tabindex.
tapIndex: PropTypes.number,
type: PropTypes.oneOf(TYPES),
// Deprectation Warning: Use tabindex.
kind: PropTypes.oneOf(KINDS),
analyticstrackmessage: PropTypes.string,
};
Button.defaultProps = {
[KIND_DICT.PRIMARY]: false,
[KIND_DICT.SECONDARY]: false,
[KIND_DICT.TERTIARY]: false,
[PRESENTATION_DICT.DISABLED]: false,
[PRESENTATION_DICT.INVERTED]: false,
className: undefined,
style: {},
onClick: () => {},
tabIndex: undefined,
tapIndex: undefined,
type: TYPES[0],
kind: undefined,
analyticstrackmessage: undefined,
};
export { TYPES };
export default Button;
If anyone has any ideas as to what may be causing this please feel free to comment or answer. Anything will be helpful.
Thank you in advance.