diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index 29569fa3..36bc3979 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -96,7 +96,7 @@ The following NPM packages may be included in this product: - @yext/answers-core@1.3.2 - @yext/answers-headless-react@0.3.0-beta.0 - - @yext/answers-headless@0.0.5 + - @yext/answers-headless@0.1.0-beta.0 These packages each contain the following license and notice below: diff --git a/package-lock.json b/package-lock.json index 1bf12448..a73d9dfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@reduxjs/toolkit": "^1.6.2", "@types/react": "^17.0.15", - "@yext/answers-headless": "^0.0.5", + "@yext/answers-headless": "^0.1.0-beta.0", "typescript": "^4.3.5" }, "devDependencies": { @@ -3260,9 +3260,9 @@ } }, "node_modules/@yext/answers-headless": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@yext/answers-headless/-/answers-headless-0.0.5.tgz", - "integrity": "sha512-+eE9e2n8o41T06Rq2pzfWkgm2n8CBWusmPJ7wSyswmRbBLfczaed/e3lKn4xvvE7KGDdIIfygNcJk0yGdyVO5w==", + "version": "0.1.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/answers-headless/-/answers-headless-0.1.0-beta.0.tgz", + "integrity": "sha512-Oc6A6CteF0OAvM7By7XIWIm3vLhvMvXnzwyvBexHFfxKJ5+Arc3/Kk9DTQS1wwlo8kZfy4JzigqbYLqwNv6YgQ==", "dependencies": { "@reduxjs/toolkit": "^1.6.0", "@yext/answers-core": "^1.3.2", @@ -13649,9 +13649,9 @@ } }, "@yext/answers-headless": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@yext/answers-headless/-/answers-headless-0.0.5.tgz", - "integrity": "sha512-+eE9e2n8o41T06Rq2pzfWkgm2n8CBWusmPJ7wSyswmRbBLfczaed/e3lKn4xvvE7KGDdIIfygNcJk0yGdyVO5w==", + "version": "0.1.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/answers-headless/-/answers-headless-0.1.0-beta.0.tgz", + "integrity": "sha512-Oc6A6CteF0OAvM7By7XIWIm3vLhvMvXnzwyvBexHFfxKJ5+Arc3/Kk9DTQS1wwlo8kZfy4JzigqbYLqwNv6YgQ==", "requires": { "@reduxjs/toolkit": "^1.6.0", "@yext/answers-core": "^1.3.2", diff --git a/package.json b/package.json index ff77a377..c3d5000f 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "dependencies": { "@reduxjs/toolkit": "^1.6.2", "@types/react": "^17.0.15", - "@yext/answers-headless": "^0.0.5", + "@yext/answers-headless": "^0.1.0-beta.0", "typescript": "^4.3.5" }, "devDependencies": { diff --git a/sample-app/package-lock.json b/sample-app/package-lock.json index 1383fb3f..d9275498 100644 --- a/sample-app/package-lock.json +++ b/sample-app/package-lock.json @@ -91,9 +91,9 @@ "name": "@yext/answers-headless-react", "version": "0.3.0-beta.0", "dependencies": { - "@reduxjs/toolkit": "^1.6.1", + "@reduxjs/toolkit": "^1.6.2", "@types/react": "^17.0.15", - "@yext/answers-headless": "^0.0.4", + "@yext/answers-headless": "^0.1.0-beta.0", "typescript": "^4.3.5" }, "devDependencies": { @@ -25120,7 +25120,7 @@ "@babel/preset-env": "^7.15.6", "@babel/preset-react": "^7.14.5", "@babel/preset-typescript": "^7.15.0", - "@reduxjs/toolkit": "^1.6.1", + "@reduxjs/toolkit": "^1.6.2", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^12.1.0", "@testing-library/user-event": "^13.2.1", @@ -25128,7 +25128,7 @@ "@types/react": "^17.0.15", "@typescript-eslint/eslint-plugin": "^4.28.5", "@typescript-eslint/parser": "^4.28.5", - "@yext/answers-headless": "^0.0.4", + "@yext/answers-headless": "^0.1.0-beta.0", "eslint": "^7.32.0", "eslint-config-react-app": "^6.0.0", "eslint-plugin-flowtype": "^5.8.2", diff --git a/sample-app/src/App.test.tsx b/sample-app/src/App.test.tsx index 8ec10347..c704795c 100644 --- a/sample-app/src/App.test.tsx +++ b/sample-app/src/App.test.tsx @@ -4,5 +4,5 @@ import App from './App'; test('renders learn react link', () => { const { getByText } = render(); - expect(getByText(/Stateful Core React Test/i)).toBeInTheDocument(); + expect(getByText(/Answers Headless React Test/i)).toBeInTheDocument(); }); diff --git a/sample-app/src/components/Facet.tsx b/sample-app/src/components/Facet.tsx index e891fcb4..1bf5c2e1 100644 --- a/sample-app/src/components/Facet.tsx +++ b/sample-app/src/components/Facet.tsx @@ -29,7 +29,7 @@ export default function Facet(props: FacetProps): JSX.Element { }); const facetOptions = searchable - ? answersActions.searchThroughFacet(facet, filterValue).options + ? answersActions.utilities.searchThroughFacet(facet, filterValue).options : facet.options; return ( diff --git a/src/AnswersActionsContext.ts b/src/AnswersActionsContext.ts index 7645e352..cf4d1028 100644 --- a/src/AnswersActionsContext.ts +++ b/src/AnswersActionsContext.ts @@ -1,5 +1,5 @@ -import { StatefulCore } from '@yext/answers-headless'; +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 AnswersActionsContext = createContext({} as StatefulCore); +export const AnswersActionsContext = createContext({} as AnswersHeadless); diff --git a/src/AnswersActionsProvider.tsx b/src/AnswersActionsProvider.tsx index 75396a14..292ed965 100644 --- a/src/AnswersActionsProvider.tsx +++ b/src/AnswersActionsProvider.tsx @@ -1,5 +1,5 @@ import { ReactChild, ReactChildren } from 'react'; -import { provideStatefulCore, StatefulCore } from '@yext/answers-headless'; +import { provideAnswersHeadless, AnswersHeadless } from '@yext/answers-headless'; import { AnswersConfig } from '@yext/answers-core'; import { AnswersActionsContext } from './AnswersActionsContext'; @@ -10,10 +10,10 @@ interface Props extends AnswersConfig { export function AnswersActionsProvider(props: Props): JSX.Element { const { children, verticalKey, ...answersConfig } = props; - const statefulCore: StatefulCore = provideStatefulCore(answersConfig); - verticalKey && statefulCore.setVerticalKey(verticalKey); + const answers: AnswersHeadless = provideAnswersHeadless(answersConfig); + verticalKey && answers.setVerticalKey(verticalKey); return ( - + {children} ); diff --git a/src/subscribeToStateUpdates.tsx b/src/subscribeToStateUpdates.tsx index c8771173..16386c9b 100644 --- a/src/subscribeToStateUpdates.tsx +++ b/src/subscribeToStateUpdates.tsx @@ -20,22 +20,22 @@ export function subscribeToStateUpdates( const generateSubscriberHOC: SubscriberGenerator = WrappedComponent => { /** * Keep manual track of the props mapped from state instead of storing - * it in the StatefulCoreSubscriber's state. This avoids react's batching + * it in the AnswersHeadlessSubscriber's state. This avoids react's batching * of state updates, which can result in mappedState not updating immediately. - * This can, in turn, result in extra stateful-core listener invocations. + * This can, in turn, result in extra answers-headless listener invocations. */ let previousPropsFromState = {}; - return function StatefulCoreSubscriber(props: Record) { - const statefulCore = useContext(AnswersActionsContext); + return function AnswersHeadlessSubscriber(props: Record) { + const answers = useContext(AnswersActionsContext); const [mergedProps, dispatch] = useReducer(() => { return { ...props, ...previousPropsFromState }; - }, { ...props, ...mapStateToProps(statefulCore.state) }); + }, { ...props, ...mapStateToProps(answers.state) }); useEffect(() => { - return statefulCore.addListener({ + return answers.addListener({ valueAccessor: (state: State) => mapStateToProps(state), callback: newPropsFromState => { if (!isShallowEqual(previousPropsFromState, newPropsFromState)) { diff --git a/src/useAnswersActions.ts b/src/useAnswersActions.ts index 397caaea..224882ea 100644 --- a/src/useAnswersActions.ts +++ b/src/useAnswersActions.ts @@ -1,8 +1,8 @@ -import { StatefulCore } from '@yext/answers-headless'; +import { AnswersHeadless } from '@yext/answers-headless'; import { useContext } from 'react'; import { AnswersActionsContext } from './AnswersActionsContext'; -export type AnswersActions = StatefulCore; +export type AnswersActions = AnswersHeadless; export function useAnswersActions(): AnswersActions { return useContext(AnswersActionsContext); diff --git a/src/useAnswersState.tsx b/src/useAnswersState.tsx index 046d8cdd..f6207acc 100644 --- a/src/useAnswersState.tsx +++ b/src/useAnswersState.tsx @@ -9,10 +9,10 @@ export type StateSelector = (s: State) => T; * Very similar to useSelector in react-redux. */ export function useAnswersState(stateSelector: StateSelector): T { - const statefulCore = useContext(AnswersActionsContext); + const answers = useContext(AnswersActionsContext); // useRef stores values across renders without triggering additional ones - const storedStoreState = useRef(statefulCore.state); + const storedStoreState = useRef(answers.state); const storedSelector = useRef>(stateSelector); const storedSelectedState = useRef(); /** @@ -20,7 +20,7 @@ export function useAnswersState(stateSelector: StateSelector): T { * Otherwise it's run an additional time every render, even when storedSelectedState is already initialized. */ if (storedSelectedState.current === undefined) { - storedSelectedState.current = stateSelector(statefulCore.state); + storedSelectedState.current = stateSelector(answers.state); } /** @@ -28,8 +28,8 @@ export function useAnswersState(stateSelector: StateSelector): T { * Tries to use {@link storedSelectedState} when possible. */ const selectedStateToReturn: T = (() => { - if (storedStoreState.current !== statefulCore.state || storedSelector.current !== stateSelector) { - return stateSelector(statefulCore.state); + if (storedStoreState.current !== answers.state || storedSelector.current !== stateSelector) { + return stateSelector(answers.state); } return storedSelectedState.current; })(); @@ -37,13 +37,13 @@ export function useAnswersState(stateSelector: StateSelector): T { const [, triggerRender] = useState(storedSelectedState.current); useLayoutEffect(() => { storedSelector.current = stateSelector; - storedStoreState.current = statefulCore.state; + storedStoreState.current = answers.state; storedSelectedState.current = selectedStateToReturn; }); useLayoutEffect(() => { let unsubscribed = false; - const unsubscribe = statefulCore.addListener({ + const unsubscribe = answers.addListener({ valueAccessor: state => state, callback: (state: State) => { // prevent React state update on an unmounted component @@ -61,7 +61,7 @@ export function useAnswersState(stateSelector: StateSelector): T { unsubscribed = true; unsubscribe(); }; - }, [statefulCore]); + }, [answers]); return selectedStateToReturn; } diff --git a/tests/useAnswersState.test.tsx b/tests/useAnswersState.test.tsx index 7adf4e2b..36b87c3f 100644 --- a/tests/useAnswersState.test.tsx +++ b/tests/useAnswersState.test.tsx @@ -1,7 +1,7 @@ import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Result } from '@yext/answers-core'; -import { provideStatefulCore } from '@yext/answers-headless'; +import { provideAnswersHeadless } from '@yext/answers-headless'; import { State } from '@yext/answers-headless/lib/esm/models/state'; import React, { useCallback, useReducer } from 'react'; import { AnswersActionsContext, useAnswersActions, useAnswersState } from '../src'; @@ -43,13 +43,13 @@ it('does not perform extra renders/listener registrations for nested components' ); } - const statefulCore = createStatefulCore(); - const addListenerSpy = jest.spyOn(statefulCore, 'addListener'); + const answers = createAnswersHeadless(); + const addListenerSpy = jest.spyOn(answers, 'addListener'); expect(addListenerSpy).toHaveBeenCalledTimes(0); expect(parentStateUpdates).toHaveLength(0); expect(childStateUpdates).toHaveLength(0); render( - + ); @@ -88,16 +88,16 @@ it('does not trigger render on unmounted component', async () => { return
child component
; } - const statefulCore = createStatefulCore(); + const answers = createAnswersHeadless(); render( - + ); - act(() => statefulCore.setQuery('resultsWithFilter')); - await act( () => statefulCore.executeUniversalQuery()); - act(() => statefulCore.setQuery('default')); - await act( () => statefulCore.executeUniversalQuery()); + act(() => answers.setQuery('resultsWithFilter')); + await act( () => answers.executeUniversalQuery()); + act(() => answers.setQuery('default')); + await act( () => answers.executeUniversalQuery()); expect(consoleSpy).not.toHaveBeenCalledWith( expect.stringMatching('Can\'t perform a React state update on an unmounted component'), expect.anything(), @@ -120,9 +120,9 @@ describe('uses the most recent selector',() => { ); } - const statefulCore = createStatefulCore(); + const answers = createAnswersHeadless(); render( - + ); @@ -153,11 +153,11 @@ describe('uses the most recent selector',() => { ); } - const statefulCore = createStatefulCore(); - statefulCore.setQuery('initial value'); + const answers = createAnswersHeadless(); + answers.setQuery('initial value'); expect(stateUpdates).toHaveLength(0); render( - + ); @@ -173,18 +173,18 @@ describe('uses the most recent selector',() => { expect(stateUpdates).toEqual(['initial value', 1]); act(() => { - statefulCore.setContext('trigger a state update that would not update the initial selector'); + answers.setContext('trigger a state update that would not update the initial selector'); }); expect(stateUpdates).toEqual(['initial value', 1, 3]); }); }); -function createStatefulCore() { - const statefulCore = provideStatefulCore({ +function createAnswersHeadless() { + const answers = provideAnswersHeadless({ apiKey: 'fake api key', experienceKey: 'fake exp key', locale: 'en', }); - statefulCore.setVerticalKey('fakeVerticalKey'); - return statefulCore; + answers.setVerticalKey('fakeVerticalKey'); + return answers; } \ No newline at end of file