diff --git a/CONTRIBUTIONS.md b/CONTRIBUTIONS.md
index e69de29..86f4543 100644
--- a/CONTRIBUTIONS.md
+++ b/CONTRIBUTIONS.md
@@ -0,0 +1,138 @@
+# Contributing to React API Search
+
+Thank you for considering contributing to **React API Search**! We welcome contributions from the community and appreciate your interest in improving this project.
+
+## How to Contribute
+
+There are several ways you can contribute to this project:
+
+1. **Report Bugs**
+2. **Suggest Features**
+3. **Fix Bugs or Implement Features**
+4. **Update Documentation**
+5. **Improve Tests**
+
+# Fork the repository on GitHub and clone it
+
+```bash
+git clone https://github.com/ghosnkarl/react-api-search.git
+cd react-api-search
+```
+
+## Setting Up the Development Environment
+
+1. Install dependencies:
+
+ ```bash
+ npm install
+ ```
+
+2. Since the library cannot run as a standalone app, use \`npm link\` to test it in a separate project:
+
+ ```bash
+ npm link
+ ```
+
+3. In your test project (e.g., a React app where you want to test the library), link the library:
+
+ ```bash
+ cd path-to-your-test-project
+ npm link react-api-search
+ ```
+
+4. Now, import the library in your test project and test the functionality.
+
+5. To unlink after testing:
+
+ ```bash
+ npm unlink react-api-search
+ ```
+
+6. To run tests:
+
+ ```bash
+ npm test
+ ```
+
+## Making Changes
+
+1. **Create a new branch**:
+ Always create a new branch for your changes. Use a descriptive name for the branch that reflects the feature or bug you're working on.
+
+ ```bash
+ git checkout -b feature-name
+ ```
+
+2. **Make your changes**:
+ Edit the necessary files, and follow the existing code style and conventions.
+
+3. **Write tests** (if applicable):
+ If your change involves new functionality or fixes a bug, please add tests. The project uses [Jest](https://jestjs.io/) for testing.
+
+4. **Commit your changes**:
+ Commit your changes with a clear, concise message that explains the purpose of your modification.
+
+ ```bash
+ git add .
+ git commit -m "Add feature XYZ"
+ ```
+
+5. **Push your changes**:
+ Push your branch to your forked repository.
+
+ ```bash
+ git push origin feature-name
+ ```
+
+6. **Open a Pull Request**:
+ Go to the [GitHub repository](https://github.com/ghosnkarl/react-api-search) and open a pull request (PR). Describe the changes you've made and the reason for them.
+
+## Code Style
+
+To ensure a consistent code style throughout the project, please follow these guidelines:
+
+- Use **ESLint** and **Prettier** for linting and formatting (both are set up in this project).
+- Use **TypeScript** for type safety.
+- Follow the established pattern of functional components and hooks.
+- Keep code clean and well-commented.
+
+## Writing Tests
+
+The project uses [Jest](https://jestjs.io/) for testing. If you are adding new features or fixing bugs, please write tests to cover those changes.
+
+- **Unit tests**: For testing individual functions or components.
+- **Integration tests**: For testing how components work together.
+
+To run tests:
+
+```bash
+npm test
+```
+
+## Commit Messages
+
+We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) convention for commit messages. This makes it easier to generate changelogs and understand the history of the project.
+
+Example commit message format:
+
+- **feat**: Add new search result feature
+- **fix**: Fix error handling in search results
+- **docs**: Update documentation for new features
+- **chore**: Update dependencies
+
+## Reporting Issues
+
+If you find a bug or issue, please report it by opening a new issue in the [Issues section](https://github.com/ghosnkarl/react-api-search/issues) of the repository. When submitting an issue, please provide:
+
+- A clear description of the problem.
+- Steps to reproduce the issue.
+- Expected vs. actual behavior.
+- Any relevant error messages or logs.
+
+## License
+
+By contributing to this project, you agree that your contributions will be licensed under the project’s [MIT License](LICENSE).
+
+---
+
+Thank you for contributing! Together, we can make **React API Search** even better. 🚀
diff --git a/README.md b/README.md
index e69de29..1ab0f70 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,133 @@
+# React API Search
+
+A highly customizable, debounce-enabled, and fully-featured React API Search component designed to fetch and display search results asynchronously. Ideal for scenarios where the search query is used to fetch data from APIs or databases, with built-in support for loading, error handling, and no-result states.
+
+## Features
+
+- **Debounced Search**: Customizable debounce delay to prevent excessive API calls while typing.
+- **Loading & Error States**: Display loading spinners or error messages while fetching data.
+- **Dropdown Menu**: A dropdown menu that displays search results, with full control over its visibility.
+- **Customizable Styles**: Easily apply custom styles to the container, input field, results list, items, and icons.
+- **Flexible Data Fetching**: Works with any asynchronous data-fetching method.
+- **TypeScript Support**: Fully typed for better development experience and safety.
+
+## Installation
+
+```bash
+npm install react-api-search
+```
+
+or
+
+```bash
+yarn add react-api-search
+```
+
+## Usage
+
+```tsx
+import React, { useState } from 'react';
+import SearchBar from 'react-api-search';
+
+const MyComponent = () => {
+ const [selectedItem, setSelectedItem] = useState(null);
+
+ const fetchSearchResults = async (query: string) => {
+ const response = await fetch('https://api.example.com/search?q=${query}');
+ const data = await response.json();
+ return data.results; // return an array of results
+ };
+
+ const renderSearchResult = (item: any) =>
+ );
+};
+
+export default MyComponent;
+```
+
+## Props
+
+| Prop | Type | Description | Default Value |
+| ----------------------- | ------------------------------- | ---------------------------------------------------------- | --------------------------------- |
+| placeholder | string | Placeholder text for the input field. | 'Search...' |
+| fetchData | (query: string) => Promise | A function that fetches data based on the search query. | N/A |
+| renderItem | (item: T) => JSX.Element | A function to render each search result item. | N/A |
+| onSelect | (item: T) => void | A callback function triggered when a user selects an item. | undefined |
+| loadingElement | JSX.Element | JSX to display while results are loading. | `
Loading...
` |
+| emptyElement | JSX.Element | JSX to display when no results are found. | `
No results found
` |
+| errorElement | JSX.Element | JSX to display when an error occurs. | `
Something went wrong
` |
+| debounceDelay | number | The debounce delay in milliseconds. | 500 |
+| containerClassName | string | Custom class for the search bar container. | undefined |
+| inputClassName | string | Custom class for the input field. | undefined |
+| dropdownClassName | string | Custom class for the dropdown containing search results. | undefined |
+| itemClassName | string | Custom class for each search result item. | undefined |
+| hideSearchIcon | boolean | Whether to hide the search icon. | false |
+| searchIconClassName | string | Custom class for the search icon. | undefined |
+| closeIconClassName | string | Custom class for the close icon. | undefined |
+| inputFontColor | string | Font color of the input field. | #000 |
+| inputBorderRadius | string | Border radius of the input field. | '8px' |
+| inputBorderColor | string | Border color of the input field. | #ccc |
+| inputFontSize | string | Font size of the input field. | '16px' |
+| inputHeight | string | Height of the input field. | '45px' |
+| inputBackgroundColor | string | Background color of the input field. | #fff |
+| searchIconColor | string | Color of the search icon. | #888 |
+| closeIconColor | string | Color of the close icon. | #888 |
+| dropDownBackgroundColor | string | Background color of the dropdown. | #fff |
+| dropDownBorderColor | string | Border color of the dropdown. | #ccc |
+| dropDownMaxHeight | string | Maximum height of the dropdown. | '60vh' |
+| dropDownBorderRadius | string | Border radius of the dropdown. | '8px' |
+| scrollBarColor | string | Color of the scrollbar inside the dropdown. | #ccc |
+
+## Example
+
+### Basic Example
+
+```tsx
+ {
+ const response = await fetch('https://api.example.com/search?q=${query}');
+ return response.json();
+ }}
+ renderItem={(item) =>
}
+/>
+```
+
+## License
+
+This project is licensed under the MIT License. See the [LICENSE](https://github.com/ghosnkarl/react-api-search/blob/main/LICENSE) file for more information.
+
+## Contributing
+
+Contributions are welcome! 🎉
+
+1. Fork the project.
+2. Create a feature branch (git checkout -b feature-name).
+3. Commit your changes (git commit -m 'Add some feature').
+4. Push to the branch (git push origin feature-name).
+5. Open a Pull Request.
+
+For more information, please checkout the [CONTRIBUTIONS](https://github.com/ghosnkarl/react-api-search/blob/files/CONTRIBUTIONS.md) document.
+
+---
+
+**Note**: This component uses TypeScript and provides full type safety. It can be easily integrated into any TypeScript or JavaScript-based React project.
diff --git a/createRelease.sh b/createRelease.sh
new file mode 100755
index 0000000..853cd6f
--- /dev/null
+++ b/createRelease.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+# Parse command-line arguments
+while [[ "$#" -gt 0 ]]; do
+ case $1 in
+ -m)
+ if [[ -n "$2" ]]; then
+ COMMIT_MESSAGE="$2"
+ shift
+ else
+ echo "Missing value for -m argument"
+ exit 1
+ fi
+ ;;
+ *)
+ echo "Unknown parameter passed: $1"
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+# Check if the profile argument is provided
+if [[ -z "$COMMIT_MESSAGE" ]]; then
+ echo "Missing -m argument"
+ exit 1
+fi
+
+# Read the contents of version_number.js into a variable
+version_file=$(cat src/version_number.ts)
+
+echo $version_file
+
+# Use a regular expression to extract the version number from the file
+version_regex="const search_bar_version = ['\"]([0-9]+\.[0-9]+\.[0-9]+)['\"]"
+if [[ $version_file =~ $version_regex ]]; then
+ search_bar_version=${BASH_REMATCH[1]}
+else
+ echo "Error: Could not extract version number from version_number.ts"
+ exit 1
+fi
+
+# Use the version number in a command
+echo "The current version is $search_bar_version"
+
+# Create new tag
+TAG_NAME="v$search_bar_version"
+
+# Check if release with tag already exists
+output=$(git ls-remote --tags origin "refs/tags/$TAG_NAME")
+if [[ $output == *"refs/tags/$TAG_NAME"* ]]; then
+ echo "Release $TAG_NAME exists."
+ echo "Please make sure to increase the version number"
+ exit 1
+else
+ echo "Release $TAG_NAME does not exist."
+fi
+
+echo "Creating new release"
+
+npm version $search_bar_version --no-git-tag-version --allow-same-version
+
+# commit changes
+git add .
+git commit -m "R $TAG_NAME: $COMMIT_MESSAGE"
+git push
+
+# create tag and push to remote repository
+git tag -a "$TAG_NAME" -m "Release $TAG_NAME"
+git push origin "$TAG_NAME"
+
+gh release create "$TAG_NAME" --title "Release $TAG_NAME" --notes "$COMMIT_MESSAGE"
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 9e2dfb6..1f93eda 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,15 +1,14 @@
{
- "name": "type-search-api",
+ "name": "react-api-search",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "type-search-api",
+ "name": "react-api-search",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
- "classnames": "^2.5.1",
"react-icons": "^5.4.0"
},
"devDependencies": {
@@ -736,12 +735,6 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/classnames": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
- "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
- "license": "MIT"
- },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
diff --git a/package.json b/package.json
index 099837e..f30867f 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "search-bar-component",
+ "name": "react-api-search",
"version": "1.0.0",
"description": "SearchBarComponent is a customizable and responsive search bar component for React applications. It provides a flexible, debounced search experience, ideal for integrating search functionality into your app. The component supports dynamic fetching of search results from a remote source, rendering custom items, and handling loading states, empty results, and error handling.",
"main": "dist/index.js",
@@ -35,7 +35,6 @@
"typescript": "^5.5.3"
},
"dependencies": {
- "classnames": "^2.5.1",
"react-icons": "^5.4.0"
}
}
diff --git a/src/SearchBar.module.css b/src/SearchBar.module.css
index 7d850de..dd4db70 100644
--- a/src/SearchBar.module.css
+++ b/src/SearchBar.module.css
@@ -1,17 +1,17 @@
.container {
position: relative;
- width: 40rem;
+ width: 100%;
}
.inputWrapper {
display: flex;
align-items: center;
- height: 45px;
padding: 0 10px;
- border: 1px solid #ccc;
- border-radius: 8px;
overflow: hidden;
- background: #fff;
+}
+
+.inputWrapper:focus-within {
+ box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
}
.searchIcon,
@@ -22,22 +22,35 @@
.closeIcon {
cursor: pointer;
- transition: opacity 0.2s ease;
+ transition: all 0.2s ease-in-out;
}
.closeIcon:hover {
- opacity: 0.6;
+ transform: rotate(90deg);
}
.input {
flex: 1;
padding: 10px 15px;
- font-size: 16px;
border: none;
outline: none;
background: transparent;
}
+/* Scrollbar Customization */
+.resultsList {
+ scrollbar-width: thin;
+}
+
+.resultsList::-webkit-scrollbar {
+ width: 6px;
+}
+
+.resultsList::-webkit-scrollbar-thumb {
+ background-color: transparent;
+ border-radius: 4px;
+}
+
/* Animation for dropdown appearance */
@keyframes slideBounce {
0% {
@@ -58,16 +71,13 @@
top: 100%;
left: 0;
right: 0;
- max-height: 60vh;
display: none;
flex-direction: column;
overflow-y: auto;
+ overflow-x: hidden;
margin: 0;
padding: 0;
list-style: none;
- background: #fff;
- border: 1px solid #ccc;
- border-radius: 4px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
z-index: 1000;
opacity: 0;
@@ -87,10 +97,6 @@
transition: background-color 0.2s ease;
}
-.resultItem:hover {
- background-color: #f0f0f0;
-}
-
.centerElement {
display: flex;
flex-direction: column;
diff --git a/src/SearchBar.tsx b/src/SearchBar.tsx
index 3a93bf1..f9d6dc7 100644
--- a/src/SearchBar.tsx
+++ b/src/SearchBar.tsx
@@ -1,5 +1,4 @@
import { useState, useEffect, ChangeEvent, useRef } from 'react';
-import classNames from 'classnames';
import styles from './SearchBar.module.css';
import { SearchBarProps } from './types';
import { MdSearch } from 'react-icons/md';
@@ -15,18 +14,18 @@ import { MdClose } from 'react-icons/md';
* @param {string} [props.placeholder='Search...'] The placeholder text for the input field.
* @param {function} props.fetchData A function that fetches data based on the query string. Should return a promise with the results.
* @param {function} props.renderItem A function that renders an individual search result item.
- * @param {function} props.onSelect Callback function triggered when a user selects a search result.
- * @param {React.ReactNode} [props.loadingElement=
Loading...
] The JSX to display when loading.
+ * @param {function} props.onSelect Callback function triggered when a user selects an item from the search results.
+ * @param {React.ReactNode} [props.loadingElement=
Loading...
] The JSX to display when loading search results.
* @param {React.ReactNode} [props.emptyElement=
No results found
] The JSX to display when no results are found.
- * @param {React.ReactNode} [props.errorElement=
Something went wrong
] The JSX to display when an error occurs.
- * @param {number} [props.debounceDelay=500] The debounce delay (in milliseconds) to wait before calling the `fetchData` function after the user stops typing.
- * @param {string} [props.containerClassName] Custom class for the search bar container.
- * @param {string} [props.inputClassName] Custom class for the input field.
- * @param {string} [props.dropdownClassName] Custom class for the dropdown containing search results.
- * @param {string} [props.itemClassName] Custom class for each search result item.
- * @param {boolean} [props.hideSearchIcon=false] Whether to hide the search icon in the input field.
- * @param {string} [props.searchIconClassName] Custom class for the search icon.
- * @param {string} [props.closeIconClassName] Custom class for the close icon.
+ * @param {React.ReactNode} [props.errorElement=
Something went wrong
] The JSX to display when an error occurs during fetching.
+ * @param {number} [props.debounceDelay=500] The debounce delay (in milliseconds) to wait before calling `fetchData` after the user stops typing.
+ * @param {boolean} [props.hideSearchIcon=false] Whether to hide the search icon inside the input field.
+ * @param {string} [props.containerClassName] Custom CSS class name applied to the search bar container.
+ * @param {string} [props.inputClassName] Custom CSS class name applied to the search input field.
+ * @param {string} [props.dropdownClassName] Custom CSS class name applied to the dropdown containing search results.
+ * @param {string} [props.itemClassName] Custom CSS class name applied to each individual search result item.
+ * @param {string} [props.searchIconClassName] Custom CSS class name applied to the search icon.
+ * @param {string} [props.closeIconClassName] Custom CSS class name applied to the close icon used for clearing the search input.
*
* @returns The rendered SearchBar component.
*/
@@ -39,13 +38,22 @@ function SearchBar({
emptyElement =
@@ -147,7 +171,7 @@ function SearchBar({
results.map((item, index) => (
onSelect?.(item)}
>
{renderItem(item)}
diff --git a/src/types.ts b/src/types.ts
index ceae9ff..10c4852 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -2,23 +2,35 @@
* Type definition for the props of the SearchBar component.
*
* @template T The type of the items in the search results.
- * @property {string} [placeholder='Search...'] The placeholder text for the search input.
- * @property {(query: string) => Promise} fetchData A function that takes a search query and returns a promise that resolves to an array of search results.
- * @property {(item: T) => JSX.Element} renderItem A function that renders an individual item in the search results.
+ * @property {string} [placeholder='Search...'] The placeholder text for the search input field.
+ * @property {(query: string) => Promise} fetchData A function that fetches data based on the query string. Returns a promise with the search results.
+ * @property {(item: T) => JSX.Element} renderItem A function that renders an individual search result item.
* @property {(item: T) => void} [onSelect] An optional callback function triggered when a user selects an item from the search results.
* @property {JSX.Element} [loadingElement=
Loading...
] The JSX element displayed while the search results are loading.
* @property {JSX.Element} [emptyElement=
No results found
] The JSX element displayed when no results are found.
* @property {JSX.Element} [errorElement=
Something went wrong
] The JSX element displayed when an error occurs while fetching results.
- * @property {number} [debounceDelay=500] The debounce delay in milliseconds that determines how long to wait after the user stops typing before calling the fetchData function.
- * @property {string} [containerClassName] The custom CSS class name applied to the search bar container.
- * @property {string} [inputClassName] The custom CSS class name applied to the search input field.
- * @property {string} [dropdownClassName] The custom CSS class name applied to the dropdown that holds the search results.
- * @property {string} [itemClassName] The custom CSS class name applied to each individual search result item.
+ * @property {number} [debounceDelay=500] The debounce delay in milliseconds that determines how long to wait after the user stops typing before calling `fetchData`.
* @property {boolean} [hideSearchIcon=false] Whether to hide the search icon inside the input field.
- * @property {string} [searchIconClassName] The custom CSS class name applied to the search icon.
- * @property {string} [closeIconClassName] The custom CSS class name applied to the close icon used for clearing the search input.
+ * @property {string} [containerClassName] Custom CSS class name applied to the search bar container.
+ * @property {string} [inputClassName] Custom CSS class name applied to the search input field.
+ * @property {string} [dropdownClassName] Custom CSS class name applied to the dropdown that holds the search results.
+ * @property {string} [itemClassName] Custom CSS class name applied to each individual search result item.
+ * @property {string} [searchIconClassName] Custom CSS class name applied to the search icon.
+ * @property {string} [closeIconClassName] Custom CSS class name applied to the close icon used for clearing the search input.
+ * @property {string} [inputFontColor='#000'] Font color for the search input.
+ * @property {string} [inputBorderRadius='8px'] Border radius for the search input.
+ * @property {string} [inputBorderColor='#ccc'] Border color for the search input.
+ * @property {string} [inputFontSize='16px'] Font size for the search input.
+ * @property {string} [inputHeight='45px'] Height of the search input.
+ * @property {string} [inputBackgroundColor='#fff'] Background color for the search input.
+ * @property {string} [searchIconColor='#888'] Color for the search icon.
+ * @property {string} [closeIconColor='#888'] Color for the close icon.
+ * @property {string} [dropDownBackgroundColor='#fff'] Background color for the dropdown containing search results.
+ * @property {string} [dropDownBorderColor='#ccc'] Border color for the dropdown containing search results.
+ * @property {string} [dropDownMaxHeight='60vh'] Maximum height for the dropdown.
+ * @property {string} [dropDownBorderRadius='8px'] Border radius for the dropdown.
+ * @property {string} [scrollBarColor='#ccc'] Color for the scrollbar of the dropdown.
*/
-
export type SearchBarProps = {
placeholder?: string;
fetchData: (query: string) => Promise;
@@ -28,11 +40,20 @@ export type SearchBarProps = {
emptyElement?: JSX.Element;
errorElement?: JSX.Element;
debounceDelay?: number;
- containerClassName?: string;
- inputClassName?: string;
- dropdownClassName?: string;
- itemClassName?: string;
hideSearchIcon?: boolean;
- searchIconClassName?: string;
- closeIconClassName?: string;
+
+ // Styling Props
+ inputFontColor?: string;
+ inputBorderRadius?: string;
+ inputBorderColor?: string;
+ inputFontSize?: string;
+ inputHeight?: string;
+ inputBackgroundColor?: string;
+ searchIconColor?: string;
+ closeIconColor?: string;
+ dropDownBackgroundColor?: string;
+ dropDownBorderColor?: string;
+ dropDownMaxHeight?: string;
+ dropDownBorderRadius?: string;
+ scrollBarColor?: string;
};
diff --git a/src/version_number.ts b/src/version_number.ts
new file mode 100644
index 0000000..dcf7b73
--- /dev/null
+++ b/src/version_number.ts
@@ -0,0 +1,3 @@
+const search_bar_version = '1.0.0';
+
+export { search_bar_version };