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
74 changes: 37 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
# 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/answers-headless).
oshi97 marked this conversation as resolved.
Show resolved Hide resolved

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 AnswersHeadless 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'
Expand All @@ -44,55 +44,55 @@ function MyApp() {
<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 `AnswersHeadless` 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 `AnswersHeadless` 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).

```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}/>;
}
```

## Class Components

For users that want to use class components instead of functional components, you can use the `AnswersHeadlessContext` directly to dispatch actions, and the `subscribeToStateUpdates` HOC to 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.

These also work with functional components.

Expand All @@ -101,7 +101,7 @@ These also work with functional components.
Here is our MostRecentSearch component again, rewritten as a class with `subscribeToStateUpdates`.
oshi97 marked this conversation as resolved.
Show resolved Hide resolved

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

interface Props {
Expand All @@ -119,16 +119,16 @@ export default subscribeToStateUpdates(state => ({
}))(MostRecentSearch);
```

## `AnswersHeadlessContext`
## `SearchHeadlessContext`

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

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

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

render() {
const answers: AnswersHeadless = this.context;
Expand All @@ -144,14 +144,14 @@ export default class Searcher extends Component {
}
```

## `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 `AnswersHeadless.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.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@yext/answers-headless-react",
"name": "@yext/search-headless-react",
"version": "1.2.0",
"main": "./lib/esm/src/index.js",
"types": "./lib/esm/src/index.d.ts",
Expand Down Expand Up @@ -95,6 +95,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;
yen-tt marked this conversation as resolved.
Show resolved Hide resolved

/**
* @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
};
};
4 changes: 2 additions & 2 deletions src/subscribeToStateUpdates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentType, useReducer, useEffect, useContext } from 'react';
import { State } from '@yext/answers-headless';
import { AnswersHeadlessContext } from './AnswersHeadlessContext';
import { SearchHeadlessContext } from './SearchHeadlessContext';
import isShallowEqual from './utils/isShallowEqual';

type SubscriberGenerator = (WrappedComponent: ComponentType<any>) => (props: any) => JSX.Element;
Expand All @@ -26,7 +26,7 @@ export function subscribeToStateUpdates(
*/
let previousPropsFromState = {};
return function AnswersHeadlessSubscriber(props: Record<string, unknown>) {
const answers = useContext(AnswersHeadlessContext);
const answers = useContext(SearchHeadlessContext);
const [mergedProps, dispatch] = useReducer(() => {
return {
...props,
Expand Down
14 changes: 0 additions & 14 deletions src/useAnswersActions.ts

This file was deleted.

9 changes: 0 additions & 9 deletions src/useAnswersUtilities.ts

This file was deleted.

14 changes: 14 additions & 0 deletions src/useSearchActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AnswersHeadless } from '@yext/answers-headless';
import { useContext } from 'react';
import { SearchHeadlessContext } from './SearchHeadlessContext';

export type SearchActions = AnswersHeadless;

export function useSearchActions(): SearchActions {
const answersHeadless = useContext(SearchHeadlessContext);
if (answersHeadless.state === undefined) {
throw new Error('Attempted to call useAnswersActions() outside of SearchHeadlessProvider.'
+ ' Please ensure that \'useAnswersActions()\' is called within an SearchHeadlessProvider component.');
}
return answersHeadless;
}
Loading