Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 7 additions & 2 deletions packages/shared/src/components/FeedItemComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { FeedItemType } from './cards/common/common';
import { AdGrid } from './cards/ad/AdGrid';
import { AdList } from './cards/ad/AdList';
import { SignalAdList } from './cards/ad/SignalAdList';
import type { AdCardProps } from './cards/ad/common/common';
import { AcquisitionFormGrid } from './cards/AcquisitionForm/AcquisitionFormGrid';
import { AcquisitionFormList } from './cards/AcquisitionForm/AcquisitionFormList';
import { FreeformGrid } from './cards/Freeform/FreeformGrid';
Expand Down Expand Up @@ -394,9 +395,12 @@ function FeedItemComponent({
}

switch (item.type) {
case FeedItemType.Ad:
case FeedItemType.Ad: {
const AdComponent = AdTag as React.ForwardRefExoticComponent<
AdCardProps & React.RefAttributes<Element>
>;
return (
<AdTag
<AdComponent
ref={inViewRef}
ad={item.ad}
index={item.index}
Expand All @@ -409,6 +413,7 @@ function FeedItemComponent({
}
/>
);
}
case FeedItemType.UserAcquisition:
return <AcquisitionFormTag key="user-acquisition-card" />;
case FeedItemType.MarketingCta:
Expand Down
4 changes: 3 additions & 1 deletion packages/shared/src/components/LoginButton.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ describe('LoginButton', () => {
logEvent.mockReset();
});

const renderLayout = (user: LoggedUser = null): RenderResult => {
const renderLayout = (
user: LoggedUser = null as unknown as LoggedUser,
): RenderResult => {
const client = new QueryClient();

return render(
Expand Down
87 changes: 42 additions & 45 deletions packages/shared/src/components/ProfileMenu/ProfileSectionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
TypographyTag,
TypographyType,
} from '../typography/Typography';
import ConditionalWrapper from '../ConditionalWrapper';
import { combinedClicks } from '../../lib/click';
import { ArrowIcon, OpenLinkIcon } from '../icons';
import { anchorDefaultRel } from '../../lib/strings';
Expand Down Expand Up @@ -55,56 +54,54 @@ export const ProfileSectionItem = ({
typography,
}: ProfileSectionItemProps): ReactElement => {
const isMobile = useViewSize(ViewSize.MobileL);

const tag = href ? TypographyTag.Link : TypographyTag.Button;

const showLinkIcon = href && external;
const openNewTab = showLinkIcon && !href.startsWith(webappUrl);

return (
<ConditionalWrapper
condition={!!href}
wrapper={(children) => (
<Link href={href} passHref>
{children}
</Link>
const content = (
<Typography<typeof tag>
tag={tag}
color={typography?.color ?? TypographyColor.Tertiary}
type={typography?.type ?? TypographyType.Subhead}
className={classNames(
'flex h-10 cursor-pointer items-center gap-2 rounded-10 px-1 tablet:h-8',
(href || onClick) && 'hover:bg-surface-float',
isActive ? 'bg-surface-active' : undefined,
className,
)}
{...combinedClicks(() => onClick?.())}
{...(openNewTab && { target: '_blank', rel: anchorDefaultRel })}
>
<Typography<typeof tag>
tag={tag}
color={typography?.color ?? TypographyColor.Tertiary}
type={typography?.type ?? TypographyType.Subhead}
className={classNames(
'flex h-10 cursor-pointer items-center gap-2 rounded-10 px-1 tablet:h-8',
(href || onClick) && 'hover:bg-surface-float',
isActive ? 'bg-surface-active' : undefined,
className,
)}
{...combinedClicks(() => onClick?.())}
{...(openNewTab && { target: '_blank', rel: anchorDefaultRel })}
>
{Icon && (
<Icon
secondary={isActive}
size={isMobile ? IconSize.Small : IconSize.XSmall}
/>
)}
<span>{title}</span>
{Icon && (
<Icon
secondary={isActive}
size={isMobile ? IconSize.Small : IconSize.XSmall}
/>
)}
<span>{title}</span>

{!isMobile && showLinkIcon && (
<OpenLinkIcon
className="ml-auto text-text-quaternary"
size={IconSize.Size16}
/>
)}
{!isMobile && showLinkIcon && (
<OpenLinkIcon
className="ml-auto text-text-quaternary"
size={IconSize.Size16}
/>
)}

{isMobile && !external && (
<ArrowIcon
className="ml-auto rotate-90 text-text-quaternary"
size={IconSize.Size16}
/>
)}
</Typography>
</ConditionalWrapper>
{isMobile && !external && (
<ArrowIcon
className="ml-auto rotate-90 text-text-quaternary"
size={IconSize.Size16}
/>
)}
</Typography>
);

if (!href) {
return content;
}

return (
<Link href={href} passHref>
{content}
</Link>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function AuthenticationBanner({
<Section className="w-[23.25rem]">
<AuthOptions
ignoreMessages
formRef={null}
formRef={null as unknown as React.MutableRefObject<HTMLFormElement>}
trigger={AuthTriggers.Onboarding}
simplified
defaultDisplay={AuthDisplay.OnboardingSignup}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function CodeVerificationForm({
setEmailSent(true);
},
onVerifyCodeSuccess: () => {
onSubmit();
onSubmit?.();
},
},
);
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/auth/CustomAuthBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useViewSize, ViewSize } from '../../hooks';
import LoginButton from '../LoginButton';
import { authGradientBg } from '../banners';

const CustomAuthBanner = (): ReactElement => {
const CustomAuthBanner = (): ReactElement | null => {
const { shouldShowAuthBanner } = useOnboardingActions();
const { shouldShowLogin } = useAuthContext();
const isLaptop = useViewSize(ViewSize.Laptop);
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/auth/EmailSignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function EmailSignupForm({
isReady,
showDisclaimer = true,
}: EmailSignupFormProps): ReactElement {
const [email, setEmail] = useState(null);
const [email, setEmail] = useState<string | null>(null);
const inputId = useId();
const isSubmitDisabled = !email || !isReady;

Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/auth/SignBackButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function SignBackButton({
disabled = false,
onClick,
}: SignBackButtonProps): ReactElement {
const item = providerMap[provider];
const item = providerMap[provider.toLowerCase() as keyof typeof providerMap];

return (
<button
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/components/auth/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ export interface AuthFormProps {
export const getFormEmail = (e: React.FormEvent): string => {
const form = e.currentTarget as HTMLFormElement;
const input = Array.from(form.elements).find((el) =>
['email', 'traits.email'].includes(el.getAttribute('name')),
['email', 'traits.email'].includes(el.getAttribute('name') ?? ''),
) as HTMLInputElement;

return input?.value?.trim();
return input?.value?.trim() ?? '';
};

export enum AuthDisplay {
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/buttons/Button.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { UpvoteIcon } from '../icons';
const renderComponent = <Tag extends AllowedTags>(
props: Partial<ButtonProps<Tag>> = {},
): RenderResult => {
return render(<Button {...props} />);
return render(<Button {...(props as ButtonProps<'button'>)} />);
};

describe('Button', () => {
Expand Down
8 changes: 7 additions & 1 deletion packages/shared/src/components/buttons/QuaternaryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ function QuaternaryButtonComponent<TagName extends AllowedTags>(
ref?: Ref<ButtonElementType<TagName>>,
): ReactElement {
const anchorRef = useRef<ButtonElementType<TagName>>(null);
useImperativeHandle(ref, () => anchorRef?.current);
useImperativeHandle(ref, () => {
if (!anchorRef.current) {
throw new Error('QuaternaryButton ref is not attached');
}

return anchorRef.current;
});
const [isHovered, setIsHovered] = useState(false);
const onLabelClick = (event: React.MouseEvent<HTMLLabelElement>): void => {
event.preventDefault();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ReactElement, ReactNode } from 'react';
import React from 'react';
import { largeNumberFormat } from '../../../lib';
import ConditionalWrapper from '../../ConditionalWrapper';
import Link from '../../utilities/Link';
import { Tooltip } from '../../tooltip/Tooltip';

Expand All @@ -26,26 +25,28 @@ export function LeaderboardListItem({
const shouldShowTooltip =
concatScore && typeof index === 'number' && index >= 1000;
const actualNumber = index.toLocaleString();
const content = (
<>
<Tooltip content={actualNumber} visible={shouldShowTooltip}>
<span className="inline-flex w-14 shrink-0 justify-center tabular-nums text-text-quaternary">
{formattedNumber}
</span>
</Tooltip>
{children}
</>
);

return (
<li className={className} onMouseEnter={onMouseEnter}>
<ConditionalWrapper
condition={!!href}
wrapper={(child) => (
<Link href={href} passHref key={href} prefetch={false}>
<a className="flex w-full flex-row items-center rounded-8 px-2 hover:bg-accent-pepper-subtler">
{child}
</a>
</Link>
)}
>
<Tooltip content={actualNumber} visible={shouldShowTooltip}>
<span className="inline-flex w-14 shrink-0 justify-center tabular-nums text-text-quaternary">
{formattedNumber}
</span>
</Tooltip>
{children}
</ConditionalWrapper>
{href ? (
<Link href={href} prefetch={false}>
<a className="flex w-full flex-row items-center rounded-8 px-2 hover:bg-accent-pepper-subtler">
{content}
</a>
</Link>
) : (
content
)}
</li>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ import { apiUrl } from '../../../../lib/config';
import { adFaviconPlaceholder } from '../../../../lib/image';
import { getAdFaviconImageLink } from './getAdFaviconImageLink';

const originalWindowDescriptor = Object.getOwnPropertyDescriptor(
globalThis,
'window',
);

describe('getAdFaviconImageLink', () => {
afterEach(() => {
delete (globalThis as typeof globalThis & { window?: Window }).window;
if (originalWindowDescriptor) {
Object.defineProperty(globalThis, 'window', originalWindowDescriptor);
return;
}

Reflect.deleteProperty(globalThis, 'window');
});

it('returns companyLogo first when available', () => {
Expand All @@ -29,6 +39,11 @@ describe('getAdFaviconImageLink', () => {
});

it('uses a large fallback favicon size during SSR when window is unavailable', () => {
Object.defineProperty(globalThis, 'window', {
configurable: true,
value: undefined,
});

expect(
getAdFaviconImageLink({
ad: { ...ad, adDomain: 'daily.dev' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ it('should call on share click on copy link button click', async () => {
it('should not display publication date createdAt is empty', async () => {
renderComponent({
...defaultProps,
post: { ...post, createdAt: null },
post: { ...post, createdAt: undefined },
});
const el = screen.queryByText('Jun 13, 2018');
expect(el).not.toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const BriefBannerWithContext = ({ style, ...props }: ComponentProps<'div'>) => {

const saveBannerState = useCallback(() => {
if (bannerLastSeenRef.current !== null) {
updateAlerts({ briefBannerLastSeen: bannerLastSeenRef.current });
updateAlerts?.({ briefBannerLastSeen: bannerLastSeenRef.current });
bannerLastSeenRef.current = null;
}
}, [updateAlerts]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ it('should call on share click on copy link button click', async () => {
it('should not display publication date createdAt is empty', async () => {
renderComponent({
...defaultProps,
post: { ...post, createdAt: null },
post: { ...post, createdAt: undefined },
});
const el = screen.queryByText('Jun 13, 2018');
expect(el).not.toBeInTheDocument();
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/cards/common/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const getPostClassNames = (
...postClassNames: string[]
): string =>
classNames(
{ [styles.read]: post.read, [styles.trending]: post.trending > 0 },
{ [styles.read]: post.read, [styles.trending]: (post.trending ?? 0) > 0 },
styles.post,
'group',
...postClassNames,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ export const ClickbaitShield = ({ post }: { post: Post }): ReactElement => {
type: LazyModal.ClickbaitShield,
});
} else {
if (!user) {
throw new Error(
'ClickbaitShield requires an authenticated user to edit feed settings',
);
}

router.push(
`${webappUrl}feeds/${user.id}/edit?dview=${FeedSettingsMenu.AI}`,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export const PostCardHeader = ({
content={getReadPostButtonText(post)}
className="mr-2"
variant={ButtonVariant.Primary}
href={articleLink}
href={articleLink ?? ''}
onClick={onReadArticleClick}
openNewTab={openNewTab}
icon={getReadPostButtonIcon(post)}
Expand Down
Loading
Loading