diff --git a/app/forms/instance-create.tsx b/app/forms/instance-create.tsx index 55256846e..dcaa441fc 100644 --- a/app/forms/instance-create.tsx +++ b/app/forms/instance-create.tsx @@ -250,7 +250,9 @@ export default function CreateInstanceForm({ - {title} + + {title} + diff --git a/libs/api-mocks/msw/handlers.ts b/libs/api-mocks/msw/handlers.ts index f4f60842a..d2d2072bf 100644 --- a/libs/api-mocks/msw/handlers.ts +++ b/libs/api-mocks/msw/handlers.ts @@ -385,7 +385,7 @@ export const handlers = [ time_run_state_updated: new Date().toISOString(), } db.instances.push(newInstance) - return res(json(newInstance, 201)) + return res(json(newInstance, 201, 2000)) } ), diff --git a/libs/api-mocks/msw/util.ts b/libs/api-mocks/msw/util.ts index 591306853..b8c9a4b7b 100644 --- a/libs/api-mocks/msw/util.ts +++ b/libs/api-mocks/msw/util.ts @@ -7,8 +7,8 @@ import { compose, context } from 'msw' * * https://mswjs.io/docs/basics/response-transformer#custom-transformer */ -export const json = (body: B, status = 200): ResponseTransformer => - compose(context.status(status), context.json(body)) +export const json = (body: B, status = 200, delay = 0): ResponseTransformer => + compose(context.status(status), context.json(body), context.delay(delay)) export interface ResultsPage { items: I[] diff --git a/libs/ui/lib/button/Button.stories.tsx b/libs/ui/lib/button/Button.stories.tsx index beee4ff14..4107b5d1b 100644 --- a/libs/ui/lib/button/Button.stories.tsx +++ b/libs/ui/lib/button/Button.stories.tsx @@ -42,14 +42,25 @@ export const All = () => { {colors.map((color) => (
{variants.map((variant) => ( - + <> + + + ))}
))} diff --git a/libs/ui/lib/button/Button.tsx b/libs/ui/lib/button/Button.tsx index b8d536fdc..c5c630de7 100644 --- a/libs/ui/lib/button/Button.tsx +++ b/libs/ui/lib/button/Button.tsx @@ -1,6 +1,7 @@ import cn from 'classnames' import { forwardRef } from 'react' +import { Spinner } from '@oxide/ui' import { assertUnreachable } from '@oxide/util' import './button.css' @@ -69,7 +70,10 @@ type ButtonStyleProps = { color?: Color } -export type ButtonProps = React.ComponentPropsWithRef<'button'> & ButtonStyleProps +export type ButtonProps = React.ComponentPropsWithRef<'button'> & + ButtonStyleProps & { + loading?: boolean + } export const buttonStyle = ({ size = 'base', @@ -89,7 +93,7 @@ export const buttonStyle = ({ // Use `forwardRef` so the ref points to the DOM element (not the React Component) // so it can be focused using the DOM API (eg. this.buttonRef.current.focus()) export const Button = forwardRef( - ({ children, size, variant, color, className, ...rest }, ref) => { + ({ children, size, variant, color, className, loading, ...rest }, ref) => { return ( ) } diff --git a/libs/ui/lib/button/button.css b/libs/ui/lib/button/button.css index a9b90d495..cbaadeff0 100644 --- a/libs/ui/lib/button/button.css +++ b/libs/ui/lib/button/button.css @@ -4,19 +4,31 @@ .btn-primary { @apply text-accent bg-accent-secondary hover:bg-accent-secondary-hover disabled:text-accent-disabled disabled:bg-accent-secondary; } +.btn-primary:disabled > .spinner { + @apply text-accent; +} /* Using `-solid` since ghost version is used as default secondary button state */ .btn-secondary-solid { @apply text-secondary bg-secondary hover:bg-secondary-hover disabled:text-quaternary disabled:bg-secondary; } +.btn-secondary-solid:disabled > .spinner { + @apply text-secondary; +} .btn-destructive { @apply text-destructive bg-destructive-secondary hover:bg-destructive-secondary-hover disabled:text-destructive-disabled disabled:bg-destructive-secondary; } +.btn-destructive:disabled > .spinner { + @apply text-destructive; +} .btn-notice { @apply text-notice bg-notice-secondary hover:bg-notice-secondary-hover disabled:text-notice-disabled disabled:bg-notice-secondary; } +.btn-notice:disabled > .spinner { + @apply text-notice; +} /** * Ghost diff --git a/libs/ui/lib/spinner/Spinner.tsx b/libs/ui/lib/spinner/Spinner.tsx index a5ae39f68..c1fa20f41 100644 --- a/libs/ui/lib/spinner/Spinner.tsx +++ b/libs/ui/lib/spinner/Spinner.tsx @@ -1,4 +1,10 @@ -export const Spinner = () => { +import cn from 'classnames' + +interface SpinnerProps { + className?: string +} + +export const Spinner = ({ className }: SpinnerProps) => { return ( { fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="Spinner" - className="animate-spin text-accent fill-green-800" + className={cn('spinner animate-spin', className)} >