diff --git a/README.md b/README.md index fb22f25..04ac2d1 100644 --- a/README.md +++ b/README.md @@ -119,16 +119,17 @@ export default MyComponent ## Props -| Prop | Description | Type | Default | Required? | -| ------------- | ----------------------------------------------------------------------------- | ----------------- | ----------- | --------- | -| `suggestions` | A collection of HTML elements or React components used for search suggestions | React.ReactNode[] | | Y | -| `id` | ID for entire component | string | `undefined` | N | -| `className` | Optional class name to style component | string | `''` | N | -| `name` | Input name | string | `'q'` | N | -| `placeholder` | Input placeholder | string | `'Search'` | N | -| `autoFocus` | Input autoFocus | boolean | `false` | N | -| `onChange` | Input onChange handler | function | `undefined` | N | -| `withStyles` | Basic styling for the component | boolean | `false` | N | +| Prop | Description | Type | Default | Required? | +| ------------------- | -------------------------------------------------------------------------------------- | ----------------- | ----------- | --------- | +| `suggestions` | A collection of HTML elements or React components used for search suggestions | React.ReactNode[] | | Y | +| `id` | ID for entire component | string | `undefined` | N | +| `className` | Optional class name to style component | string | `''` | N | +| `name` | Input name | string | `'q'` | N | +| `placeholder` | Input placeholder | string | `'Search'` | N | +| `autoFocus` | Input autoFocus | boolean | `false` | N | +| `onChange` | Input onChange handler | function | `undefined` | N | +| `withStyles` | Basic styling for the component | boolean | `false` | N | +| `highlightKeywords` | Highlight letters that match search term by wrapping a `` tag around suggestions | boolean | `false` | N | ## Styling diff --git a/package.json b/package.json index df31f79..427c462 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@adhamu/react-search-suggestions", - "version": "2.1.3", + "version": "2.2.0", "description": "A React input component with pluggable search suggestions.", "keywords": [ "react", diff --git a/src/__tests__/SearchSuggestions.test.tsx b/src/__tests__/SearchSuggestions.test.tsx index 7cb659c..e12436c 100644 --- a/src/__tests__/SearchSuggestions.test.tsx +++ b/src/__tests__/SearchSuggestions.test.tsx @@ -4,7 +4,7 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import SearchSuggestions from '../SearchSuggestions' -import * as getElementText from '../elementText' +import * as elementText from '../elementText' const suggestions = ['reddit', 'facebook', 'twitter'].map(word => ( @@ -13,7 +13,8 @@ const suggestions = ['reddit', 'facebook', 'twitter'].map(word => ( )) describe('SearchSuggestions', () => { - const mockGetElementText = jest.spyOn(getElementText, 'getElementText') + const mockGetElementText = jest.spyOn(elementText, 'getElementText') + const mockWrapElementText = jest.spyOn(elementText, 'wrapElementText') beforeEach(jest.clearAllMocks) @@ -65,6 +66,12 @@ describe('SearchSuggestions', () => { it('does not show suggestions if no input has been entered', () => { expect(screen.queryByRole('list')).not.toBeInTheDocument() }) + + it('does not wrap search suggestions', () => { + userEvent.type(screen.getByRole('searchbox'), 't') + + expect(mockWrapElementText).not.toHaveBeenCalled() + }) }) describe('renders correctly with custom options', () => { @@ -111,6 +118,14 @@ describe('SearchSuggestions', () => { // eslint-disable-next-line testing-library/no-node-access expect(container.firstChild).toHaveClass('react-search') }) + + it('calls wrapElementText when highlightKeywords provided', () => { + render() + + userEvent.type(screen.getByRole('searchbox'), 't') + + expect(mockWrapElementText).toHaveBeenCalledTimes(2) + }) }) it('fires an onChange event if provided', () => { diff --git a/src/__tests__/elementText.test.tsx b/src/__tests__/elementText.test.tsx index 21631d9..2da19f7 100644 --- a/src/__tests__/elementText.test.tsx +++ b/src/__tests__/elementText.test.tsx @@ -1,46 +1,89 @@ import React from 'react' -import { getElementText } from '../elementText' +import { render } from '@testing-library/react' -describe('getElementText', () => { - const Test = ({ children }: { children?: React.ReactNode }) => ( -
{children}
- ) +import { getElementText, wrapElementText } from '../elementText' - it('handles strings', () => { - expect(getElementText('This is a test')).toBe('This is a test') - }) +describe('elementText', () => { + describe('getElementText', () => { + const Test = ({ children }: { children?: React.ReactNode }) => ( +
{children}
+ ) - it('handles numbers', () => { - expect(getElementText(100)).toBe(100) - }) + it('handles strings', () => { + expect(getElementText('This is a test')).toBe('This is a test') + }) - it('handles nested markup', () => { - expect( - getElementText( -
    -
  • Option 1
  • -
  • Option 2
  • -
  • Option 3
  • -
  • Option 4
  • -
- ) - ).toBe('Option 1 Option 2 Option 3 Option 4') + it('handles numbers', () => { + expect(getElementText(100)).toBe(100) + }) + + it('handles nested markup', () => { + expect( + getElementText( +
    +
  • Option 1
  • +
  • Option 2
  • +
  • Option 3
  • +
  • Option 4
  • +
+ ) + ).toBe('Option 1 Option 2 Option 3 Option 4') + }) + + it('handles React elements', () => { + expect( + getElementText( + + Level 1 + Level 2 + Level 3 + + ) + ).toBe('Level 1 Level 2 Level 3') + }) + + it('returns undefined if no text found', () => { + expect(getElementText()).toBeUndefined() + }) }) - it('handles React elements', () => { - expect( - getElementText( - - Level 1 - Level 2 - Level 3 - + describe('wrapElementText', () => { + it('can handle strings', () => { + const Result = () => <>{wrapElementText(
Level One
, 'one')} + + const { container } = render() + + expect(container.innerHTML).toBe('
Level One
') + }) + + it('can handle elements with children', () => { + const Result = () => ( + <> + {wrapElementText( +
+ +
, + 'twit' + )} + ) - ).toBe('Level 1 Level 2 Level 3') - }) - it('returns undefined if no text found', () => { - expect(getElementText()).toBeUndefined() + const { container } = render() + + expect(container.innerHTML).toBe( + '
' + ) + }) }) }) diff --git a/src/example/App.tsx b/src/example/App.tsx index fad7d75..6ea4b94 100644 --- a/src/example/App.tsx +++ b/src/example/App.tsx @@ -54,20 +54,8 @@ const suggestions = [ 'pressure', 'cooperate', ].map(word => ( - { - if (e.key === 'Enter') { - console.log(word) - } - }} - onClick={() => { - console.log(word) - }} - > -
-
{word}
-
+
+ {word} ))