Skip to content

Commit

Permalink
Switch Button and TextField to shadcn (#891)
Browse files Browse the repository at this point in the history
* Switch `Button` and `TextField` to shadcn

* Remove MUI import

* Make shadcn documentation links more specific

* Handle missing label

* Use `cn` for conditional class names

* Also use `cn` for `Button`

* Don't use 'papi-textfield'
  • Loading branch information
dewert99 authored May 17, 2024
1 parent 47d67ca commit b96ee0f
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 44 deletions.
2 changes: 2 additions & 0 deletions lib/platform-bible-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"@emotion/styled": ">=11.11.0",
"@mui/material": ">=5.15.10",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
"autoprefixer": "^10.4.19",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
Expand Down
13 changes: 7 additions & 6 deletions lib/platform-bible-react/src/components/button.component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button as MuiButton } from '@mui/material';
import { Button as ShadButton } from '@/components/shadcn-ui/button';
import { cn } from '@/utils/shadcn-ui.util';
import { MouseEventHandler, PropsWithChildren } from 'react';
import '@/components/button.component.css';

Expand All @@ -22,8 +23,8 @@ export type ButtonProps = PropsWithChildren<{
/**
* Button a user can click to do something
*
* Thanks to MUI for heavy inspiration and documentation
* https://mui.com/material-ui/getting-started/overview/
* Thanks to Shadcn for heavy inspiration and documentation
* https://ui.shadcn.com/docs/components/button
*/
function Button({
id,
Expand All @@ -34,15 +35,15 @@ function Button({
children,
}: ButtonProps) {
return (
<MuiButton
<ShadButton
id={id}
disabled={isDisabled}
className={`papi-button ${className ?? ''}`}
className={cn('papi-button', className)}
onClick={onClick}
onContextMenu={onContextMenu}
>
{children}
</MuiButton>
</ShadButton>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function RefSelector({ scrRef, handleSubmit, id }: ScrRefSelectorProps) {
const currentBookName = useMemo(() => getBookNameOptions()[scrRef.bookNum - 1], [scrRef.bookNum]);

return (
<span id={id}>
<span id={id} className="pr-flex pr-place-items-center">
<ComboBox
title="Book"
className="papi-ref-selector book"
Expand Down
56 changes: 56 additions & 0 deletions lib/platform-bible-react/src/components/shadcn-ui/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/utils/shadcn-ui.util"

const buttonVariants = cva(
"pr-inline-flex pr-items-center pr-justify-center pr-whitespace-nowrap pr-rounded-md pr-text-sm pr-font-medium pr-ring-offset-background pr-transition-colors focus-visible:pr-outline-none focus-visible:pr-ring-2 focus-visible:pr-ring-ring focus-visible:pr-ring-offset-2 disabled:pr-pointer-events-none disabled:pr-opacity-50",
{
variants: {
variant: {
default: "pr-bg-primary pr-text-primary-foreground hover:pr-bg-primary/90",
destructive:
"pr-bg-destructive pr-text-destructive-foreground hover:pr-bg-destructive/90",
outline:
"pr-border pr-border-input pr-bg-background hover:pr-bg-accent hover:pr-text-accent-foreground",
secondary:
"pr-bg-secondary pr-text-secondary-foreground hover:pr-bg-secondary/80",
ghost: "hover:pr-bg-accent hover:pr-text-accent-foreground",
link: "pr-text-primary pr-underline-offset-4 hover:pr-underline",
},
size: {
default: "pr-h-10 pr-px-4 pr-py-2",
sm: "pr-h-9 pr-rounded-md pr-px-3",
lg: "pr-h-11 pr-rounded-md pr-px-8",
icon: "pr-h-10 pr-w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"

export { Button, buttonVariants }
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
'pr-flex pr-h-10 pr-w-full pr-rounded-md pr-bg-background pr-px-3 pr-py-2 pr-text-sm pr-ring-offset-background file:pr-border-0 file:pr-bg-transparent file:pr-text-sm file:pr-font-medium placeholder:pr-text-muted-foreground disabled:pr-cursor-not-allowed disabled:pr-opacity-50',
'pr-flex pr-h-10 pr-rounded-md pr-bg-background pr-px-3 pr-py-2 pr-text-sm pr-ring-offset-background file:pr-border-0 file:pr-bg-transparent file:pr-text-sm file:pr-font-medium placeholder:pr-text-muted-foreground disabled:pr-cursor-not-allowed disabled:pr-opacity-50',
className,
)}
ref={ref}
Expand Down
24 changes: 24 additions & 0 deletions lib/platform-bible-react/src/components/shadcn-ui/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/utils/shadcn-ui.util"

const labelVariants = cva(
"pr-text-sm pr-font-medium pr-leading-none peer-disabled:pr-cursor-not-allowed peer-disabled:pr-opacity-70"
)

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName

export { Label }
65 changes: 29 additions & 36 deletions lib/platform-bible-react/src/components/text-field.component.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { TextField as MuiTextField } from '@mui/material';
import { Input as ShadInput } from '@/components/shadcn-ui/input';
import { Label as ShadLabel } from '@/components/shadcn-ui/label';
import { ChangeEventHandler, FocusEventHandler } from 'react';
import { cn } from '@/utils/shadcn-ui.util';

export type TextFieldProps = {
/**
* The variant to use.
*
* @default 'outlined'
*/
variant?: 'outlined' | 'filled';
/** Optional unique identifier */
id?: string;
/**
Expand All @@ -22,12 +18,6 @@ export type TextFieldProps = {
* @default false
*/
hasError?: boolean;
/**
* If `true`, the input will take up the full width of its container.
*
* @default false
*/
isFullWidth?: boolean;
/** Text that gives the user instructions on what contents the TextField expects */
helperText?: string;
/** The title of the TextField */
Expand All @@ -43,9 +33,9 @@ export type TextFieldProps = {
/** Additional css classes to help with unique styling of the text field */
className?: string;
/** Starting value for the text field if it is not controlled */
defaultValue?: unknown;
defaultValue?: string | number;
/** Value of the text field if controlled */
value?: unknown;
value?: string | number;
/** Triggers when content of textfield is changed */
onChange?: ChangeEventHandler<HTMLInputElement>;
/** Triggers when textfield gets focus */
Expand All @@ -57,15 +47,13 @@ export type TextFieldProps = {
/**
* Text input field
*
* Thanks to MUI for heavy inspiration and documentation
* https://mui.com/material-ui/getting-started/overview/
* Thanks to Shadcn for heavy inspiration and documentation
* https://ui.shadcn.com/docs/components/input#with-label
*/
function TextField({
variant = 'outlined',
id,
isDisabled = false,
hasError = false,
isFullWidth = false,
helperText,
label,
placeholder,
Expand All @@ -78,23 +66,28 @@ function TextField({
onBlur,
}: TextFieldProps) {
return (
<MuiTextField
variant={variant}
id={id}
disabled={isDisabled}
error={hasError}
fullWidth={isFullWidth}
helperText={helperText}
label={label}
placeholder={placeholder}
required={isRequired}
className={`papi-textfield ${className ?? ''}`}
defaultValue={defaultValue}
value={value}
onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
/>
<div className="pr-inline-grid pr-items-center pr-gap-1.5">
<ShadLabel
htmlFor={id}
className={cn({
'pr-text-red-600': hasError,
'pr-hidden': !label,
})}
>{`${label}${isRequired ? '*' : ''}`}</ShadLabel>
<ShadInput
id={id}
disabled={isDisabled}
placeholder={placeholder}
required={isRequired}
className={cn(className, { 'pr-border-red-600': hasError })}
defaultValue={defaultValue}
value={value}
onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
/>
<p className={cn({ 'pr-hidden': !helperText })}>{helperText}</p>
</div>
);
}

Expand Down
36 changes: 36 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b96ee0f

Please sign in to comment.