box-ui-elements-mlh
Version:
144 lines (123 loc) • 4.5 kB
JavaScript
// @flow
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import uniqueId from 'lodash/uniqueId';
import { Editor } from 'draft-js';
import type { EditorState } from 'draft-js';
import 'draft-js/dist/Draft.css';
import Tooltip from '../tooltip';
import commonMessages from '../../common/messages';
import './DraftJSEditor.scss';
const OptionalFormattedMessage = () => (
<span className="bdl-Label-optional">
(<FormattedMessage {...commonMessages.optional} />)
</span>
);
type Props = {
editorState: EditorState,
error?: ?Object,
hideLabel?: boolean,
inputProps: Object,
isDisabled?: boolean,
isRequired?: boolean,
label: React.Node,
onBlur: Function,
onChange: Function,
onFocus: Function,
onReturn?: Function,
placeholder?: string,
};
class DraftJSEditor extends React.Component<Props> {
static defaultProps = {
inputProps: {},
isRequired: false,
isFocused: false,
};
/**
* Calls onChange handler passed in
* @param {EditorState} editorState The new/updated editor state
* @returns {void}
*/
handleChange = (editorState: EditorState) => {
const { onChange } = this.props;
onChange(editorState);
};
handleBlur = (editorState: EditorState) => {
const { onBlur } = this.props;
onBlur(editorState);
};
/**
* Event handler which being passed to 'handleReturn' prop of DraftJSEditor
* This gives consumer the ability to handle return key event before DraftJSEditor handles it.
* @param {SyntheticKeyboardEvent} event
* @returns {string}
*/
handleReturn = (event: SyntheticKeyboardEvent<>) => {
const { onReturn, inputProps } = this.props;
if (onReturn && !inputProps['aria-activedescendant']) {
// Return 'handled' tells DraftJS Editor to not handle return key event,
// return 'not-handled' tells DraftJS Editor to continue handle the return key event.
// We encapsulate this DraftJS Editor specific contract here, and use true/fase
// to indicate whether to let DraftJS Editor handle return event or not in the upper level.
return onReturn(event) ? 'handled' : 'not-handled';
}
return 'not-handled';
};
labelID = uniqueId('label');
render() {
const {
editorState,
error,
hideLabel,
inputProps,
isDisabled,
isRequired,
label,
onFocus,
placeholder,
} = this.props;
const { handleBlur, handleChange } = this;
const classes = classNames({
'draft-js-editor': true,
'is-disabled bdl-is-disabled': isDisabled,
'show-error': !!error,
});
let a11yProps = {};
if (inputProps.role) {
a11yProps = {
ariaActiveDescendantID: inputProps['aria-activedescendant'],
ariaAutoComplete: inputProps['aria-autocomplete'],
ariaExpanded: inputProps['aria-expanded'],
ariaOwneeID: inputProps['aria-owns'],
role: inputProps.role,
};
}
return (
<div className={classes}>
<span className={classNames('bdl-Label', { 'accessibility-hidden': hideLabel })} id={this.labelID}>
{label}
{!isRequired && <OptionalFormattedMessage />}
</span>
<Tooltip isShown={!!error} position="bottom-left" text={error ? error.message : ''} theme="error">
{/* need div so tooltip can set aria-describedby */}
<div>
<Editor
{...a11yProps}
ariaLabelledBy={this.labelID}
editorState={editorState}
handleReturn={this.handleReturn}
onBlur={handleBlur}
onChange={handleChange}
onFocus={onFocus}
placeholder={placeholder}
readOnly={isDisabled}
stripPastedStyles
/>
</div>
</Tooltip>
</div>
);
}
}
export default DraftJSEditor;