Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
adhamu committed Mar 18, 2022
1 parent 9b88f69 commit ba44e3e
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 51 deletions.
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# React Search Suggestions
# React Input Suggestions

<div align="center">
<img src="https://raw.githubusercontent.com/adhamu/react-search-suggestions/main/demo.png" alt="demo"/>
<img src="https://raw.githubusercontent.com/adhamu/react-input-suggestions/main/demo.png" alt="demo"/>

A React input component with pluggable search suggestions.
A React input component with pluggable search suggestions and autocomplete.

Also includes arrow key navigation through results.

[![Build](https://github.com/adhamu/react-search-suggestions/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/adhamu/react-search-suggestions/actions)
[![Build](https://github.com/adhamu/react-input-suggestions/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/adhamu/react-search-suggestions/actions)

</div>

Expand All @@ -18,17 +18,17 @@ Also includes arrow key navigation through results.
## Installation

```shell
yarn add react-search-suggestions
yarn add react-input-suggestions
```

## Usage

```jsx
import React from 'react'
import { SearchSuggestions } from 'react-search-suggestions'
import { InputSuggestions } from 'react-input-suggestions'

const MyComponent = () => (
<SearchSuggestions
<InputSuggestions
autoFocus
suggestions={[
'polite',
Expand Down Expand Up @@ -80,14 +80,14 @@ If you wanted to do something else `onClick` or `onKeyDown`, you could do someth

```jsx
import React from 'react'
import { SearchSuggestions } from 'react-search-suggestions'
import { InputSuggestions } from 'react-input-suggestions'

const customFunction = (arg: string) => {
console.log(arg)
}

const MyComponent = () => (
<SearchSuggestions
<InputSuggestions
autoFocus
suggestions={[
'polite',
Expand Down Expand Up @@ -119,23 +119,24 @@ 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 |
| `highlightKeywords` | Highlight letters that match search term by wrapping a `<mark>` tag around suggestions | 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 |
| `type` | Input type | 'search' \| 'text' | `search` | 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 `<mark>` tag around suggestions | boolean | `false` | N |

## Styling

By default, the component comes with almost no styles. Given the semantic nature of the markup, it is quite easy to target these with CSS. As mentioned above, you can provide a `className` to the component for this.

Alternatively, you can set the `withStyles` prop to `true` to achieve some very basic styling. An example of this can be seen on [GitHub Pages](http://adhamu.github.io/react-search-suggestions/).
Alternatively, you can set the `withStyles` prop to `true` to achieve some very basic styling. An example of this can be seen on [GitHub Pages](http://adhamu.github.io/react-input-suggestions/).

**Important**: The `:focus` attribute on each top level element's search suggestion is what powers the active state of a selected element. Refer to the [HTML Structure](#html-structure) above to correctly determine any CSS selectors.

Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
{
"name": "react-search-suggestions",
"name": "react-input-suggestions",
"version": "1.0.0",
"description": "A React input component with pluggable search suggestions.",
"description": "A React input component with pluggable suggestions and autocomplete",
"keywords": [
"react",
"autcomplete",
"autocomplete",
"search",
"suggestions",
"typescript",
"arrow key navigation",
"esbuild"
"esbuild",
"input"
],
"repository": {
"type": "git",
"url": "https://github.com/adhamu/react-search-suggestions"
"url": "https://github.com/adhamu/react-input-suggestions"
},
"license": "MIT",
"author": "Amit Dhamu <[email protected]>",
Expand Down
10 changes: 3 additions & 7 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">

<head>
<title>React Search Suggestions</title>
<title>React Input Suggestions</title>
<meta charset="utf-8" />
<meta name="description" content="React Search Suggestions" />
<meta name="description" content="React Input Suggestions" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap" rel="stylesheet" />
Expand All @@ -21,10 +21,6 @@
margin-bottom: 1.5rem;
}

.searchSuggestions ul {
top: calc(100% - 3px);
}

mark {
background: transparent;
font-weight: bold;
Expand All @@ -34,7 +30,7 @@
</head>

<body>
<h1>React Search Suggestions</h1>
<h1>React Input Suggestions</h1>
<main id="app"></main>
<script type="module" src="./script.js"></script>
</body>
Expand Down
7 changes: 4 additions & 3 deletions src/SearchSuggestions.tsx → src/InputSuggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { getElementText, wrapElementText } from './elementText'
import { Styled } from './styled'
import { useSuggestions } from './useSuggestions'

const SearchSuggestions = ({
const InputSuggestions = ({
suggestions,
type = 'search',
name = 'q',
placeholder = 'Search',
autoFocus = false,
Expand Down Expand Up @@ -37,7 +38,7 @@ const SearchSuggestions = ({
<Styled id={id} className={className} withTheme={withTheme}>
<input
ref={inputSearchRef}
type="search"
type={type}
name={name}
placeholder={placeholder}
autoFocus={autoFocus}
Expand Down Expand Up @@ -76,4 +77,4 @@ const SearchSuggestions = ({
)
}

export default SearchSuggestions
export default InputSuggestions
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import SearchSuggestions from '../SearchSuggestions'
import InputSuggestions from '../InputSuggestions'
import * as elementText from '../elementText'

const suggestions = ['reddit', 'facebook', 'twitter'].map(word => (
Expand All @@ -12,15 +12,15 @@ const suggestions = ['reddit', 'facebook', 'twitter'].map(word => (
</a>
))

describe('SearchSuggestions', () => {
describe('InputSuggestions', () => {
const mockGetElementText = jest.spyOn(elementText, 'getElementText')
const mockWrapElementText = jest.spyOn(elementText, 'wrapElementText')

beforeEach(jest.clearAllMocks)

describe('renders correctly with default options', () => {
beforeEach(() => {
render(<SearchSuggestions suggestions={suggestions} />)
render(<InputSuggestions suggestions={suggestions} />)
})

it('has the correct type', () => {
Expand Down Expand Up @@ -75,15 +75,21 @@ describe('SearchSuggestions', () => {
})

describe('renders correctly with custom options', () => {
it('has the correct type', () => {
render(<InputSuggestions suggestions={suggestions} type="text" />)

expect(screen.getByRole('textbox')).toHaveAttribute('type', 'text')
})

it('sets the name attribute', () => {
render(<SearchSuggestions suggestions={suggestions} name="search" />)
render(<InputSuggestions suggestions={suggestions} name="search" />)

expect(screen.getByRole('searchbox')).toHaveAttribute('name', 'search')
})

it('sets the placeholder attribute', () => {
render(
<SearchSuggestions
<InputSuggestions
suggestions={suggestions}
placeholder="Enter keywords"
/>
Expand All @@ -96,14 +102,14 @@ describe('SearchSuggestions', () => {
})

it('sets autoFocus', () => {
render(<SearchSuggestions suggestions={suggestions} autoFocus />)
render(<InputSuggestions suggestions={suggestions} autoFocus />)

expect(screen.getByRole('searchbox')).toHaveFocus()
})

it('sets an ID', () => {
const { container } = render(
<SearchSuggestions suggestions={suggestions} id="react-search" />
<InputSuggestions suggestions={suggestions} id="react-search" />
)

// eslint-disable-next-line testing-library/no-node-access
Expand All @@ -112,15 +118,15 @@ describe('SearchSuggestions', () => {

it('sets a className', () => {
const { container } = render(
<SearchSuggestions suggestions={suggestions} className="react-search" />
<InputSuggestions suggestions={suggestions} className="react-search" />
)

// eslint-disable-next-line testing-library/no-node-access
expect(container.firstChild).toHaveClass('react-search')
})

it('calls wrapElementText when highlightKeywords provided', () => {
render(<SearchSuggestions suggestions={suggestions} highlightKeywords />)
render(<InputSuggestions suggestions={suggestions} highlightKeywords />)

userEvent.type(screen.getByRole('searchbox'), 't')

Expand All @@ -132,7 +138,7 @@ describe('SearchSuggestions', () => {
const mockOnChange = jest.fn()

render(
<SearchSuggestions suggestions={suggestions} onChange={mockOnChange} />
<InputSuggestions suggestions={suggestions} onChange={mockOnChange} />
)

expect(mockOnChange).not.toHaveBeenCalled()
Expand All @@ -144,7 +150,7 @@ describe('SearchSuggestions', () => {
})

it('shows filtered search suggestions based on input entered', () => {
render(<SearchSuggestions suggestions={suggestions} />)
render(<InputSuggestions suggestions={suggestions} />)

expect(screen.queryByRole('list')).not.toBeInTheDocument()

Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/styled.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { render } from '@testing-library/react'

import { Styled } from '../styled'

describe('Search Suggestion Styles', () => {
describe('Input Suggestion Styles', () => {
it('has the correct styles without theme', () => {
const { container } = render(<Styled>Hello world</Styled>)

Expand Down
4 changes: 2 additions & 2 deletions src/example/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'

import { SearchSuggestions } from '..'
import { InputSuggestions } from '..'

const suggestions = [
'polite',
Expand Down Expand Up @@ -60,7 +60,7 @@ const suggestions = [
))

const App = (): JSX.Element => (
<SearchSuggestions
<InputSuggestions
suggestions={suggestions}
autoFocus
withTheme
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export type { Props } from './types'
export { useSuggestions } from './useSuggestions'
export { default as SearchSuggestions } from './SearchSuggestions'
export { default as InputSuggestions } from './InputSuggestions'
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type React from 'react'

export type Props = {
id?: string
type?: 'search' | 'text'
suggestions: React.ReactNode[]
className?: string
name?: string
Expand Down

0 comments on commit ba44e3e

Please sign in to comment.