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

Answers -> Search 1: Customer facing renaming #141

Merged
merged 16 commits into from
Jul 14, 2022
141 changes: 73 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,152 +1,157 @@
# Answers Headless React
# Search Headless React

Answers Headless React is the official React UI Bindings layer for [Answers Headless](https://www.npmjs.com/package/@yext/answers-headless).
Search Headless React is the official React UI Bindings layer for [Search Headless](https://www.npmjs.com/package/@yext/search-headless).

Written in 100% TypeScript.

<div>
<a href="https://npmjs.org/package/@yext/answers-headless-react">
<img src="https://img.shields.io/npm/v/@yext/answers-headless-react" alt="NPM version"/>
<a href="https://npmjs.org/package/@yext/search-headless-react">
<img src="https://img.shields.io/npm/v/@yext/search-headless-react" alt="NPM version"/>
</a>
<a href="./LICENSE">
<img src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" alt="License"/>
</a>
<a href='https://coveralls.io/github/yext/answers-headless-react?branch=main'>
<img src='https://coveralls.io/repos/github/yext/answers-headless-react/badge.svg?branch=main' alt='Coverage Status' />
<a href='https://coveralls.io/github/yext/search-headless-react?branch=main'>
<img src='https://coveralls.io/repos/github/yext/search-headless-react/badge.svg?branch=main' alt='Coverage Status' />
</a>
</div>
<br>

## Installation

```shell
npm install @yext/answers-headless-react
npm install @yext/search-headless-react
```

## Getting Started - `AnswersHeadlessProvider`
## Getting Started - `SearchHeadlessProvider`

Answers Headless React includes an `<AnswersHeadlessProvider />` component, which instantiates an AnswersHeadless instance and makes it available to the rest of your app.
Search Headless React includes an `<SearchHeadlessProvider />` component, which instantiates an SearchHeadless instance and makes it available to the rest of your app.

```tsx
import { AnswersHeadlessProvider } from '@yext/answers-headless-react';
import { SearchHeadlessProvider } from '@yext/search-headless-react';
import SearchBar from './SearchBar';
import MostRecentSearch from './MostRecentSearch';
import UniversalResults from './UniversalResults';

function MyApp() {
return (
<AnswersHeadlessProvider
<SearchHeadlessProvider
apiKey='your api key'
experienceKey='your experience key'
locale='en'
>
{/* Add components that use Answers as children */}
{/* Add components that use Search as children */}
<SearchBar/>
<MostRecentSearch/>
<UniversalResults/>
</AnswersHeadlessProvider>
</SearchHeadlessProvider>
);
}
```

## Respond to State Updates with `useAnswersState`
## Respond to State Updates with `useSearchState`

`useAnswersState` reads a value from the `AnswersHeadless` state and subscribes to updates.
`useSearchState` reads a value from the `SearchHeadless` state and subscribes to updates.

```tsx
import { useAnswersState } from '@yext/answers-headless-react';
import { useSearchState } from '@yext/search-headless-react';

export default function MostRecentSearch() {
const mostRecentSearch = useAnswersState(state => state.query.mostRecentSearch);
default function MostRecentSearch() {
const mostRecentSearch = useSearchState(state => state.query.mostRecentSearch);
return <div>Showing results for {mostRecentSearch}</div>;
}
```

## Dispatch Actions with `useAnswersActions`
## Dispatch Actions with `useSearchActions`

`useAnswersActions` allows you to dispatch actions using the `AnswersHeadless` instance.
`useSearchActions` allows you to dispatch actions using the `SearchHeadless` instance.

These include performing searches, getting autocomplete suggestions, and adding filters.

For a full list of capabilities see [the answers-headless docs](https://www.npmjs.com/package/@yext/answers-headless).
For a full list of capabilities see [the search-headless docs](https://www.npmjs.com/package/@yext/search-headless).

```tsx
import { useAnswersActions } from '@yext/answers-headless-react';
import { useSearchActions } from '@yext/search-headless-react';
import { ChangeEvent, KeyboardEvent, useCallback } from 'react';

function SearchBar() {
const answers = useAnswersActions();
const search = useSearchActions();
const handleTyping = useCallback((e: ChangeEvent<HTMLInputElement>) => {
answers.setQuery(e.target.value);
}, [answers]);
search.setQuery(e.target.value);
}, [search]);

const handleKeyDown = useCallback((evt: KeyboardEvent<HTMLInputElement>) => {
if (evt.key === 'Enter' ) {
answers.executeUniversalQuery();
search.executeUniversalQuery();
}
}, [answers]);
}, [search]);

return <input onChange={handleTyping} onKeyDown={handleKeyDown}/>;
}
```

## `AnswersHeadlessContext`
### Class Components
## Class Components

For users that want to use class components instead of functional components, you can use the `AnswersHeadlessContext` directly to dispatch actions and receive updates from state.
For users that want to use class components instead of functional components, you can use the `SearchHeadlessContext` directly to dispatch actions, and the `subscribeToStateUpdates` HOC to receive updates from state.

As an example, here is our simple SearchBar again, rewritten as a class using `AnswersHeadlessContext`.
These also work with functional components.

## `subscribeToStateUpdates`

Here is our MostRecentSearch component again, rewritten as a class with `subscribeToStateUpdates`.
oshi97 marked this conversation as resolved.
Show resolved Hide resolved

```tsx
import { AnswersHeadlessContext, AnswersHeadless, State } from '@yext/answers-headless-react';
import { subscribeToStateUpdates } from '@yext/search-headless-react';
import { Component } from 'react';

export default class Searcher extends Component {
static contextType = AnswersHeadlessContext;
unsubscribeQueryListener: any;
state = { query: "" };

componentDidMount() {
const answers: AnswersHeadless = this.context;
this.unsubscribeQueryListener = answers.addListener({
valueAccessor: (state: State) => state.query.mostRecentSearch,
callback: newPropsFromState => {
this.setState({ query: newPropsFromState })
}
});
}
interface Props {
mostRecentSearch: string
}

componentWillUnmount() {
this.unsubscribeQueryListener();
class MostRecentSearch extends Component<Props> {
render() {
return <div>Showing results for {this.props.mostRecentSearch}</div>;
}
}

export default subscribeToStateUpdates(state => ({
Copy link
Contributor

@yen-tt yen-tt Jul 13, 2022

Choose a reason for hiding this comment

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

we want the updated README portion here from develop, I think you might have reverted back when resolving merge conflict (subscribeToStateUpdates is now deprecated)

mostRecentSearch: state.query.mostRecentSearch
}))(MostRecentSearch);
```

## `SearchHeadlessContext`

And here is our simple SearchBar again, rewritten as a class using `SearchHeadlessContext`.

```tsx
import { SearchHeadlessContext, SearchHeadless } from '@yext/search-headless-react';
import { Component } from 'react';

export default class Searcher extends Component {
static contextType = SearchHeadlessContext;

render() {
const answers: AnswersHeadless = this.context;
return (
<div>
<p>Query: {this.state.query}</p>
<input
onChange={evt => answers.setQuery(evt.target.value)}
onKeyDown={evt => {
if (evt.key === 'Enter') {
answers.executeUniversalQuery();
}
}}
/>
</div>
)
const search: SearchHeadless = this.context;
return <input
onChange={evt => search.setQuery(evt.target.value)}
onKeyDown={evt => {
if (evt.key === 'Enter') {
search.executeUniversalQuery();
}
}}
/>
}
}
```

## `useAnswersUtilities`
## `useSearchUtilities`

We offer a `useAnswersUtilities` convenience hook for accessing `AnswersHeadless.utilities`, which offers a number of stateless utility methods.
The `answersUtilities` and `answersUtilitiesFromActions` variables below are equivalent.
We offer a `useSearchUtilities` convenience hook for accessing `SearchHeadless.utilities`, which offers a number of stateless utility methods.
The `searchUtilities` and `searchUtilitiesFromActions` variables below are equivalent.

For class components, you can access `AnswersUtilities` through `AnswersHeadlessContext`.
For class components, you can access `SearchUtilities` through `SearchHeadlessContext`.

```ts
const answersUtilities = useAnswersUtilities();
const answersUtilitiesFromActions = useAnswersActions().utilities;
const searchUtilities = useSearchUtilities();
const searchUtilitiesFromActions = useSearchActions().utilities;
```
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@yext/answers-headless-react",
"name": "@yext/search-headless-react",
"version": "1.2.0",
"description": "The official React UI Bindings layer for Answers Headless",
"description": "The official React UI Bindings layer for Search Headless",
"main": "./lib/esm/src/index.js",
"license": "BSD-3-Clause",
"types": "./lib/esm/src/index.d.ts",
Expand Down Expand Up @@ -106,6 +106,6 @@
},
"repository": {
"type": "git",
"url": "https://github.com/yext/answers-headless-react.git"
"url": "https://github.com/yext/search-headless-react.git"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import { AnswersHeadless } from '@yext/answers-headless';
import { createContext } from 'react';

// The default is empty because we don't know the user's config yet
export const AnswersHeadlessContext = createContext<AnswersHeadless>({} as AnswersHeadless);
export const SearchHeadlessContext = createContext<AnswersHeadless>({} as AnswersHeadless);
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PropsWithChildren } from 'react';
import { provideAnswersHeadless, AnswersHeadless, HeadlessConfig } from '@yext/answers-headless';
import { AnswersHeadlessContext } from './AnswersHeadlessContext';
import { SearchHeadlessContext } from './SearchHeadlessContext';
import acquireSessionId from './utils/acquireSessionId';
import packageJson from '../package.json';

Expand All @@ -11,7 +11,7 @@ type Props = HeadlessConfig & {
sessionTrackingEnabled?: boolean
};

export function AnswersHeadlessProvider(props: PropsWithChildren<Props>): JSX.Element {
export function SearchHeadlessProvider(props: PropsWithChildren<Props>): JSX.Element {
const { children, verticalKey, sessionTrackingEnabled=true, ...answersConfig } = props;
const additionalHttpHeaders = {
'Client-SDK': {
Expand All @@ -27,8 +27,8 @@ export function AnswersHeadlessProvider(props: PropsWithChildren<Props>): JSX.El
sessionId && answers.setSessionId(sessionId);
}
return (
<AnswersHeadlessContext.Provider value={answers}>
<SearchHeadlessContext.Provider value={answers}>
{children}
</AnswersHeadlessContext.Provider>
</SearchHeadlessContext.Provider>
);
}
}
40 changes: 40 additions & 0 deletions src/deprecated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useSearchActions, SearchActions } from './useSearchActions';
import { useSearchState } from './useSearchState';
import { useSearchUtilities, SearchUtilities } from './useSearchUtilities';
import { SearchHeadlessProvider } from './SearchHeadlessProvider';
import { SearchHeadlessContext } from './SearchHeadlessContext';

/**
* @deprecated AnswersHeadlessContext has been deprecated and replaced by SearchHeadlessContext
*/
export const AnswersHeadlessContext = SearchHeadlessContext;

/**
* @deprecated AnswersActions has been deprecated and replaced by SearchActions
*/
export type AnswersActions = SearchActions;

/**
* @deprecated AnswersUtilities has been deprecated and replaced by SearchUtilities
*/
export type AnswersUtilities = SearchUtilities;

/**
* @deprecated useAnswersActions has been deprecated and replaced by useSearchActions
*/
export const useAnswersActions = useSearchActions;

/**
* @deprecated useAnswersState has been deprecated and replaced by useSearchState
*/
export const useAnswersState = useSearchState;

/**
* @deprecated useAnswersUtilities has been deprecated and repalced by useSearchUtilities
*/
export const useAnswersUtilities = useSearchUtilities;

/**
* @deprecated AnswersHeadlessProvider has been deprecated and replaced by SearfchHeadlessProvider
*/
export const AnswersHeadlessProvider = SearchHeadlessProvider;
27 changes: 14 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { useAnswersActions, AnswersActions } from './useAnswersActions';
import { useAnswersState, StateSelector } from './useAnswersState';
import { useAnswersUtilities, AnswersUtilities } from './useAnswersUtilities';
import { useSearchActions, SearchActions } from './useSearchActions';
import { useSearchState, StateSelector } from './useSearchState';
import { useSearchUtilities, SearchUtilities } from './useSearchUtilities';
import { subscribeToStateUpdates } from './subscribeToStateUpdates';
import { AnswersHeadlessProvider } from './AnswersHeadlessProvider';
import { AnswersHeadlessContext } from './AnswersHeadlessContext';
import { SearchHeadlessProvider } from './SearchHeadlessProvider';
import { SearchHeadlessContext } from './SearchHeadlessContext';

export * from '@yext/answers-headless';
export * from './deprecated';
export {
AnswersHeadlessContext,
SearchHeadlessContext,
subscribeToStateUpdates,
useAnswersActions,
useAnswersState,
useAnswersUtilities,
AnswersHeadlessProvider,
AnswersActions,
AnswersUtilities,
useSearchActions,
useSearchState,
useSearchUtilities,
SearchHeadlessProvider,
SearchActions,
SearchUtilities,
StateSelector
};
};
Loading