Skip to content

Commit d2de5d1

Browse files
committed
AppLink: remove appUrl and href in favor of to
1 parent 01a296a commit d2de5d1

File tree

4 files changed

+38
-65
lines changed

4 files changed

+38
-65
lines changed

packages/components/src/internal/components/navigation/NavItem.tsx

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ interface NavItemProps extends PropsWithChildren {
2727

2828
export const NavItem: FC<NavItemProps> = memo(({ children, onActive, to, isActive }) => {
2929
const location = useLocation();
30-
// AppLink is explicit about AppURL vs href
31-
const href = to instanceof AppURL ? undefined : to;
32-
const appUrl = to instanceof AppURL ? to : undefined;
3330
const itemRef = useRef<HTMLLIElement>(undefined);
3431
const [active, setActive] = useState<boolean>(false);
3532

@@ -54,31 +51,23 @@ export const NavItem: FC<NavItemProps> = memo(({ children, onActive, to, isActiv
5451

5552
return (
5653
<li className={active ? 'active' : null} ref={itemRef}>
57-
<AppLink href={href} appUrl={appUrl}>
58-
{children}
59-
</AppLink>
54+
<AppLink to={to}>{children}</AppLink>
6055
</li>
6156
);
6257
});
6358
NavItem.displayName = 'NavItem';
6459

65-
export const ParentNavItem: FC<NavItemProps> = memo(({ children, to }) => {
66-
// AppLink is explicit about AppURL vs href
67-
const href = to instanceof AppURL ? undefined : to;
68-
const appUrl = to instanceof AppURL ? to : undefined;
69-
70-
return (
71-
<div className="parent-nav">
72-
<ul className="nav navbar-nav">
73-
<li>
74-
<AppLink href={href} appUrl={appUrl}>
75-
<i className="fa fa-chevron-left" />
76-
&nbsp;
77-
{children}
78-
</AppLink>
79-
</li>
80-
</ul>
81-
</div>
82-
);
83-
});
60+
export const ParentNavItem: FC<NavItemProps> = memo(({ children, to }) => (
61+
<div className="parent-nav">
62+
<ul className="nav navbar-nav">
63+
<li>
64+
<AppLink to={to}>
65+
<i className="fa fa-chevron-left" />
66+
&nbsp;
67+
{children}
68+
</AppLink>
69+
</li>
70+
</ul>
71+
</div>
72+
));
8473
ParentNavItem.displayName = 'ParentNavItem';

packages/components/src/internal/components/navigation/ProductMenuSection.tsx

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ MenuSectionItemLabel.displayName = 'MenuSectionItemLabel';
4747

4848
const MenuSectionLink: FC<MenuSectionLinkProps> = ({ config, item }) => {
4949
// const isAppUrl = (item.url instanceof AppURL || item.url.indexOf('#') === 0) && !config.useOriginalURL;
50-
const href = item.url instanceof AppURL ? undefined : item.url;
51-
const appUrl = item.url instanceof AppURL ? item.url : undefined;
5250
return (
53-
<AppLink appUrl={appUrl} href={href} className="menu-section-link">
51+
<AppLink to={item.url} className="menu-section-link">
5452
<MenuSectionItemLabel config={config} item={item} />
5553
</AppLink>
5654
);
@@ -92,9 +90,8 @@ export const ProductMenuSection: FC<MenuSectionProps> = memo(props => {
9290
headerText
9391
);
9492

95-
// In order to make sure we don't break useRouteLeave we need to use <Link> for AppURLs and <a> for non-app URLs
96-
let headerEl = label;
97-
if (headerEl) {
93+
let headerEl: ReactNode;
94+
if (label) {
9895
const headerURL = config.useOriginalURL
9996
? section.url
10097
: createProductUrlFromPartsWithContainer(
@@ -105,33 +102,22 @@ export const ProductMenuSection: FC<MenuSectionProps> = memo(props => {
105102
config.headerURLPart ?? section.key
106103
);
107104

108-
if (headerURL instanceof AppURL) {
109-
headerEl = (
110-
<Link to={headerURL.toString()} className="menu-section-link">
111-
{label}
112-
</Link>
113-
);
114-
} else {
115-
headerEl = <a href={getHref(headerURL)}>{label}</a>;
116-
}
105+
const className = headerURL instanceof AppURL ? 'menu-section-link' : undefined;
106+
headerEl = (
107+
<AppLink to={headerURL} className={className}>
108+
{label}
109+
</AppLink>
110+
);
117111
}
118112

119113
let emptyLink: ReactNode;
120114
if (config.emptyAppURL) {
121115
const emptyURL = createProductUrl(section.productId, currentProductId, config.emptyAppURL, containerPath);
122-
if (emptyURL instanceof AppURL) {
123-
emptyLink = (
124-
<Link className="menu-section-link" to={emptyURL.toString()}>
125-
{config.emptyURLText}
126-
</Link>
127-
);
128-
} else {
129-
emptyLink = (
130-
<a className="menu-section-link" href={emptyURL}>
131-
{config.emptyURLText}
132-
</a>
133-
);
134-
}
116+
emptyLink = (
117+
<AppLink to={emptyURL} className="menu-section-link">
118+
{config.emptyURLText}
119+
</AppLink>
120+
);
135121
}
136122

137123
const visibleItems = section.items.filter(item => !item.hidden).sortBy(item => item.label, naturalSort);

packages/components/src/internal/renderers/DefaultRenderer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ interface Props {
3737
}
3838

3939
const TARGET_BLANK = '_blank';
40-
const URL_REL = 'noopener noreferrer';
40+
4141
/**
4242
* This is the default cell renderer for Details/Grids using a QueryGridModel.
4343
*/
@@ -76,7 +76,7 @@ export const DefaultRenderer: FC<Props> = memo(({ col, data, noLink }) => {
7676
if (url && !noLink) {
7777
const targetBlank = data.get('urlTarget') === TARGET_BLANK;
7878
return (
79-
<AppLink className={className} href={url} targetBlank={targetBlank} style={style}>
79+
<AppLink className={className} to={url} targetBlank={targetBlank} style={style}>
8080
{display}
8181
</AppLink>
8282
);

packages/components/src/internal/url/AppLink.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import React, { FC, memo, PropsWithChildren, StyleHTMLAttributes } from 'react';
22

33
import { Link } from 'react-router-dom';
44

5-
import { AppURL } from './AppURL';
65
import { ActionURL } from '@labkey/api';
76

7+
import { AppURL } from './AppURL';
8+
89
const TARGET_BLANK = '_blank';
910
const URL_REL = 'noopener noreferrer';
1011

@@ -34,27 +35,24 @@ export function parseAppPath(href: string): string | undefined {
3435
}
3536

3637
interface Props extends PropsWithChildren {
37-
appUrl?: AppURL;
3838
className?: string;
39-
href?: string;
4039
style?: StyleHTMLAttributes<HTMLAnchorElement>;
4140
targetBlank?: boolean;
41+
to: string | AppURL;
4242
}
4343

4444
/**
4545
* Use this when you need to generate an anchor tag. This should be preferred to all usages of <a> or <Link>, as it
4646
* handles all the corner cases around moving within our apps, between our apps, to other parts of LKS, and externally.
4747
*/
4848
export const AppLink: FC<Props> = memo(props => {
49-
const { appUrl, children, className, href, style, targetBlank } = props;
50-
51-
if (!appUrl && !href) throw new Error('AppLink: incorrect usage. Must pass "href" or "appUrl".');
49+
const { children, className, style, targetBlank, to } = props;
5250

53-
const to = appUrl ? appUrl.toString() : parseAppPath(href);
51+
const appPath = to instanceof AppURL ? to.toString() : parseAppPath(to);
5452

55-
if (to) {
53+
if (appPath) {
5654
return (
57-
<Link className={className} style={style} to={to}>
55+
<Link className={className} style={style} to={appPath}>
5856
{children}
5957
</Link>
6058
);
@@ -63,7 +61,7 @@ export const AppLink: FC<Props> = memo(props => {
6361
return (
6462
<a
6563
className={className}
66-
href={href}
64+
href={to as string}
6765
rel={targetBlank ? URL_REL : undefined}
6866
style={style}
6967
target={targetBlank ? TARGET_BLANK : undefined}

0 commit comments

Comments
 (0)