Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextHighlight: Convert component to TypeScript #41698

Merged
merged 13 commits into from
Jun 30, 2022
4 changes: 4 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Internal

- `TextHighlight`: Convert to TypeScript ([#41698](https://github.com/WordPress/gutenberg/pull/41698)).

## 19.14.0 (2022-06-29)

### Bug Fix
Expand Down
12 changes: 6 additions & 6 deletions packages/components/src/text-highlight/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ const MyTextHighlight = () => (

The component accepts the following props.

### text
### `highlight`: `string`

The string of text to be tested for occurrences of then given `highlight`.
The string to search for and highlight within the `text`. Case insensitive. Multiple matches.

- Type: `String`
- Required: Yes
- Default: `''`

### highlight
### `text`: `string`

The string to search for and highlight within the `text`. Case insensitive. Multiple matches.
The string of text to be tested for occurrences of then given `highlight`.

- Type: `String`
- Required: Yes
- Default: `''`
28 changes: 0 additions & 28 deletions packages/components/src/text-highlight/index.js

This file was deleted.

49 changes: 49 additions & 0 deletions packages/components/src/text-highlight/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* External dependencies
*/
import { escapeRegExp } from 'lodash';

/**
* WordPress dependencies
*/
import { createInterpolateElement } from '@wordpress/element';

/**
* Internal dependencies
*/
import type { TextHighlightProps } from './types';

/**
* Highlights occurrences of a given string within another string of text. Wraps
* each match with a `<mark>` tag which provides browser default styling.
*
* ```jsx
* import { TextHighlight } from '@wordpress/components';
*
* const MyTextHighlight = () => (
* <TextHighlight
* text="Why do we like Gutenberg? Because Gutenberg is the best!"
* highlight="Gutenberg"
* />
* );
* ```
*/
export const TextHighlight = ( props: TextHighlightProps ) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting example of how ref forwarding and using the WordPressComponentProps type don't make much sense with the current way the component is written (cc @mirka )

const { text = '', highlight = '' } = props;
walbo marked this conversation as resolved.
Show resolved Hide resolved
const trimmedHighlightText = highlight.trim();

if ( ! trimmedHighlightText ) {
return <>{ text }</>;
walbo marked this conversation as resolved.
Show resolved Hide resolved
}

const regex = new RegExp(
`(${ escapeRegExp( trimmedHighlightText ) })`,
'gi'
);

return createInterpolateElement( text.replace( regex, '<mark>$&</mark>' ), {
mark: <mark />,
} );
};

export default TextHighlight;
28 changes: 0 additions & 28 deletions packages/components/src/text-highlight/stories/index.js

This file was deleted.

33 changes: 33 additions & 0 deletions packages/components/src/text-highlight/stories/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* External dependencies
*/
import type { ComponentMeta, ComponentStory } from '@storybook/react';

/**
* Internal dependencies
*/
import TextHighlight from '..';

const meta: ComponentMeta< typeof TextHighlight > = {
component: TextHighlight,
title: 'Components/TextHighlight',
parameters: {
controls: {
expanded: true,
},
docs: { source: { state: 'open' } },
},
};
export default meta;

const Template: ComponentStory< typeof TextHighlight > = ( args ) => {
return <TextHighlight { ...args } />;
};

export const Default: ComponentStory< typeof TextHighlight > = Template.bind(
{}
);
Default.args = {
text: 'We call the new editor Gutenberg. The entire editing experience has been rebuilt for media rich pages and posts.',
highlight: 'Gutenberg',
};
120 changes: 0 additions & 120 deletions packages/components/src/text-highlight/test/index.js

This file was deleted.

93 changes: 93 additions & 0 deletions packages/components/src/text-highlight/test/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* External dependencies
*/
import { render } from '@testing-library/react';

/**
* Internal dependencies
*/
import TextHighlight from '..';

const getMarks = ( container: Element ) =>
// Use querySelectorAll because the `mark` role is not officially supported
// yet. This should be changed to `getByRole` when it is.
Array.from( container.querySelectorAll( 'mark' ) );
walbo marked this conversation as resolved.
Show resolved Hide resolved

const defaultText =
'We call the new editor Gutenberg. The entire editing experience has been rebuilt for media rich pages and posts.';

describe( 'TextHighlight', () => {
describe( 'Basic rendering', () => {
it.each( [ [ 'Gutenberg' ], [ 'media' ] ] )(
'should highlight the singular occurance of the text "%s" in the text if it exists',
( highlight ) => {
const { container } = render(
<TextHighlight
text={ defaultText }
highlight={ highlight }
/>
);

const highlightedEls = getMarks( container );

highlightedEls.forEach( ( el ) => {
expect( el ).toHaveTextContent(
new RegExp( `^${ highlight }$` )
);
} );
}
);

it( 'should highlight multiple occurances of the string every time it exists in the text', () => {
const highlight = 'edit';

const { container } = render(
<TextHighlight text={ defaultText } highlight={ highlight } />
);

const highlightedEls = getMarks( container );

expect( highlightedEls ).toHaveLength( 2 );

highlightedEls.forEach( ( el ) => {
expect( el.textContent ).toEqual(
expect.stringContaining( highlight )
);
} );
} );

it( 'should highlight occurances of a string regardless of capitalisation', () => {
// Note that `The` occurs twice in the default text, once in
// lowercase and once capitalized.
const highlight = 'The';

const { container } = render(
<TextHighlight text={ defaultText } highlight={ highlight } />
);

const highlightedEls = getMarks( container );

expect( highlightedEls ).toHaveLength( 2 );

// Make sure the matcher is case insensitive, since the test should
// match regardless of the case of the string.
const regex = new RegExp( highlight, 'i' );

highlightedEls.forEach( ( el ) => {
expect( el.innerHTML ).toMatch( regex );
} );
} );

it( 'should not highlight a string that is not in the text', () => {
const highlight = 'Antidisestablishmentarianism';

const { container } = render(
<TextHighlight text={ defaultText } highlight={ highlight } />
);

const highlightedEls = getMarks( container );

expect( highlightedEls ).toHaveLength( 0 );
} );
} );
} );
Loading