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

feat(Touchable): newTab support in to links #1143

Merged
merged 4 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
24 changes: 24 additions & 0 deletions src/__tests__/touchable-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,27 @@ test('Touchable has the appropiate role', async () => {
expect(screen.getByRole('tab', {name: 'href'})).toBeInTheDocument();
expect(screen.getByRole('link', {name: 'onPress'})).toBeInTheDocument();
});

test('target="_blank" is set with newTab', async () => {
const url = 'https://example.org';

render(
<ThemeContextProvider theme={makeTheme()}>
<Touchable to={url} newTab>
to link
</Touchable>
<Touchable href={url} newTab>
href link
</Touchable>
</ThemeContextProvider>
);

const toLink = screen.getByRole('link', {name: /to link/});
const hrefLink = screen.getByRole('link', {name: /href link/});

expect(toLink).toHaveAttribute('href', url);
expect(hrefLink).toHaveAttribute('href', url);

expect(toLink).toHaveAttribute('target', '_blank');
expect(hrefLink).toHaveAttribute('target', '_blank');
});
4 changes: 1 addition & 3 deletions src/__type_tests__/highlighted-card-type-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const s = 'maybe string' as string | undefined;

<HighlightedCard title="title" onPress={() => {}} />;
<HighlightedCard title="title" to="#" fullPageOnWebView />;
<HighlightedCard title="title" to="#" newTab />;
<HighlightedCard title="title" href="#" newTab />;
<HighlightedCard title="title" button={<ButtonPrimary onPress={() => {}}>button</ButtonPrimary>} />;

Expand All @@ -29,9 +30,6 @@ const s = 'maybe string' as string | undefined;
// @ts-expect-error incompatible touchable props
<HighlightedCard title="title" onPress={() => {}} newTab />;

// @ts-expect-error incompatible touchable props
<HighlightedCard title="title" to="#" newTab />;

// @ts-expect-error incompatible touchable props
<HighlightedCard title="title" onPress={() => {}} fullPageOnWebView />;

Expand Down
4 changes: 2 additions & 2 deletions src/__type_tests__/icon-button-type-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {Icon2GRegular, IconButton} from '..';
trackingEvent={{name: 'something'}}
to="/somewhere"
/>;
<IconButton Icon={Icon2GRegular} aria-label="something" to="/somewhere" newTab />;

// @ts-expect-error - onPress doesn't support href
<IconButton Icon={Icon2GRegular} aria-label="something" onPress={() => {}} href="/somewhere" />;
Expand All @@ -55,8 +56,7 @@ import {Icon2GRegular, IconButton} from '..';
<IconButton Icon={Icon2GRegular} aria-label="something" href="/somewhere" to="/somewhere" />;

<IconButton Icon={Icon2GRegular} aria-label="something" href="/somewhere" newTab />;
// @ts-expect-error - to doesn't support newTab
<IconButton Icon={Icon2GRegular} aria-label="something" to="/somewhere" newTab />;

// @ts-expect-error - onPress doesn't support newTab
<IconButton Icon={Icon2GRegular} aria-label="something" onPress={() => {}} newTab />;

Expand Down
1 change: 0 additions & 1 deletion src/__type_tests__/list-type-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const v = true as boolean;
<Row title="to" to="/to" />
<Row title="to" to="/to" trackingEvent={{name: 'something'}} />
<Row title="to" to="/to" fullPageOnWebView />
{/* @ts-expect-error - newTab not allowed in to */}
<Row title="to" to="/to" newTab />
{/* OK - href */}
<Row title="href" href="/href" />
Expand Down
1 change: 0 additions & 1 deletion src/__type_tests__/touchable-type-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import type {TouchableComponentProps} from '../touchable';
text
</Touchable>;

// @ts-expect-error - to doesn't support newTab
<Touchable to="/" newTab>
text
</Touchable>;
Expand Down
40 changes: 11 additions & 29 deletions src/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
TrackingEvent,
} from './utils/types';
import type {Location} from 'history';
import type {ExclusifyUnion} from './utils/utility-types';

const renderButtonElement = ({
content,
Expand Down Expand Up @@ -261,54 +262,31 @@ interface CommonProps {

export interface ToButtonProps extends CommonProps {
to: string | Location;
newTab?: boolean;
/** @deprecated use newTab */
fullPageOnWebView?: boolean;
submit?: undefined;
fake?: undefined;
onPress?: undefined;
href?: undefined;
onNavigate?: () => void | Promise<void>;
}
export interface OnPressButtonProps extends CommonProps {
onPress: (event: React.MouseEvent<HTMLElement>) => void | undefined | Promise<void>;
submit?: undefined;
fake?: undefined;
to?: undefined;
href?: undefined;
onNavigate?: undefined;
}
export interface HrefButtonProps extends CommonProps {
href: string;
newTab?: boolean;
loadOnTop?: boolean;
submit?: undefined;
fake?: undefined;
onPress?: undefined;
to?: undefined;

onNavigate?: () => void | Promise<void>;
}
export interface FakeButtonProps extends CommonProps {
fake: true;
submit?: undefined;
onPress?: undefined;
to?: undefined;
href?: undefined;
onNavigate?: undefined;
}
export interface SubmitButtonProps extends CommonProps {
submit: true;
to?: undefined;
fake?: undefined;
onPress?: undefined;
href?: undefined;
onNavigate?: undefined;
}

export type ButtonProps =
| FakeButtonProps
| SubmitButtonProps
| ToButtonProps
| OnPressButtonProps
| HrefButtonProps;
export type ButtonProps = ExclusifyUnion<
FakeButtonProps | SubmitButtonProps | ToButtonProps | OnPressButtonProps | HrefButtonProps
>;

const Button = React.forwardRef<TouchableElement, ButtonProps & {type: ButtonType}>((props, ref) => {
const {textPresets} = useTheme();
Expand Down Expand Up @@ -437,6 +415,7 @@ const Button = React.forwardRef<TouchableElement, ButtonProps & {type: ButtonTyp
<BaseTouchable
{...commonProps}
to={props.to}
newTab={props.newTab}
fullPageOnWebView={props.fullPageOnWebView}
onNavigate={props.onNavigate}
/>
Expand Down Expand Up @@ -504,6 +483,8 @@ interface ButtonLinkHrefProps extends ButtonLinkCommonProps {

interface ButtonLinkToProps extends ButtonLinkCommonProps {
to: string;
newTab?: boolean;
/** @deprecated use newTab */
fullPageOnWebView?: boolean;
pladaria marked this conversation as resolved.
Show resolved Hide resolved
onPress?: undefined;
href?: undefined;
Expand Down Expand Up @@ -634,6 +615,7 @@ const BaseButtonLink = React.forwardRef<
ref={ref}
{...commonProps}
to={props.to}
newTab={props.newTab}
fullPageOnWebView={props.fullPageOnWebView}
onNavigate={props.onNavigate}
/>
Expand Down
10 changes: 9 additions & 1 deletion src/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ type BaseIconButtonAction = {
type IconButtonAction = BaseIconButtonAction &
ExclusifyUnion<
| {href: string; newTab?: boolean}
| {to: string; fullPageOnWebView?: boolean; replace?: boolean}
| {
to: string;
newTab?: boolean;
/** @deprecated use newTab */
fullPageOnWebView?: boolean;
replace?: boolean;
}
| {onPress: () => void}
>;

Expand Down Expand Up @@ -474,6 +480,8 @@ type TouchableProps = {
}
| {
to: string | undefined;
newTab?: boolean;
/** @deprecated use newTab */
fullPageOnWebView?: boolean;
replace?: boolean;
onNavigate?: () => void | Promise<void>;
Expand Down
16 changes: 13 additions & 3 deletions src/community/advanced-data-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,19 @@ import type {
type TouchableProps = {
trackingEvent?: TrackingEvent | ReadonlyArray<TrackingEvent>;
} & ExclusifyUnion<
| {href: string | undefined; newTab?: boolean}
| {to: string | undefined; fullPageOnWebView?: boolean}
| {onPress: PressHandler | undefined}
| {
href: string | undefined;
newTab?: boolean;
}
| {
to: string | undefined;
newTab?: boolean;
/** @deprecated use newTab */
fullPageOnWebView?: boolean;
}
| {
onPress: PressHandler | undefined;
}
>;
type TouchableCard<T> = T & TouchableProps;
type MaybeTouchableCard<T> = ExclusifyUnion<TouchableCard<T> | T>;
Expand Down
2 changes: 2 additions & 0 deletions src/icon-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ interface HrefProps {

interface ToProps {
to: string;
newTab?: boolean;
/** @deprecated use newTab */
fullPageOnWebView?: boolean;
replace?: boolean;
}
Expand Down
2 changes: 2 additions & 0 deletions src/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ interface HrefRowContentProps extends CommonProps {
interface ToRowContentProps extends CommonProps {
trackingEvent?: TrackingEvent | ReadonlyArray<TrackingEvent>;
to: string | undefined;
newTab?: boolean;
/** @deprecated use newTab */
fullPageOnWebView?: boolean;
replace?: boolean;
onNavigate?: () => void | Promise<void>;
Expand Down
5 changes: 5 additions & 0 deletions src/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ export const dimensions = {
headerDesktopHeight: NAVBAR_HEIGHT_DESKTOP,
};

/**
* https://reactrouter.com/en/main/components/link
* https://nextjs.org/docs/app/api-reference/components/link
*/
type LinkComponent = React.ComponentType<{
style?: React.CSSProperties;
className?: string;
Expand Down Expand Up @@ -315,6 +319,7 @@ type LinkComponent = React.ComponentType<{
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
onKeyDown?: React.KeyboardEventHandler<HTMLAnchorElement>;
children: React.ReactNode;
target?: string;
}>;

const AnchorLink: LinkComponent = ({to, innerRef, ...props}) => (
Expand Down
28 changes: 18 additions & 10 deletions src/touchable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type HrefProps = {

type ToProps = {
to: string | Location;
newTab?: boolean;
/** @deprecated set the newTab */
fullPageOnWebView?: boolean;
replace?: boolean;
onNavigate?: () => void | Promise<void>;
Expand Down Expand Up @@ -121,7 +123,7 @@ const RawTouchable = React.forwardRef<TouchableElement, TouchableProps>((props,

const type = props.type ? props.type : 'button';

const openNewTab = !!props.href && !!props.newTab;
const openNewTab = !!props.newTab;
const openInCurrentPage = props.href?.startsWith('#');
const loadOnTop = !openNewTab && !!props.href && !!props.loadOnTop;

Expand Down Expand Up @@ -203,6 +205,18 @@ const RawTouchable = React.forwardRef<TouchableElement, TouchableProps>((props,
}
};

const renderScreenReaderOnlyHint = () => {
return openNewTab ? (
<ScreenReaderOnly>
<span>{texts.linkOpensInNewTab}</span>
</ScreenReaderOnly>
) : openInCurrentPage ? (
<ScreenReaderOnly>
<span>{texts.linkOpensInCurrentPage}</span>
</ScreenReaderOnly>
) : null;
};

if (!!props.href || (props.to && props.fullPageOnWebView && isInsideNovumNativeApp(platformOverrides))) {
return (
<a
Expand All @@ -221,15 +235,7 @@ const RawTouchable = React.forwardRef<TouchableElement, TouchableProps>((props,
ref={ref as React.RefObject<HTMLAnchorElement>}
>
{children}
{openNewTab ? (
<ScreenReaderOnly>
<span>{texts.linkOpensInNewTab}</span>
</ScreenReaderOnly>
) : openInCurrentPage ? (
<ScreenReaderOnly>
<span>{texts.linkOpensInCurrentPage}</span>
</ScreenReaderOnly>
) : null}
{renderScreenReaderOnlyHint()}
</a>
);
}
Expand All @@ -238,6 +244,7 @@ const RawTouchable = React.forwardRef<TouchableElement, TouchableProps>((props,
return (
<Link
{...commonProps}
target={props.newTab ? '_blank' : undefined}
aria-label={props['aria-label']}
aria-labelledby={props['aria-labelledby']}
innerRef={ref as React.RefObject<HTMLAnchorElement>}
Expand All @@ -247,6 +254,7 @@ const RawTouchable = React.forwardRef<TouchableElement, TouchableProps>((props,
onKeyDown={handleKeyDown}
>
{children}
{renderScreenReaderOnlyHint()}
</Link>
);
}
Expand Down
Loading