Skip to content

Instantly share code, notes, and snippets.

@bjankord
Last active June 21, 2017 18:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bjankord/efd75a6812f3ccaf972ce5687c6b4b8a to your computer and use it in GitHub Desktop.
Save bjankord/efd75a6812f3ccaf972ce5687c6b4b8a to your computer and use it in GitHub Desktop.
Toggler with inert aria-hidden content
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import AnimateHeight from 'react-animate-height';
import 'terra-base/lib/baseStyles';
import './Toggler.scss';
const propTypes = {
/**
* Content in the body of the toggler component that will be expanded or collapsed
*/
children: PropTypes.node.isRequired,
/**
* When set, will animate the toggler component as it is expanded or collapsed
*/
isAnimated: PropTypes.bool,
/**
* Used to expand or collapse toggler content
*/
isOpen: PropTypes.bool,
onAnimationEnd: PropTypes.func,
onAnimationStart: PropTypes.func,
};
const defaultProps = {
children: null,
isAnimated: false,
isOpen: false,
};
class Toggler extends React.Component {
constructor(props) {
super(props);
this.handleOnAnimationEnd = this.handleOnAnimationEnd.bind(this);
this.handleOnAnimationStart = this.handleOnAnimationStart.bind(this);
}
handleOnAnimationEnd(open) {
if (!open) {
if (this.contentContainer) {
const inertLinks = this.contentContainer.getElementsByTagName('a');
const inertButtons = this.contentContainer.getElementsByTagName('button');
const inertInputs = this.contentContainer.getElementsByTagName('input');
const inertSelects = this.contentContainer.getElementsByTagName('select');
const inertTextareas = this.contentContainer.getElementsByTagName('textarea');
const inertFormElements = [...inertButtons, ...inertInputs, ...inertSelects, ...inertTextareas];
// Make links inert
let l = 0;
while (l < inertLinks.length) {
inertLinks[l].setAttribute('tabindex', '-1');
l += 1;
}
// Make form elements inert
let f = 0;
while (f < inertFormElements.length) {
inertFormElements[f].setAttribute('disabled', '');
f += 1;
}
}
}
}
handleOnAnimationStart(open) {
if (open) {
if (this.contentContainer) {
const inertLinks = this.contentContainer.getElementsByTagName('a');
const inertButtons = this.contentContainer.getElementsByTagName('button');
const inertInputs = this.contentContainer.getElementsByTagName('input');
const inertSelects = this.contentContainer.getElementsByTagName('select');
const inertTextareas = this.contentContainer.getElementsByTagName('textarea');
const inertFormElements = [...inertButtons, ...inertInputs, ...inertSelects, ...inertTextareas];
// Make links inert
let l = 0;
while (l < inertLinks.length) {
inertLinks[l].removeAttribute('tabindex');
l += 1;
}
// Make form elements inert
let f = 0;
while (f < inertFormElements.length) {
inertFormElements[f].removeAttribute('disabled');
f += 1;
}
}
}
}
render() {
const { isAnimated, isOpen, children, onAnimationEnd, onAnimationStart, ...customProps } = this.props;
const attributes = Object.assign({}, customProps);
const TogglerClassNames = classNames([
'terra-Toggler',
attributes.className,
]);
const height = isOpen ? 'auto' : '0';
let body;
if (isAnimated) {
body = (
<div ref={(div) => { this.contentContainer = div; }}>
<AnimateHeight
duration={250}
height={height}
onAnimationEnd={this.handleOnAnimationEnd(isOpen)}
onAnimationStart={this.handleOnAnimationStart(isOpen)}
>
{children}
</AnimateHeight>
</div>
);
} else {
body = (
isOpen && children
);
}
return (
<div
{...attributes}
className={TogglerClassNames}
aria-hidden={!isOpen}
ref={(div) => { this.toggleDiv = div; }}
>
{body}
</div>
);
}
}
class Toggler extends React.Component {
constructor(props) {
super(props);
this.state = {
display: 'block',
};
this.handleOnAnimationEnd = this.handleOnAnimationEnd.bind(this);
this.handleOnAnimationStart = this.handleOnAnimationStart.bind(this);
}
handleOnAnimationEnd(open) {
if (!open) {
setTimeout(function() { this.setState({ display: 'none' }); }.bind(this), 250);
}
}
handleOnAnimationStart(open) {
if (open) {
setTimeout(function() { this.setState({ display: 'block' }); }.bind(this), 0);
}
}
render() {
const { isAnimated, isOpen, children, onAnimationEnd, onAnimationStart, ...customProps } = this.props;
const attributes = Object.assign({}, customProps);
const TogglerClassNames = classNames([
'terra-Toggler',
attributes.className,
]);
const height = isOpen ? 'auto' : '0';
let body;
if (isAnimated) {
body = (
<AnimateHeight
duration={250}
height={height}
onAnimationEnd={this.handleOnAnimationEnd(isOpen)}
onAnimationStart={this.handleOnAnimationStart(isOpen)}
style={{ display: this.state.display }}
>
{children}
</AnimateHeight>
);
} else {
body = (
isOpen && children
);
}
return (
<div
{...attributes}
className={TogglerClassNames}
aria-hidden={!isOpen}
ref={(div) => { this.toggleDiv = div; }}
>
{body}
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment