All files / components/Underline Underline.tsx

0% Statements 0/22
0% Branches 0/14
0% Functions 0/1
0% Lines 0/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98                                                                                                                                                                                                   
import _ from 'lodash';
import React from 'react';
import PropTypes from 'react-peek/prop-types';
import { partitionText } from '../../util/text-manipulation';
import { lucidClassNames } from '../../util/style-helpers';
import { StandardProps, omitProps } from '../../util/component-types';
 
const cx = lucidClassNames.bind('&-Underline');
 
const { node, string, instanceOf, oneOfType } = PropTypes;
 
const matchAllRegexp = /^.*$/;
 
export interface IUnderlineProps
	extends StandardProps,
		React.DetailedHTMLProps<
			React.HTMLAttributes<HTMLSpanElement>,
			HTMLSpanElement
		> {
	/** The first match of the given pattern has the underline style applied to it. */
	match?: string | RegExp;
}
 
export const Underline = ({
	className,
	children,
	match,
	...passThroughs
}: IUnderlineProps): React.ReactElement => {
	if (!_.isRegExp(match)) {
		if (_.isString(match)) {
			match = new RegExp(_.escapeRegExp(match), 'i');
		} else {
			match = matchAllRegexp;
		}
	}
 
	if (!_.isString(children)) {
		return (
			<span
				className={cx('&', className)}
				{...omitProps(passThroughs, undefined, _.keys(Underline.propTypes))}
			>
				<span
					style={
						match === matchAllRegexp
							? { textDecoration: 'underline' }
							: undefined
					}
				>
					{children}
				</span>
			</span>
		);
	}
 
	const [pre, matchText, post] = partitionText(children, match);
 
	return (
		<span
			className={cx('&', className)}
			{...omitProps(passThroughs, undefined, _.keys(Underline.propTypes))}
		>
			{[
				pre && <span key='pre'>{pre}</span>,
				matchText && (
					<span key='match' style={{ textDecoration: 'underline' }}>
						{matchText}
					</span>
				),
				post && <span key='post'>{post}</span>,
			]}
		</span>
	);
};
 
Underline.displayName = 'Underline';
Underline.peek = {
	description: `
		Underlines a portion of text that matches a given pattern
	`,
	categories: ['controls', 'selectors'],
};
Underline.propTypes = {
	className: string`
		Appended to the component-specific class names set on the root element.
	`,
	children: node`
		Text to be partially or fully underlined. If non-text is passed as
		children, it will not attempt to match the given pattern.
	`,
	match: oneOfType([string, instanceOf(RegExp)])`
		The first match of the given pattern has the underline style applied to it.
	`,
};
 
export default Underline;