-
Notifications
You must be signed in to change notification settings - Fork 47
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
KHP3-7349-Implement Side navs and Nav groups with icons #564
Merged
donaldkibet
merged 3 commits into
palladiumkenya:main
from
Omoshlawi:KHP3-7349-implement-side-navs-and-nav-groups-with-icons
Jan 28, 2025
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
0c08c63
Grouped side nav link and implemented nav-util components and functions.
Omoshlawi ef90aeb
fix: update import statement for CarbonIconType to use type syntax
Omoshlawi af56254
fix: add order property to patientChartClinicalConsultationNavGroup i…
Omoshlawi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
packages/esm-version-app/src/app-navigation/home-root.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { ExtensionSlot, useExtensionStore } from '@openmrs/esm-framework'; | ||
import React, { useEffect } from 'react'; | ||
import { BrowserRouter, Route, Routes } from 'react-router-dom'; | ||
|
||
const HomeRoot = () => { | ||
const baseName = window.getOpenmrsSpaBase() + 'home'; | ||
|
||
return ( | ||
<BrowserRouter basename={baseName}> | ||
<Routes> | ||
<Route path="/" element={<ExtensionSlot name="home-dashboard-slot" />} /> | ||
<Route path="/providers/*" element={<ExtensionSlot name="providers-dashboard-slot" />} /> | ||
<Route path="/referrals/*" element={<ExtensionSlot name="referrals-slot" />} /> | ||
<Route path="/bed-admission/*" element={<ExtensionSlot name="bed-admission-dashboard-slot" />} /> | ||
{/* Patient services Routes */} | ||
<Route path="/appointments/*" element={<ExtensionSlot name="clinical-appointments-dashboard-slot" />} /> | ||
<Route path="/service-queues/*" element={<ExtensionSlot name="service-queues-dashboard-slot" />} /> | ||
{/* Diagnostics routes */} | ||
<Route path="/lab-manifest/*" element={<ExtensionSlot name="lab-manifest-slot" />} /> | ||
<Route path="/laboratory/*" element={<ExtensionSlot name="laboratory-dashboard-slot" />} /> | ||
<Route path="/procedure/*" element={<ExtensionSlot name="procedure-dashboard-slot" />} /> | ||
<Route path="/imaging-orders/*" element={<ExtensionSlot name="imaging-dashboard-slot" />} /> | ||
{/* lINKAGE services Routes */} | ||
<Route path="/pharmacy/*" element={<ExtensionSlot name="pharmacy-dashboard-slot" />} /> | ||
<Route path="/case-management/*" element={<ExtensionSlot name="case-management-dashboard-slot" />} /> | ||
<Route path="/peer-calendar/*" element={<ExtensionSlot name="peer-calendar-dashboard-slot" />} /> | ||
{/* Billing routes */} | ||
<Route path="/billing/*" element={<ExtensionSlot name="billing-dashboard-slot" />} /> | ||
</Routes> | ||
</BrowserRouter> | ||
); | ||
}; | ||
|
||
export default HomeRoot; |
27 changes: 27 additions & 0 deletions
27
packages/esm-version-app/src/app-navigation/nav-utils/create-dasboard-group.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from 'react'; | ||
import DashboardGroupExtension from './dashboard-group-extension.component'; | ||
import { CarbonIconType } from '@carbon/react/icons'; | ||
import { createDashboardGroup as cdg } from '@openmrs/esm-patient-common-lib'; | ||
type Conf = { | ||
title: string; | ||
slotName: string; | ||
isExpanded?: boolean; | ||
icon?: CarbonIconType; | ||
}; | ||
|
||
const createDashboardGroup = ({ slotName, title, isExpanded, icon }: Conf) => { | ||
const DashboardGroup = ({ basePath }: { basePath: string }) => { | ||
return ( | ||
<DashboardGroupExtension | ||
title={title} | ||
slotName={slotName} | ||
basePath={basePath} | ||
isExpanded={isExpanded} | ||
icon={icon} | ||
/> | ||
); | ||
}; | ||
return DashboardGroup; | ||
}; | ||
|
||
export default createDashboardGroup; |
21 changes: 21 additions & 0 deletions
21
packages/esm-version-app/src/app-navigation/nav-utils/create-left-panel-link.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from 'react'; | ||
import { BrowserRouter } from 'react-router-dom'; | ||
import { LinkExtension } from './link-extension.component'; | ||
import { CarbonIconType } from '@carbon/react/icons'; | ||
|
||
type LinkConfig = { | ||
route: string; | ||
title: string; | ||
otherRoutes?: Array<string>; | ||
icon?: CarbonIconType; | ||
}; | ||
|
||
const createLeftPanelLink = (config: LinkConfig) => { | ||
return () => ( | ||
<BrowserRouter> | ||
<LinkExtension route={config.route} title={config.title} otherRoutes={config.otherRoutes} icon={config.icon} /> | ||
</BrowserRouter> | ||
); | ||
}; | ||
|
||
export default createLeftPanelLink; |
21 changes: 21 additions & 0 deletions
21
...ages/esm-version-app/src/app-navigation/nav-utils/create-patient-chart-dashboard-meta.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from 'react'; | ||
import PatientChartDasboardExtension, { DashboardExtensionProps } from './patient-chart-dashboard.component'; | ||
import { BrowserRouter } from 'react-router-dom'; | ||
|
||
const createPatientChartDashboardExtension = (props: Omit<DashboardExtensionProps, 'basePath'>) => { | ||
return ({ basePath }: { basePath: string }) => { | ||
return ( | ||
<BrowserRouter> | ||
<PatientChartDasboardExtension | ||
basePath={basePath} | ||
title={props.title} | ||
path={props.path} | ||
moduleName={props.moduleName} | ||
icon={props.icon} | ||
/> | ||
</BrowserRouter> | ||
); | ||
}; | ||
}; | ||
|
||
export default createPatientChartDashboardExtension; |
36 changes: 36 additions & 0 deletions
36
...ages/esm-version-app/src/app-navigation/nav-utils/dashboard-group-extension.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { Accordion, AccordionItem } from '@carbon/react'; | ||
import { CarbonIconType } from '@carbon/react/icons'; | ||
import { ExtensionSlot } from '@openmrs/esm-framework'; | ||
import { registerNavGroup } from '@openmrs/esm-patient-common-lib'; | ||
import React, { useEffect } from 'react'; | ||
import styles from './nav.scss'; | ||
type Props = { | ||
title: string; | ||
slotName?: string; | ||
basePath: string; | ||
isExpanded?: boolean; | ||
icon?: CarbonIconType; | ||
}; | ||
const DashboardGroupExtension: React.FC<Props> = ({ basePath, title, isExpanded, slotName, icon }) => { | ||
useEffect(() => { | ||
registerNavGroup(slotName); | ||
}, [slotName]); | ||
return ( | ||
<Accordion> | ||
<AccordionItem | ||
className={styles.item} | ||
open={isExpanded ?? true} | ||
title={ | ||
<span className={styles.itemTitle}> | ||
{icon && React.createElement(icon)} | ||
{title} | ||
</span> | ||
} | ||
style={{ border: 'none' }}> | ||
<ExtensionSlot name={slotName ?? title} state={{ basePath }} /> | ||
</AccordionItem> | ||
</Accordion> | ||
); | ||
}; | ||
|
||
export default DashboardGroupExtension; |
6 changes: 6 additions & 0 deletions
6
packages/esm-version-app/src/app-navigation/nav-utils/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export * from './link-extension.component'; | ||
export * from './utils'; | ||
export { default as createDashboardGroup } from './create-dasboard-group'; | ||
export { default as createLeftPanelLink } from './create-left-panel-link'; | ||
export { default as PatientChartDasboardExtension } from './patient-chart-dashboard.component'; | ||
export { default as createPatientChartDashboardExtension } from './create-patient-chart-dashboard-meta'; |
48 changes: 48 additions & 0 deletions
48
packages/esm-version-app/src/app-navigation/nav-utils/link-extension.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { ConfigurableLink } from '@openmrs/esm-framework'; | ||
import React, { ReactNode, useCallback, useMemo } from 'react'; | ||
import { useLocation } from 'react-router-dom'; | ||
import { parseParams } from './utils'; | ||
import { CarbonIconType } from '@carbon/react/icons'; | ||
import styles from './nav.scss'; | ||
export interface LinkConfig { | ||
route: string; | ||
title: string; | ||
otherRoutes?: Array<string>; | ||
icon?: CarbonIconType; | ||
} | ||
|
||
export const LinkExtension: React.FC<LinkConfig> = ({ route, title, otherRoutes = [], icon }) => { | ||
const spaBasePath = window.getOpenmrsSpaBase(); | ||
const location = useLocation(); | ||
const path = useMemo(() => location.pathname.replace(spaBasePath, ''), [spaBasePath, location]); | ||
// Parse params to see if the current route matches the location path | ||
const matcher = useCallback( | ||
(route: string) => { | ||
const staticMatch = `/${route}/`.replaceAll('//', '/') === `/${path}/`.replaceAll('//', '/'); // Exact match for static routes | ||
const paramMatch = !staticMatch && parseParams(route, path) !== null; // Check parameterized match if not exact | ||
|
||
return staticMatch || paramMatch; | ||
}, | ||
[path], | ||
); | ||
// Check if the route is active | ||
const isActive = matcher(route); | ||
const isOtherRoutesActive = useMemo(() => { | ||
return otherRoutes.some(matcher); | ||
}, [otherRoutes, matcher]); | ||
// Generate the `to` URL for the ConfigurableLink | ||
const to = useMemo(() => { | ||
return (spaBasePath + route).replaceAll('//', '/'); | ||
}, [spaBasePath, route]); | ||
|
||
return ( | ||
<ConfigurableLink | ||
to={to} | ||
className={`cds--side-nav__link ${isActive || isOtherRoutesActive ? 'active-left-nav-link' : ''} ${ | ||
styles.itemTitle | ||
}`}> | ||
{icon && React.createElement(icon)} | ||
{title} | ||
</ConfigurableLink> | ||
); | ||
}; |
9 changes: 9 additions & 0 deletions
9
packages/esm-version-app/src/app-navigation/nav-utils/nav.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
@use '@carbon/layout'; | ||
@use '@carbon/type'; | ||
|
||
.itemTitle { | ||
display: flex; | ||
flex-direction: row; | ||
gap: layout.$spacing-05; | ||
align-items: center; | ||
} |
41 changes: 41 additions & 0 deletions
41
packages/esm-version-app/src/app-navigation/nav-utils/patient-chart-dashboard.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { CarbonIconType } from '@carbon/react/icons'; | ||
import { ConfigurableLink } from '@openmrs/esm-framework'; | ||
import classNames from 'classnames'; | ||
import last from 'lodash-es/last'; | ||
import React, { useMemo } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { useLocation } from 'react-router-dom'; | ||
import styles from './nav.scss'; | ||
|
||
export interface DashboardExtensionProps { | ||
path: string; | ||
title: string; | ||
basePath: string; | ||
moduleName?: string; | ||
icon?: CarbonIconType; | ||
} | ||
|
||
const PatientChartDasboardExtension = ({ | ||
path, | ||
title, | ||
basePath, | ||
moduleName = '@openmrs/esm-patient-chart-app', | ||
icon, | ||
}: DashboardExtensionProps) => { | ||
const { t } = useTranslation(moduleName); | ||
const location = useLocation(); | ||
const navLink = useMemo(() => decodeURIComponent(last(location.pathname.split('/'))), [location.pathname]); | ||
|
||
return ( | ||
<div key={path}> | ||
<ConfigurableLink | ||
className={classNames('cds--side-nav__link', { 'active-left-nav-link': path === navLink }, styles.itemTitle)} | ||
to={`${basePath}/${encodeURIComponent(path)}`}> | ||
{icon && React.createElement(icon)} | ||
{t(title)} | ||
</ConfigurableLink> | ||
</div> | ||
); | ||
}; | ||
|
||
export default PatientChartDasboardExtension; |
42 changes: 42 additions & 0 deletions
42
packages/esm-version-app/src/app-navigation/nav-utils/utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/** | ||
* Parses parameters from a path pattern and an actual path. | ||
* | ||
* @template T - A mapping of keys (parameter names) to their respective types. | ||
* @param pathPattern - The path pattern containing parameter placeholders (e.g., `/users/:userId/roles/:roleId`). | ||
* @param actualPath - The actual path to parse (e.g., `/users/123/roles/admin`). | ||
* @returns An object mapping parameter names to their corresponding values, or an empty object if no match is found. | ||
* | ||
* @example | ||
* const params = parseParams<{ userId: string, roleId: string }>( | ||
* "/users/:userId/roles/:roleId", | ||
* "/users/123/roles/admin" | ||
* ); | ||
* console.log(params); // { userId: "123", roleId: "admin" } | ||
*/ | ||
export function parseParams<T extends Record<string, string>>(pathPattern: string, actualPath: string): T | null { | ||
const patternSegments = pathPattern.split('/').filter(Boolean); | ||
const pathSegments = actualPath.split('/').filter(Boolean); | ||
|
||
// Return null if segments do not match in length | ||
if (patternSegments.length !== pathSegments.length) { | ||
return null; | ||
} | ||
|
||
const params: Partial<T> = {}; | ||
|
||
for (let i = 0; i < patternSegments.length; i++) { | ||
const patternSegment = patternSegments[i]; | ||
const pathSegment = pathSegments[i]; | ||
|
||
if (patternSegment.startsWith(':')) { | ||
// Extract the parameter name (e.g., `:userId` becomes `userId`) | ||
const paramName = patternSegment.slice(1); | ||
(params as any)[paramName] = pathSegment; | ||
} else if (patternSegment !== pathSegment) { | ||
// If non-parameter segments do not match, return null | ||
return null; | ||
} | ||
} | ||
|
||
return params as T; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.