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 (