mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 21:59:16 +03:00
* chore: Refactor `NewAccessTokenButton` to use shadcn components This brings `NewAccessTokenButton` into conformance with the latest UI patterns of using shadcn based components, zod schemas, and react-hook-form for form submission. I based this refactor on the implementation of `CreateSecretAPIKeyDialog`, which already made use of the above. * add success and failure toasts * add smoke test for `NewAccessTokenButton`, fix vitest config This adds a minimal test for `NewAccessTokenButton` but more importantly fixes a critical bug in the vitest configuration for `studio`. Because `restoreMocks: true` was set in the config, this meant that the mock implementation for `window.matchMedia` defined in the `tests/vitestSetup.ts` file was being **reset** to an empty function before each test suite was run. While this didn't appear to be a problem before, that appears to be because none of the existing tests had `motion.div` in their component trees. Since `motion.div` calls `addListener` on a media query as part of it's lifecycle, that meant that any test which included it would fail as `addListener` would be undefined in this situation. Removing `restoreMocks: true` from the config results in the desired mocking behavior. This change is necessary in order to test any component that has the `FormItemLayout` component, as it uses `framer-motion` to animate in error messages for input elements. * add warning comment in case of future config regression * update test cases, reset form on dialog close, testing setup fixes This fixes an issue with the polyfills for the testing environment, where a call to `useMutation` would fail as a result of `TransformStream` being set to `null`. Basic tests for access token creation and form resetting added. Adds `@faker-js/faker` to the studio app devDependencies to generate mock data for `msw` endpoint response. Adds `shx` to the docs app devDependencies to ensure that the `codegen:examples` script runs cross-platform. * ensure mocked date is a string * update testing setup, pollyfills and add rich type support to addAPIMock * Update studio testing setup files Improves API mocking type safety and polyfills browser APIs necessary to run tests with framer-motion components * chore: Refactor `NewAccessTokenButton` to use shadcn components This brings `NewAccessTokenButton` into conformance with the latest UI patterns of using shadcn based components, zod schemas, and react-hook-form for form submission. I based this refactor on the implementation of `CreateSecretAPIKeyDialog`, which already made use of the above. * add success and failure toasts * add smoke test for `NewAccessTokenButton`, fix vitest config This adds a minimal test for `NewAccessTokenButton` but more importantly fixes a critical bug in the vitest configuration for `studio`. Because `restoreMocks: true` was set in the config, this meant that the mock implementation for `window.matchMedia` defined in the `tests/vitestSetup.ts` file was being **reset** to an empty function before each test suite was run. While this didn't appear to be a problem before, that appears to be because none of the existing tests had `motion.div` in their component trees. Since `motion.div` calls `addListener` on a media query as part of it's lifecycle, that meant that any test which included it would fail as `addListener` would be undefined in this situation. Removing `restoreMocks: true` from the config results in the desired mocking behavior. This change is necessary in order to test any component that has the `FormItemLayout` component, as it uses `framer-motion` to animate in error messages for input elements. * update test cases, reset form on dialog close, testing setup fixes This fixes an issue with the polyfills for the testing environment, where a call to `useMutation` would fail as a result of `TransformStream` being set to `null`. Basic tests for access token creation and form resetting added. Adds `@faker-js/faker` to the studio app devDependencies to generate mock data for `msw` endpoint response. Adds `shx` to the docs app devDependencies to ensure that the `codegen:examples` script runs cross-platform. * ensure mocked date is a string * update testing setup, pollyfills and add rich type support to addAPIMock * fix imports * fix missing listen call for msw, resolve test type error * fix imports * Update studio testing setup files Improves API mocking type safety and polyfills browser APIs necessary to run tests with framer-motion components * fix missing listen call for msw, resolve test type error * fix imports * Shift test file * Smol fix * Nit --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
190 lines
5.8 KiB
TypeScript
190 lines
5.8 KiB
TypeScript
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { ChevronDown, ExternalLink } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { useState } from 'react'
|
|
import { type SubmitHandler, useForm } from 'react-hook-form'
|
|
import { toast } from 'sonner'
|
|
import { z } from 'zod'
|
|
|
|
import { useAccessTokenCreateMutation } from 'data/access-tokens/access-tokens-create-mutation'
|
|
import {
|
|
Button,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogSection,
|
|
DialogSectionSeparator,
|
|
DialogTitle,
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
Form_Shadcn_,
|
|
FormControl_Shadcn_,
|
|
FormField_Shadcn_,
|
|
Input_Shadcn_,
|
|
} from 'ui'
|
|
import { Admonition } from 'ui-patterns'
|
|
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
|
|
|
export interface NewAccessTokenButtonProps {
|
|
onCreateToken: (token: any) => void
|
|
}
|
|
|
|
const TokenSchema = z.object({
|
|
tokenName: z.string().min(1, 'Please enter a name for the token'),
|
|
})
|
|
|
|
const formId = 'new-access-token-form'
|
|
|
|
const NewAccessTokenButton = ({ onCreateToken }: NewAccessTokenButtonProps) => {
|
|
const [visible, setVisible] = useState(false)
|
|
const [tokenScope, setTokenScope] = useState<'V0' | undefined>(undefined)
|
|
|
|
const form = useForm<z.infer<typeof TokenSchema>>({
|
|
resolver: zodResolver(TokenSchema),
|
|
defaultValues: { tokenName: '' },
|
|
mode: 'onSubmit',
|
|
})
|
|
const { mutate: createAccessToken, isLoading } = useAccessTokenCreateMutation()
|
|
|
|
const onSubmit: SubmitHandler<z.infer<typeof TokenSchema>> = async (values) => {
|
|
createAccessToken(
|
|
{ name: values.tokenName, scope: tokenScope },
|
|
{
|
|
onSuccess: (data) => {
|
|
toast.success(`Your access token "${data.name}" is ready.`)
|
|
form.reset()
|
|
onCreateToken(data)
|
|
setVisible(false)
|
|
},
|
|
}
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="flex items-center">
|
|
<Button
|
|
className="rounded-r-none px-3"
|
|
onClick={() => {
|
|
setTokenScope(undefined)
|
|
setVisible(true)
|
|
}}
|
|
>
|
|
Generate new token
|
|
</Button>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
type="primary"
|
|
title="Choose token scope"
|
|
className="rounded-l-none px-[4px] py-[5px]"
|
|
icon={<ChevronDown />}
|
|
/>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" side="bottom">
|
|
<DropdownMenuItem
|
|
key="experimental-token"
|
|
onClick={() => {
|
|
setTokenScope('V0')
|
|
setVisible(true)
|
|
}}
|
|
>
|
|
<div className="space-y-1">
|
|
<p className="block text-foreground">Generate token for experimental API</p>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
|
|
<Dialog
|
|
open={visible}
|
|
onOpenChange={(open) => {
|
|
if (!open) form.reset()
|
|
setVisible(open)
|
|
}}
|
|
>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{tokenScope === 'V0' ? 'Generate token for experimental API' : 'Generate New Token'}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
<DialogSectionSeparator />
|
|
<DialogSection className="flex flex-col gap-4">
|
|
{tokenScope === 'V0' && (
|
|
<Admonition
|
|
type="warning"
|
|
title="The experimental API provides additional endpoints which allows you to manage your organizations and projects."
|
|
description={
|
|
<>
|
|
<p>
|
|
These include deleting organizations and projects which cannot be undone. As
|
|
such, be very careful when using this API.
|
|
</p>
|
|
<div className="mt-4">
|
|
<Button asChild type="default" icon={<ExternalLink />}>
|
|
<Link
|
|
href="https://api.supabase.com/api/v0"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
>
|
|
Experimental API documentation
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</>
|
|
}
|
|
/>
|
|
)}
|
|
<Form_Shadcn_ {...form}>
|
|
<form
|
|
id={formId}
|
|
className="flex flex-col gap-4"
|
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
>
|
|
<FormField_Shadcn_
|
|
key="tokenName"
|
|
name="tokenName"
|
|
control={form.control}
|
|
render={({ field }) => (
|
|
<FormItemLayout name="tokenName" label="Name">
|
|
<FormControl_Shadcn_>
|
|
<Input_Shadcn_
|
|
id="tokenName"
|
|
{...field}
|
|
placeholder="Provide a name for your token"
|
|
/>
|
|
</FormControl_Shadcn_>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
</form>
|
|
</Form_Shadcn_>
|
|
</DialogSection>
|
|
<DialogFooter>
|
|
<Button
|
|
type="default"
|
|
disabled={isLoading}
|
|
onClick={() => {
|
|
form.reset()
|
|
setVisible(false)
|
|
}}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button form={formId} htmlType="submit" loading={isLoading}>
|
|
Generate token
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default NewAccessTokenButton
|