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

Cnft1 3308 UI add link to nbs 6 search #1959

Merged
merged 25 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f884577
Add PatientSearchHeader to be interchanged with SearchNavigation base…
benlam-ignw Oct 22, 2024
32e9c2d
add events, investigations, and lab reports to the configuration and …
benlam-ignw Oct 23, 2024
5b64639
when features.search.events.enabled is true, patient search 7 beta is…
benlam-ignw Oct 23, 2024
6838308
make feature object properties optional since we have a fallback
benlam-ignw Oct 23, 2024
aa40cb2
create extended tooltip component
benlam-ignw Oct 23, 2024
619a3f0
make the extended tooltip a more generic component
benlam-ignw Oct 23, 2024
ae03daa
add a basic test for the extended tooltip component. TODO add more be…
benlam-ignw Oct 23, 2024
0a3d072
Merge branch 'main' into CNFT1-3308-UI-Add-link-to-NBS-6-Search
benlam-ignw Oct 23, 2024
e4f35f9
rename enclosing folders and files from ExtendedTooltip to RichTooltip
benlam-ignw Oct 23, 2024
b5b3f2f
update file contents to reflect name change from extended tooltip to …
benlam-ignw Oct 23, 2024
ef583e3
fix typo in styling
benlam-ignw Oct 23, 2024
2442d2b
Merge branch 'main' into CNFT1-3308-UI-Add-link-to-NBS-6-Search
benlam-ignw Oct 23, 2024
e9047ac
Merge branch 'main' into CNFT1-3308-UI-Add-link-to-NBS-6-Search
benlam-ignw Oct 23, 2024
ee09efb
refine styling a bit
benlam-ignw Oct 23, 2024
e1e3463
Merge branch 'CNFT1-3308-UI-Add-link-to-NBS-6-Search' of https://gith…
benlam-ignw Oct 23, 2024
40579e1
change patient search header styling and also rich tooltip styling
benlam-ignw Oct 24, 2024
3597145
update test
benlam-ignw Oct 24, 2024
93e1e09
rework the rich tooltip component to allow for much better customizab…
benlam-ignw Oct 24, 2024
f8d3d17
update RichTooltip
benlam-ignw Oct 24, 2024
c55dcdd
fix test
benlam-ignw Oct 24, 2024
ff9cedc
Merge branch 'main' into CNFT1-3308-UI-Add-link-to-NBS-6-Search
benlam-ignw Oct 24, 2024
1b4a0ed
Merge branch 'main' into CNFT1-3308-UI-Add-link-to-NBS-6-Search
benlam-ignw Oct 24, 2024
b18724a
replace hardcoded hex colors with values from _colors.scss
benlam-ignw Oct 24, 2024
2e38691
Merge branch 'CNFT1-3308-UI-Add-link-to-NBS-6-Search' of https://gith…
benlam-ignw Oct 24, 2024
4a31e9e
Merge branch 'main' into CNFT1-3308-UI-Add-link-to-NBS-6-Search
benlam-ignw Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { Button } from 'components/button';
import { Loading } from 'components/Spinner';
import { CollapsiblePanel } from 'design-system/collapsible-panel';
import { SearchNavigation } from './navigation/SearchNavigation';
import { PatientSearchHeader } from './patientSearchHeader/PatientSearchHeader';
import { useSearchInteraction, useSearchResultDisplay } from 'apps/search';
import { SearchLanding } from './landing';
import { SearchResults } from './result';
import { NoResults } from './result/none';
import { NoInput } from './result/NoInput';
import { FeatureToggle } from 'feature';

import styles from './search-layout.module.scss';

Expand Down Expand Up @@ -58,7 +60,12 @@ const SearchLayout = <R,>({

return (
<section className={styles.search}>
<SearchNavigation className={styles.navigation} actions={actions} />
<FeatureToggle
guard={(features) => features?.search?.events?.enabled}
fallback={<SearchNavigation className={styles.navigation} actions={actions} />}>
<PatientSearchHeader className={styles.navigation} actions={actions} />
</FeatureToggle>

<div className={styles.content}>
<CollapsiblePanel
className={styles.panel}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ReactNode, useRef } from 'react';
import classNames from 'classnames';
import styles from './patient-search-header.module.scss';
import { Link } from '@trussworks/react-uswds';
import { Icon } from 'design-system/icon';
import RichTooltip from 'design-system/richTooltip/RichTooltip';

type ActionsRenderer = () => ReactNode;

type Props = {
className?: string;
actions?: ActionsRenderer;
};

const PatientSearchHeader = ({ className, actions }: Props) => {
const infoIconRef = useRef<HTMLDivElement>(null);

return (
<nav className={classNames(styles.navigation, className)}>
<h1>Patient Search 7 Beta</h1>
<div className={styles.links}>
<div className={styles.linkContainer}>
<Link href="/nbs/LoadFindPatient1.do?ContextAction=GlobalPatient">Go to classic search</Link>
<RichTooltip marginTop={42} anchorRef={infoIconRef}>
<span>
<b>We are modernizing search</b>
<br />
To perform an event search or save a new custom queue, continue using classic search.
</span>
</RichTooltip>
<div className={styles.infoIconContainer} ref={infoIconRef}>
<Icon name="info_outline" color="#265e9d" />
</div>
</div>
</div>

{actions && actions()}
</nav>
);
};

export { PatientSearchHeader };
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.navigation {
display: flex;
gap: 1rem;
flex-shrink: 0;
z-index: 100;

h1 {
margin: 0;
}

.linkContainer {
display: flex;
align-items: center;
min-height: 2.75rem;
}

.links {
flex-grow: 1;
}
}

.infoIconContainer {
display: flex;
justify-content: center;
align-items: center;
height: 24px;
width: 24px;
}
3 changes: 3 additions & 0 deletions apps/modernization-ui/src/configuration/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type SearchView = Toggle & {
};

type Search = {
events: Toggle;
investigations: Toggle;
laboratoryReports: Toggle;
view: SearchView;
};

Expand Down
9 changes: 9 additions & 0 deletions apps/modernization-ui/src/configuration/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { Features, Properties, Configuration, Settings, Search } from './configuration';

const search: Search = {
events: {
enabled: true
},
investigations: {
enabled: true
},
laboratoryReports: {
enabled: true
},
view: {
table: {
enabled: false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { render } from '@testing-library/react';
import RichTooltip from './RichTooltip';

describe('when a rich tooltip is displayed', () => {
it('should render with no errors.', async () => {
const mockRichTooltipAnchorRef: React.RefObject<HTMLElement> = {
current: document.createElement('div', { is: 'mockRichTooltipAnchorRef' })
};
const { container } = render(
<div>
<RichTooltip anchorRef={mockRichTooltipAnchorRef}>Contents</RichTooltip>
</div>
);

expect(container).toBeTruthy();
expect(mockRichTooltipAnchorRef.current).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { RefObject, useEffect, useRef } from 'react';
import { useTooltip } from './useRichTooltip';
import styles from './rich-tooltip.module.scss';

type RichTooltipProps = {
anchorRef: RefObject<HTMLElement>;
children: React.ReactNode;
marginTop?: number;
marginLeft?: number;
};

/**
* Rich Tooltip component that provides a full React Node outlet for the tooltip that you wish to display.
* The children of the tooltip component is the content of the tooltip itself that is shown or hidden on mouseover.
* The anchorRef is the ref to the anchor element that is permanently displayed to the user (i.e an icon or button).
* You can override the default placement of the tooltip using the optional marginTop and marginLeft parameters.
*
* @param {React.ReactNode} param0.children
* @param {RefObject<HTMLElement>} param0.anchorRef
* @param {number} [param0.marginTop]
* @param {number} [param0.marginLeft]
* @return {JSX.Element}
*/
const RichTooltip = ({ children, anchorRef, marginTop, marginLeft }: RichTooltipProps) => {
const richTooltipRef = useRef<HTMLDivElement>(null);

const { position, isVisible, onMouseEnter, onMouseLeave } = useTooltip({
anchorRef,
richTooltipRef,
marginTop,
marginLeft
});

useEffect(() => {
const element = anchorRef?.current;
if (element) {
element.addEventListener('mouseenter', onMouseEnter);
element.addEventListener('mouseleave', onMouseLeave);
}
return () => {
if (element) {
element.removeEventListener('mouseenter', onMouseEnter);
element.removeEventListener('mouseleave', onMouseLeave);
}
};
}, [anchorRef, onMouseEnter, onMouseLeave]);

if (!isVisible) {
return null;
}

return (
<div
ref={richTooltipRef}
className={styles.richtooltipcontainer}
style={{
top: position.top,
left: position.left
}}>
{children}
</div>
);
};

export default RichTooltip;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@use 'styles/colors.scss';

.richtooltipcontainer {
position: absolute;
margin: 0;
padding: 16px;
font-size: 14px;
line-height: 20px;
background-color: colors.$base-white;
color: colors.$base-darkest;
outline-color: colors.$base-lighter;
outline-width: 1px;
outline-style: solid;
filter: drop-shadow(0px 4px 4px colors.$base-light);
width: 300px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { RefObject, useCallback, useMemo, useState } from 'react';

type UseTooltipProps = {
anchorRef: RefObject<HTMLElement>;
richTooltipRef: RefObject<HTMLDivElement>;
marginTop?: number;
marginLeft?: number;
};

type Position = {
top?: number;
left?: number;
width?: number;
};

export function useTooltip({ anchorRef, richTooltipRef, marginTop, marginLeft }: UseTooltipProps) {
const [isVisible, setIsVisible] = useState<boolean>(false);
const [position, setPosition] = useState<Position>({});

const calcBottomLeftPosition = (top: number, left: number, width: number) => {
// The left is calculated from the center of the element which the tooltip parent element is attached to.
// The offset is calculated then from subtracting half of that parent element's width to align with the start of the parent element.
const leftOffset = left - width / 2;
return { top: top, left: leftOffset, width: width };
};

useMemo(() => {
if (!anchorRef.current) {
return;
}

if (isVisible) {
const { top, left, width } = anchorRef.current.getBoundingClientRect();
setPosition({ ...calcBottomLeftPosition(marginTop || top, marginLeft || left, width) });
}

if (!isVisible) {
setPosition({});
}
}, [isVisible, anchorRef, richTooltipRef]);

const onMouseEnter = useCallback(() => {
setIsVisible(true);
}, []);

const onMouseLeave = useCallback(() => {
setIsVisible(false);
}, []);

return {
position: {
top: position.top ?? 0,
left: position.left ?? 0,
width: position.width ?? 0
},
isVisible,
onMouseEnter,
onMouseLeave
};
}
Loading