Skip to content

Commit

Permalink
Migrate chrome navigation state to Jotai. (#2906)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hyperkid123 authored Jul 29, 2024
1 parent bf9966d commit 1471b6e
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 541 deletions.
5 changes: 2 additions & 3 deletions src/components/AppFilter/useAppFilter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import axios from 'axios';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { BundleNavigation, ChromeModule, NavItem } from '../../@types/types';
import { ReduxState } from '../../redux/store';
import { getChromeStaticPathname } from '../../utils/common';
import { evaluateVisibility } from '../../utils/isNavItemVisible';
import { useAtomValue } from 'jotai';
import { chromeModulesAtom } from '../../state/atoms/chromeModuleAtom';
import { isPreviewAtom } from '../../state/atoms/releaseAtom';
import { navigationAtom } from '../../state/atoms/navigationAtom';

const LOCAL_PREVIEW = localStorage.getItem('chrome:local-preview') === 'true';

Expand Down Expand Up @@ -101,7 +100,7 @@ const useAppFilter = () => {
},
},
});
const existingSchemas = useSelector(({ chrome: { navigation } }: ReduxState) => navigation);
const existingSchemas = useAtomValue(navigationAtom);
const modules = useAtomValue(chromeModulesAtom);

const handleBundleData = async ({ data: { id, navItems, title } }: { data: BundleNavigation }) => {
Expand Down
43 changes: 20 additions & 23 deletions src/components/Navigation/DynamicNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import { useLoadModule } from '@scalprum/react-core';
import { Skeleton, SkeletonSize } from '@redhat-cloud-services/frontend-components/Skeleton';
import { NavItem } from '@patternfly/react-core/dist/dynamic/components/Nav';
import { useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import isEqual from 'lodash/isEqual';
import ChromeNavItem from './ChromeNavItem';
import { loadLeftNavSegment } from '../../redux/actions';
import { ReduxState } from '../../redux/store';
import { DynamicNavProps, NavItem as NavItemType, Navigation } from '../../@types/types';
import { useSetAtom } from 'jotai';
import { getDynamicSegmentItemsAtom, getNavigationSegmentAtom, setNavigationSegmentAtom } from '../../state/atoms/navigationAtom';

const toArray = (value: NavItemType | NavItemType[]) => (Array.isArray(value) ? value : [value]);
const mergeArrays = (orig: any[], index: number, value: any[]) => [...orig.slice(0, index), ...toArray(value), ...orig.slice(index)];
Expand All @@ -20,11 +19,11 @@ const isRootNavigation = (schema?: Navigation | NavItemType[]): schema is Naviga
const HookedNavigation = ({ useNavigation, dynamicNav, pathname, ...props }: DynamicNavProps) => {
const currentNamespace = pathname.split('/')[1];
const [isLoaded, setIsLoaded] = useState(false);
const dispatch = useDispatch();
const schema = useSelector(({ chrome: { navigation } }: ReduxState) => navigation[currentNamespace]);
const currNav = useSelector(({ chrome: { navigation } }: ReduxState) =>
(navigation[currentNamespace] as Navigation | undefined)?.navItems?.filter((item) => item.dynamicNav === dynamicNav)
);
const getSchema = useSetAtom(getNavigationSegmentAtom);
const getCurrNav = useSetAtom(getDynamicSegmentItemsAtom);
const setnavigationSegment = useSetAtom(setNavigationSegmentAtom);
const schema = getSchema(currentNamespace);
const currNav = getCurrNav(currentNamespace, dynamicNav);
const newNav = useNavigation({ schema, dynamicNav, currentNamespace, currNav });
useEffect(() => {
if (newNav) {
Expand All @@ -36,21 +35,19 @@ const HookedNavigation = ({ useNavigation, dynamicNav, pathname, ...props }: Dyn
if (!isEqual(newValue, currNav) && isRootNavigation(schema)) {
const currNavIndex = schema.navItems.findIndex((item) => item.dynamicNav === dynamicNav);
if (currNavIndex !== -1) {
dispatch(
loadLeftNavSegment(
{
...schema,
navItems: mergeArrays(
schema.navItems.filter((item) => !(item.dynamicNav && item.dynamicNav === dynamicNav)),
currNavIndex,
newValue
),
},
currentNamespace,
pathname,
true
)
);
setnavigationSegment({
schema: {
...schema,
navItems: mergeArrays(
schema.navItems.filter((item) => !(item.dynamicNav && item.dynamicNav === dynamicNav)),
currNavIndex,
newValue
),
},
segment: currentNamespace,
pathname,
shouldMerge: true,
});
}
}
setIsLoaded(true);
Expand Down
5 changes: 2 additions & 3 deletions src/hooks/useBreadcrumbsLinks.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { useSelector } from 'react-redux';
import { useEffect, useMemo, useState } from 'react';
import { matchRoutes, useLocation } from 'react-router-dom';
import { Required } from 'utility-types';

import { ReduxState } from '../redux/store';
import useBundle from './useBundle';
import { NavItem } from '../@types/types';
import { findNavLeafPath } from '../utils/common';
import { extractNavItemGroups, isNavItems } from '../utils/fetchNavigationFiles';
import { useAtomValue } from 'jotai';
import { moduleRoutesAtom } from '../state/atoms/chromeModuleAtom';
import { navigationAtom } from '../state/atoms/navigationAtom';

const useBreadcrumbsLinks = () => {
const { bundleId, bundleTitle } = useBundle();
const routes = useAtomValue(moduleRoutesAtom);
const navigation = useSelector(({ chrome: { navigation } }: ReduxState) => navigation);
const navigation = useAtomValue(navigationAtom);
const { pathname } = useLocation();
const [segments, setSegments] = useState<Required<NavItem, 'href'>[]>([]);
const wildCardRoutes = useMemo(() => routes.map((item) => ({ ...item, path: `${item.path}/*` })), [routes]);
Expand Down
3 changes: 0 additions & 3 deletions src/redux/action-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ export const GLOBAL_FILTER_UPDATE = '@@chrome/global-filter-update';
export const GLOBAL_FILTER_TOGGLE = '@@chrome/global-filter-toggle';
export const GLOBAL_FILTER_REMOVE = '@@chrome/global-filter-remove';

export const LOAD_NAVIGATION_LANDING_PAGE = '@@chrome/load-navigation-landing-page';
export const LOAD_LEFT_NAVIGATION_SEGMENT = '@@chrome/load-navigation-segment';

export const UPDATE_DOCUMENT_TITLE_REDUCER = '@@chrome/update-document-title';
export const MARK_ACTIVE_PRODUCT = '@@chrome/mark-active-product';

Expand Down
17 changes: 1 addition & 16 deletions src/redux/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as actionTypes from './action-types';
import { getAllSIDs, getAllTags, getAllWorkloads } from '../components/GlobalFilter/tagsApi';
import type { TagFilterOptions, TagPagination } from '../components/GlobalFilter/tagsApi';
import type { ChromeUser } from '@redhat-cloud-services/types';
import type { FlagTagsFilter, NavItem, Navigation } from '../@types/types';
import type { FlagTagsFilter } from '../@types/types';
import type { QuickStart } from '@patternfly/quickstarts';

export function userLogIn(user: ChromeUser | boolean) {
Expand Down Expand Up @@ -75,21 +75,6 @@ export function removeGlobalFilter(isHidden = true) {
};
}

export const loadNavigationLandingPage = (schema: NavItem[]) => ({
type: actionTypes.LOAD_NAVIGATION_LANDING_PAGE,
payload: schema,
});

export const loadLeftNavSegment = (schema: Navigation, segment: string, pathName: string, shouldMerge?: boolean) => ({
type: actionTypes.LOAD_LEFT_NAVIGATION_SEGMENT,
payload: {
segment,
schema,
pathName,
shouldMerge,
},
});

/**
* @deprecated
*/
Expand Down
92 changes: 0 additions & 92 deletions src/redux/chromeReducers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,98 +106,6 @@ describe('Reducers', () => {
});
});

describe('loadNavigationSegmentReducer', () => {
const navigation = { test: { navItems: [], sortedLinks: [] } };
it('should create new segment', () => {
const newNav = {
navItems: [
{
href: '/something',
},
],
};
const result = reducers.loadNavigationSegmentReducer(
{
navigation: {},
},
{
payload: {
segment: 'test',
schema: newNav,
},
}
);
expect(result).toEqual({
navigation: {
...navigation,
test: {
...navigation.test,
navItems: newNav.navItems,
sortedLinks: ['/something'],
},
},
});
});

it('should replace schema', () => {
const newNav = { navItems: [{ href: '/another' }, { href: '/different' }] };
const result = reducers.loadNavigationSegmentReducer(
{
navigation: {
...navigation,
test: {
...navigation.test,
navItems: [{ href: '/something' }],
sortedLinks: ['/something'],
},
},
},
{
payload: {
segment: 'test',
schema: newNav,
shouldMerge: true,
},
}
);
expect(result).toEqual({
navigation: {
...navigation,
test: {
...navigation.test,
navItems: newNav.navItems,
sortedLinks: ['/different', '/another'],
},
},
});
});

it('should highlight items', () => {
const result = reducers.loadNavigationSegmentReducer(
{
navigation: {},
},
{
payload: {
segment: 'test',
schema: { navItems: [{ href: '/something' }] },
pathName: '/something',
},
}
);
expect(result).toEqual({
navigation: {
...navigation,
test: {
...navigation.test,
navItems: [{ href: '/something', active: true }],
sortedLinks: ['/something'],
},
},
});
});
});

describe('Add Quickstarts to App', () => {
let prevState;
beforeEach(
Expand Down
49 changes: 1 addition & 48 deletions src/redux/chromeReducers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { QuickStart } from '@patternfly/quickstarts';
import { ChromeUser } from '@redhat-cloud-services/types';
import { NavItem, Navigation } from '../@types/types';
import { ITLess, highlightItems, levelArray } from '../utils/common';
import { ITLess } from '../utils/common';
import { ChromeState } from './store';

export function loginReducer(state: ChromeState, { payload }: { payload: ChromeUser }): ChromeState {
Expand All @@ -26,52 +25,6 @@ export function onPageObjectId(state: ChromeState, { payload }: { payload: strin
};
}

export function loadNavigationLandingPageReducer(state: ChromeState, { payload }: { payload: NavItem[] }): ChromeState {
return {
...state,
navigation: {
...state.navigation,
landingPage: payload,
},
};
}

function isNavigation(nav?: Navigation | NavItem[]): nav is Navigation {
return !Array.isArray(nav);
}

export function loadNavigationSegmentReducer(
state: ChromeState,
{
payload: { segment, schema, pathName, shouldMerge },
}: {
payload: {
segment: string;
schema: Navigation;
pathName: string;
shouldMerge?: boolean;
};
}
): ChromeState {
const mergedSchema = shouldMerge || !state.navigation?.[segment] ? schema : state.navigation?.[segment];
if (isNavigation(mergedSchema)) {
// Landing page navgation has different siganture
const sortedLinks = levelArray(mergedSchema?.navItems).sort((a, b) => (a.length < b.length ? 1 : -1));
return {
...state,
navigation: {
...state.navigation,
[segment]: {
...mergedSchema,
navItems: pathName ? highlightItems(pathName, mergedSchema.navItems, sortedLinks) : mergedSchema.navItems,
sortedLinks,
},
},
};
}
return state;
}

export function populateQuickstartsReducer(
state: ChromeState,
{ payload: { app, quickstarts } }: { payload: { app: string; quickstarts: QuickStart[] } }
Expand Down
8 changes: 0 additions & 8 deletions src/redux/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import {
clearQuickstartsReducer,
disableQuickstartsReducer,
documentTitleReducer,
loadNavigationLandingPageReducer,
loadNavigationSegmentReducer,
loginReducer,
markActiveProduct,
onPageAction,
Expand Down Expand Up @@ -39,8 +37,6 @@ import {
GLOBAL_FILTER_SCOPE,
GLOBAL_FILTER_TOGGLE,
GLOBAL_FILTER_UPDATE,
LOAD_LEFT_NAVIGATION_SEGMENT,
LOAD_NAVIGATION_LANDING_PAGE,
MARK_ACTIVE_PRODUCT,
POPULATE_QUICKSTARTS_CATALOG,
UPDATE_DOCUMENT_TITLE_REDUCER,
Expand All @@ -53,8 +49,6 @@ const reducers = {
[USER_LOGIN]: loginReducer,
[CHROME_PAGE_ACTION]: onPageAction,
[CHROME_PAGE_OBJECT]: onPageObjectId,
[LOAD_NAVIGATION_LANDING_PAGE]: loadNavigationLandingPageReducer,
[LOAD_LEFT_NAVIGATION_SEGMENT]: loadNavigationSegmentReducer,
[POPULATE_QUICKSTARTS_CATALOG]: populateQuickstartsReducer,
[ADD_QUICKSTARTS_TO_APP]: addQuickstartstoApp,
[DISABLE_QUICKSTARTS]: disableQuickstartsReducer,
Expand All @@ -78,7 +72,6 @@ const globalFilter = {

export const chromeInitialState: ReduxState = {
chrome: {
navigation: {},
quickstarts: {
quickstarts: {},
},
Expand All @@ -93,7 +86,6 @@ export default function (): {
return {
chrome: (
state = {
navigation: {},
quickstarts: {
quickstarts: {},
},
Expand Down
14 changes: 2 additions & 12 deletions src/redux/store.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import { QuickStart } from '@patternfly/quickstarts';

import { FlagTagsFilter, NavItem, Navigation } from '../@types/types';

export type InternalNavigation = {
[key: string]: Navigation | NavItem[] | undefined;
landingPage?: NavItem[];
};
import { FlagTagsFilter } from '../@types/types';

export type ChromeState = {
activeProduct?: string;
missingIDP?: boolean;
pageAction?: string;
pageObjectId?: string;
navigation: InternalNavigation;
// accessRequests: {
// count: number;
// data: AccessRequest[];
// hasUnseen: boolean;
// };
// navigation: InternalNavigation;
initialHash?: string;
quickstarts: {
disabled?: boolean;
Expand Down
Loading

0 comments on commit 1471b6e

Please sign in to comment.