feat: tailwind version of Button component

still WIP, need to figure out icon spacing exactly
This commit is contained in:
Samuel Gunter
2024-02-06 23:56:35 -06:00
parent 13bc06cc6d
commit ae08ee02f4
7 changed files with 70 additions and 34 deletions

View File

@@ -18,6 +18,7 @@
}, },
"dependencies": { "dependencies": {
"@types/sql.js": "^1.4.9", "@types/sql.js": "^1.4.9",
"@unocss/reset": "^0.58.5",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"chrome-extension-toolkit": "^0.0.51", "chrome-extension-toolkit": "^0.0.51",
"classnames": "^2.5.1", "classnames": "^2.5.1",

7
pnpm-lock.yaml generated
View File

@@ -16,6 +16,9 @@ dependencies:
'@types/sql.js': '@types/sql.js':
specifier: ^1.4.9 specifier: ^1.4.9
version: 1.4.9 version: 1.4.9
'@unocss/reset':
specifier: ^0.58.5
version: 0.58.5
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: ^4.2.1 specifier: ^4.2.1
version: 4.2.1(vite@5.0.12) version: 4.2.1(vite@5.0.12)
@@ -5046,6 +5049,10 @@ packages:
resolution: {integrity: sha512-ZZTrAdl4WWmMjQdOqcOSWdgFH6kdFKZjPu4c6Ijxk7KvY2BW3nttTTBa7IYeuXFHVfcExUFqlOgRurt+NeWYyQ==} resolution: {integrity: sha512-ZZTrAdl4WWmMjQdOqcOSWdgFH6kdFKZjPu4c6Ijxk7KvY2BW3nttTTBa7IYeuXFHVfcExUFqlOgRurt+NeWYyQ==}
dev: true dev: true
/@unocss/reset@0.58.5:
resolution: {integrity: sha512-2wMrkCj3SSb5hrx9TKs5jZa34QIRkHv9FotbJutAPo7o8hx+XXn56ogzdoUrcFPJZJUx2R2nyOVbSlGMIjtFtw==}
dev: false
/@unocss/rule-utils@0.58.4: /@unocss/rule-utils@0.58.4:
resolution: {integrity: sha512-52Jp4I+joGTaDm7ehB/7uZ2kJL+9BZcYRDUVk4IDacDH5W9yxf1F75LzYT8jJVWXD/HIhiS0r9V6qhcBq2OWZw==} resolution: {integrity: sha512-52Jp4I+joGTaDm7ehB/7uZ2kJL+9BZcYRDUVk4IDacDH5W9yxf1F75LzYT8jJVWXD/HIhiS0r9V6qhcBq2OWZw==}
engines: {node: '>=14'} engines: {node: '>=14'}

View File

@@ -1,6 +1,7 @@
import { Button } from 'src/views/components/common/Button/Button'; import { Button } from 'src/views/components/common/Button/Button';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import React from 'react'; import React from 'react';
import ImagePlaceholderIcon from '~icons/material-symbols/image';
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = { const meta = {
@@ -39,15 +40,37 @@ export const Grid: Story = {
render: props => ( render: props => (
<div style={{ display: 'flex', flexDirection: 'column' }}> <div style={{ display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
<Button {...props} type='filled' /> <Button {...props} variant='filled' className='bg-ut-black' />
<Button {...props} type='outline' /> <Button {...props} variant='outline' className='text-ut-black' />
<Button {...props} type='single' /> <Button {...props} variant='single' className='text-ut-black' />
</div> </div>
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
<Button {...props} type='filled' disabled /> <Button {...props} variant='filled' className='bg-ut-black' useScss />
<Button {...props} type='outline' disabled /> <Button {...props} variant='outline' className='text-ut-black' useScss />
<Button {...props} type='single' disabled /> <Button {...props} variant='single' className='text-ut-black' useScss />
</div> </div>
<hr />
<div style={{ display: 'flex' }}>
<Button {...props} variant='filled' className='bg-ut-black' disabled />
<Button {...props} variant='outline' className='text-ut-black' disabled />
<Button {...props} variant='single' className='text-ut-black' disabled />
</div>
<div style={{ display: 'flex' }}>
<Button {...props} variant='filled' className='bg-ut-black' disabled useScss />
<Button {...props} variant='outline' className='text-ut-black' disabled useScss />
<Button {...props} variant='single' className='text-ut-black' disabled useScss />
</div>
</div>
),
};
export const Icons: Story = {
render: props => (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Button {...props} variant='filled' icon={ImagePlaceholderIcon} />
<Button {...props} variant='outline' icon={ImagePlaceholderIcon} />
<Button {...props} variant='single' icon={ImagePlaceholderIcon} />
</div> </div>
), ),
}; };
@@ -60,14 +83,14 @@ export const CourseButtons: Story = {
render: props => ( render: props => (
<div style={{ display: 'flex', flexDirection: 'column' }}> <div style={{ display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
<Button {...props} type='filled' /> <Button {...props} variant='filled' />
<Button {...props} type='outline' /> <Button {...props} variant='outline' />
<Button {...props} type='single' /> <Button {...props} variant='single' />
</div> </div>
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
<Button {...props} type='filled' disabled /> <Button {...props} variant='filled' disabled />
<Button {...props} type='outline' disabled /> <Button {...props} variant='outline' disabled />
<Button {...props} type='single' disabled /> <Button {...props} variant='single' disabled />
</div> </div>
</div> </div>
), ),

View File

@@ -5,18 +5,18 @@ import styles from './Button.module.scss';
interface Props { interface Props {
className?: string; className?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
type?: 'filled' | 'outline' | 'single'; variant?: 'filled' | 'outline' | 'single';
onClick?: () => void; onClick?: () => void;
iconOnly?: boolean; icon?: React.ReactNode;
showSymbol?: boolean;
symbol?: React.ReactNode;
disabled?: boolean; disabled?: boolean;
title?: string; title?: string;
testId?: string; testId?: string;
primaryColor?: string; useScss?: boolean;
secondaryColor?: string;
} }
const BUTTON_BASE_CLASS =
'm-2.5 h-10 w-auto flex cursor-pointer content-center items-center gap-2 rounded-1 px-4 py-0 text-4.5 font-500 leading-normal font-sans btn-transition';
/** /**
* A reusable button component that follows the design system of the extension. * A reusable button component that follows the design system of the extension.
* @returns * @returns
@@ -24,35 +24,39 @@ interface Props {
export function Button({ export function Button({
className, className,
style, style,
type, variant,
onClick, onClick,
iconOnly, icon,
showSymbol,
symbol,
disabled, disabled,
title, title,
testId, testId,
primaryColor,
secondaryColor,
children, children,
useScss = false,
}: React.PropsWithChildren<Props>): JSX.Element { }: React.PropsWithChildren<Props>): JSX.Element {
return ( return (
<button <button
style={ style={
{ {
...style, ...style,
'--color-primary': primaryColor ?? '#333F48', '--color-primary': '#333F48',
'--color-secondary': secondaryColor ?? '#FFFFFF', '--color-secondary': '#FFFFFF',
} as React.CSSProperties } as React.CSSProperties
} }
data-testid={testId} data-testid={testId}
className={classNames(styles.button, className, styles[type ?? 'filled'], { className={classNames(useScss ? styles.button : BUTTON_BASE_CLASS, className, {
[styles.disabled]: disabled, [styles[variant]]: useScss,
[styles.disabled]: disabled && useScss,
'disabled:(cursor-not-allowed opacity-50)': disabled && !useScss,
'color-white border-none': variant === 'filled' && !useScss,
'border-current border-solid border-1 bg-white': variant === 'outline' && !useScss,
'bg-white border-none': variant === 'single' && !useScss,
})} })}
title={title} title={title}
disabled={disabled}
onClick={disabled ? undefined : onClick} onClick={disabled ? undefined : onClick}
> >
{!iconOnly && children} {icon}
{children}
</button> </button>
); );
} }

View File

@@ -2,6 +2,7 @@ import React from 'react';
import styles from './ExtensionRoot.module.scss'; import styles from './ExtensionRoot.module.scss';
import 'uno.css'; import 'uno.css';
import '@unocss/reset/tailwind-compat.css';
interface Props { interface Props {
testId?: string; testId?: string;

View File

@@ -83,7 +83,7 @@ export default function CourseButtons({ course, activeSchedule }: Props) {
<Button <Button
onClick={openRateMyProfessorURL} onClick={openRateMyProfessorURL}
disabled={!course.instructors.length} disabled={!course.instructors.length}
type='primary' variant='primary'
className={styles.button} className={styles.button}
title='Search for this professor on RateMyProfessor' title='Search for this professor on RateMyProfessor'
> >
@@ -94,7 +94,7 @@ export default function CourseButtons({ course, activeSchedule }: Props) {
</Button> </Button>
<Button <Button
onClick={openSyllabiURL} onClick={openSyllabiURL}
type='secondary' variant='secondary'
className={styles.button} className={styles.button}
title='Search for syllabi for this course' title='Search for syllabi for this course'
> >
@@ -105,7 +105,7 @@ export default function CourseButtons({ course, activeSchedule }: Props) {
</Button> </Button>
<Button <Button
onClick={openTextbookURL} onClick={openTextbookURL}
type='tertiary' variant='tertiary'
className={styles.button} className={styles.button}
title='Search for textbooks for this course' title='Search for textbooks for this course'
> >
@@ -118,7 +118,7 @@ export default function CourseButtons({ course, activeSchedule }: Props) {
disabled={!activeSchedule} disabled={!activeSchedule}
onClick={isCourseSaved ? handleRemoveCourse : handleSaveCourse} onClick={isCourseSaved ? handleRemoveCourse : handleSaveCourse}
title={isCourseSaved ? 'Remove this course from your schedule' : 'Add this course to your schedule'} title={isCourseSaved ? 'Remove this course from your schedule' : 'Add this course to your schedule'}
type={isCourseSaved ? 'danger' : 'success'} variant={isCourseSaved ? 'danger' : 'success'}
className={styles.button} className={styles.button}
> >
<Text size='medium' weight='regular' color='white'> <Text size='medium' weight='regular' color='white'>

View File

@@ -84,7 +84,7 @@ export default function TableRow({ row, isSelected, activeSchedule, onClick }: P
return ReactDOM.createPortal( return ReactDOM.createPortal(
<> <>
<Button className={styles.rowButton} onClick={onClick} type='secondary'> <Button className={styles.rowButton} onClick={onClick} variant='secondary'>
<Icon name='bar_chart' color='white' size='medium' /> <Icon name='bar_chart' color='white' size='medium' />
</Button> </Button>
{conflicts.length > 0 && ( {conflicts.length > 0 && (