refactor: update text and link components to be polymorphic (#171)

This commit is contained in:
Razboy20
2024-03-15 22:06:36 -05:00
committed by GitHub
parent 61c43962fb
commit d04818ccd8
5 changed files with 55 additions and 38 deletions

View File

@@ -15,12 +15,16 @@ interface LinkItem {
const links: LinkItem[] = [ const links: LinkItem[] = [
{ {
text: "Summer '24 Course Schedule", text: "Fall '24 Course Schedule",
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/', url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20249/',
}, },
{ {
text: "Fall '24 Course Schedule", text: "Summer '24 Course Schedule",
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20236/', url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20246/',
},
{
text: "Spring '24 Course Schedule",
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/',
}, },
{ {
text: 'Registration Info Sheet', text: 'Registration Info Sheet',

View File

@@ -7,31 +7,28 @@ import React from 'react';
import styles from './Link.module.scss'; import styles from './Link.module.scss';
type Props = Omit<TextProps, 'span'> & { type Props = TextProps<'a'> & {
url?: string; href?: string;
disabled?: boolean; disabled?: boolean;
title?: string;
}; };
/** /**
* A reusable Text component with props that build on top of the design system for the extension * A reusable Text component with props that build on top of the design system for the extension
*/ */
export default function Link(props: PropsWithChildren<Props>): JSX.Element { export default function Link(props: PropsWithChildren<Props>): JSX.Element {
let passedProps = { let { className, href, ...passedProps } = props;
...props,
};
const { url } = props;
if (url && !props.onClick) { if (href && !props.onClick) {
passedProps.onClick = () => background.openNewTab({ url }); passedProps.onClick = () => background.openNewTab({ url: href });
} }
const isDisabled = props.disabled || (!url && !props.onClick); const isDisabled = props.disabled || (!href && !props.onClick);
return ( return (
<Text <Text
color='bluebonnet' color='bluebonnet'
{...passedProps} {...passedProps}
as='span' as='a'
tabIndex={isDisabled ? -1 : 0}
className={clsx( className={clsx(
styles.link, styles.link,
{ {

View File

@@ -1,34 +1,49 @@
import type { PropsOf, ReactTag } from '@headlessui/react/dist/types';
import clsx from 'clsx'; import clsx from 'clsx';
import type { PropsWithChildren } from 'react'; import type { ElementType, ReactNode } from 'react';
import React from 'react'; import React from 'react';
import styles from './Text.module.scss'; import styles from './Text.module.scss';
/** type PropsWeControl = 'as' | 'children';
* Props for the Text component. type CleanProps<TTag extends ReactTag, TOmitableProps extends PropertyKey = never> = Omit<
*/ PropsOf<TTag>,
export type TextProps = { TOmitableProps | PropsWeControl
variant?: Variant; >;
} & ( type OurProps<TTag extends ReactTag> = {
| (React.HTMLAttributes<HTMLSpanElement> & { as?: 'span' }) as?: TTag;
| (React.HTMLAttributes<HTMLDivElement> & { as: 'div' }) children?: ReactNode;
); };
type AsProps<TTag extends ReactTag, TOverrides = {}> = CleanProps<TTag, keyof TOverrides> & OurProps<TTag> & TOverrides;
const variants = ['mini', 'small', 'p', 'h4', 'h3-course', 'h3', 'h2-course', 'h2', 'h1-course', 'h1'] as const; const variants = ['mini', 'small', 'p', 'h4', 'h3-course', 'h3', 'h2-course', 'h2', 'h1-course', 'h1'] as const;
type Variant = (typeof variants)[number]; type Variant = (typeof variants)[number];
/**
* Props for the Text component.
*/
export type TextProps<TTag extends ElementType = 'span'> = PropsOf<TTag>['className'] extends string
? AsProps<
TTag,
{
variant?: Variant;
}
>
: never;
/** /**
* A reusable Text component with props that build on top of the design system for the extension * A reusable Text component with props that build on top of the design system for the extension
*/ */
export default function Text({ variant, as, className, ...props }: PropsWithChildren<TextProps>): JSX.Element { export default function Text<TTag extends ElementType = 'span'>({
const mergedClassName = clsx(styles.text, styles[variant], className); as,
className,
variant,
...rest
}: TextProps<TTag>): JSX.Element {
const Comp = as || 'span';
const mergedClassName = clsx(styles.text, styles[variant || 'p'], className);
if (as === 'div') return <div className={mergedClassName} {...props} />; return <Comp className={mergedClassName} {...rest} />;
return (
<span className={mergedClassName} {...props}>
{props.children}
</span>
);
} }

View File

@@ -37,11 +37,11 @@ export default function RecruitmentBanner(): JSX.Element {
<div className={styles.container}> <div className={styles.container}>
<Text color='white'> <Text color='white'>
Interested in helping us develop UT Registration Plus? Check out our{' '} Interested in helping us develop UT Registration Plus? Check out our{' '}
<Link color='white' url={DISCORD_URL}> <Link color='white' href={DISCORD_URL}>
Discord Server Discord Server
</Link>{' '} </Link>{' '}
and{' '} and{' '}
<Link color='white' url={GITHUB_URL}> <Link color='white' href={GITHUB_URL}>
GitHub GitHub
</Link> </Link>
! !

View File

@@ -10,7 +10,7 @@ export default defineConfig({
rules: [ rules: [
[ [
'btn-transition', 'btn-transition',
{ transition: 'color 180ms, border-color 150ms, background-color 150ms, box-shadow 100ms, transform 50ms' }, { transition: 'color 180ms, border-color 150ms, background-color 150ms, box-shadow 0ms, transform 50ms' },
], ],
[ [
'ring-offset-0', 'ring-offset-0',
@@ -21,7 +21,8 @@ export default defineConfig({
], ],
shortcuts: { shortcuts: {
focusable: 'outline-none ring-blue-500/50 dark:ring-blue-400/60 ring-0 focus-visible:ring-4', focusable: 'outline-none ring-blue-500/50 dark:ring-blue-400/60 ring-0 focus-visible:ring-4',
btn: 'h-10 w-auto flex cursor-pointer justify-center items-center gap-2 rounded-1 px-4 py-0 text-4.5 btn-transition btn-transition disabled:(cursor-not-allowed opacity-50) active:enabled:scale-96 focusable', btn: 'h-10 w-auto flex cursor-pointer justify-center items-center gap-2 rounded-1 px-4 py-0 text-4.5 btn-transition disabled:(cursor-not-allowed opacity-50) active:enabled:scale-96 focusable',
link: 'text-ut-burntorange underline underline-offset-2 hover:text-ut-orange focus-visible:text-ut-orange focusable btn-transition ease-out-expo',
}, },
theme: { theme: {
easing: { easing: {