Skip to content

Commit

Permalink
fix(routerContext): sw-625 useNavigate state update (#1075)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdcabrera authored Mar 6, 2023
1 parent 7dff7dc commit 396dd2b
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 21 deletions.
13 changes: 9 additions & 4 deletions src/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5183,7 +5183,8 @@ Focused on exposing replace and href.
<a name="Router.module_RouterContext..useNavigate"></a>

### RouterContext~useNavigate(options) ⇒ <code>function</code>
useNavigate wrapper, apply application config context routing
useNavigate wrapper. Leverage useNavigate for a modified router with parallel "state"
update. Dispatches the same type leveraged by the initialize hook, useSetRouteDetail.

**Kind**: inner method of [<code>RouterContext</code>](#Router.module_RouterContext)
<table>
Expand All @@ -5196,6 +5197,8 @@ useNavigate wrapper, apply application config context routing
<tr>
<td>options</td><td><code>object</code></td>
</tr><tr>
<td>options.useDispatch</td><td><code>function</code></td>
</tr><tr>
<td>options.useLocation</td><td><code>function</code></td>
</tr><tr>
<td>options.useNavigate</td><td><code>function</code></td>
Expand All @@ -5205,7 +5208,8 @@ useNavigate wrapper, apply application config context routing
<a name="Router.module_RouterContext..useRouteDetail"></a>

### RouterContext~useRouteDetail(options) ⇒ <code>Object</code>
Get a route detail configuration from state.
Get a route detail from "state". Consume useSetRouteDetail and set basis for product
configuration context.

**Kind**: inner method of [<code>RouterContext</code>](#Router.module_RouterContext)
<table>
Expand Down Expand Up @@ -5278,8 +5282,9 @@ This hook defaults to merging search objects instead of overwriting them.
<a name="Router.module_RouterContext..useSetRouteDetail"></a>

### RouterContext~useSetRouteDetail(options) ⇒ <code>\*</code> \| <code>string</code>
Store product path, parameter, in state. We're opting to use "window.location.pathname"
directly since it appears to be quicker, and returns a similar structured value as useParam.
Initialize and store product path, parameter, in a "state" update parallel to routing.
We're opting to use "window.location.pathname" directly since it appears to be quicker,
and returns a similar structured value as useParam.

**Kind**: inner method of [<code>RouterContext</code>](#Router.module_RouterContext)
<table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,29 @@ exports[`RouterContext should apply a hook for useLocation: location 1`] = `
}
`;

exports[`RouterContext should apply a hook for useNavigate: navigation dispatch 1`] = `
[
[
{
"config": "rhods",
"type": "SET_PRODUCT",
},
],
[
{
"config": "rhel",
"type": "SET_PRODUCT",
},
],
[
{
"config": "rhel",
"type": "SET_PRODUCT",
},
],
]
`;

exports[`RouterContext should apply a hook for useNavigate: navigation push 1`] = `
[
"./rhods?lorem=ipsum",
Expand Down
14 changes: 9 additions & 5 deletions src/components/router/__tests__/routerContext.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,26 @@ describe('RouterContext', () => {
});

it('should apply a hook for useNavigate', () => {
const mockLocation = {
search: '?lorem=ipsum'
};

const mockDispatch = jest.fn();
const updatedCalls = [];
const { result: mockNavigationSet } = shallowHook(() =>
useNavigate({
useLocation: () => mockLocation,
useDispatch: () => mockDispatch,
useLocation: () => ({
search: '?lorem=ipsum'
}),
useNavigate: () => value => updatedCalls.push(value)
})
);

/**
* Note: Snapshots for first "mockNavigationSet" are aimed at being what Levenshtein denotes as a "closest match"
*/
mockNavigationSet('/dolor/sit');
mockNavigationSet('rhel');
mockNavigationSet('insights');

expect(mockDispatch.mock.calls).toMatchSnapshot('navigation dispatch');
expect(updatedCalls).toMatchSnapshot('navigation push');
});

Expand Down
34 changes: 22 additions & 12 deletions src/components/router/routerContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,38 +49,47 @@ const useLocation = ({
};

/**
* useNavigate wrapper, apply application config context routing
* useNavigate wrapper. Leverage useNavigate for a modified router with parallel "state"
* update. Dispatches the same type leveraged by the initialize hook, useSetRouteDetail.
*
* @param {object} options
* @param {Function} options.useDispatch
* @param {Function} options.useLocation
* @param {Function} options.useNavigate
* @returns {Function}
*/
const useNavigate = ({
useDispatch: useAliasDispatch = storeHooks.reactRedux.useDispatch,
useLocation: useAliasLocation = useLocation,
useNavigate: useAliasNavigate = useRRDNavigate
} = {}) => {
const { search = '', hash = '' } = useAliasLocation();
const navigate = useAliasNavigate();
const dispatch = useAliasDispatch();

return useCallback(
(pathLocation, options) => {
const pathName = (typeof pathLocation === 'string' && pathLocation) || pathLocation?.pathname;
const { firstMatch } = routerHelpers.getRouteConfigByPath({ pathName });

return navigate(
(firstMatch?.productPath && `${routerHelpers.pathJoin('.', firstMatch?.productPath)}${search}${hash}`) ||
(pathName && `${pathName}${search}${hash}`) ||
pathLocation,
options
);
if (firstMatch?.productPath) {
dispatch({
type: reduxTypes.app.SET_PRODUCT,
config: firstMatch?.productPath
});

return navigate(`${routerHelpers.pathJoin('.', firstMatch?.productPath)}${search}${hash}`, options);
}

return navigate((pathName && `${pathName}${search}${hash}`) || pathLocation, options);
},
[hash, navigate, search]
[dispatch, hash, navigate, search]
);
};

/**
* Get a route detail configuration from state.
* Get a route detail from "state". Consume useSetRouteDetail and set basis for product
* configuration context.
*
* @param {object} options
* @param {Function} options.t
Expand Down Expand Up @@ -171,8 +180,9 @@ const useSearchParams = ({
};

/**
* Store product path, parameter, in state. We're opting to use "window.location.pathname"
* directly since it appears to be quicker, and returns a similar structured value as useParam.
* Initialize and store product path, parameter, in a "state" update parallel to routing.
* We're opting to use "window.location.pathname" directly since it appears to be quicker,
* and returns a similar structured value as useParam.
*
* @param {object} options
* @param {Function} options.useSelector
Expand All @@ -190,7 +200,7 @@ const useSetRouteDetail = ({
const { pathname: productPath } = aliasWindowLocation;

useEffect(() => {
if (productPath && updatedPath !== productPath) {
if (productPath && productPath !== updatedPath) {
dispatch({
type: reduxTypes.app.SET_PRODUCT,
config: productPath
Expand Down

0 comments on commit 396dd2b

Please sign in to comment.