Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve code related to OAuth section #11954

Merged
merged 4 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 48 additions & 78 deletions docs/components/OAuthProviderInstructions/OAuthProviderSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,68 @@ import {
ComboboxPopover,
ComboboxProvider,
} from "@ariakit/react"
import { useOAuthProviderSelect } from "./useOAuthProviderSelect"
import dynamic from "next/dynamic"
import type { ChangeEvent } from "react"

import { Link } from "@/components/Link"
import manifest from "@/data/manifest.json"
import {
PreviewProviders,
type Provider,
} from "@/components/SearchBarProviders/PreviewProviders"
import { useSelectCombobox } from "@/hooks/use-select-combobox"

const OAuthProviderInstructions = dynamic(() =>
import("./content").then((mod) => mod.OAuthInstructions)
)

const previewProviders: Provider[] = [
{ id: "google", name: "Google" },
{ id: "github", name: "GitHub" },
{ id: "twitter", name: "Twitter" },
{ id: "keycloak", name: "Keycloak" },
{ id: "okta", name: "Okta" },
]

const items = Object.entries(manifest.providersOAuth).map(([id, name]) => ({
id,
name,
}))

export function OAuthProviderSelect() {
const { items, term, selected, handleSearchItem, handleSelectOption } =
useOAuthProviderSelect()
const {
selectedItem,
filteredItems,
hasMatchItem,
handleChange,
handleSelect,
} = useSelectCombobox({
items,
})

return (
<div className="mt-8">
<ComboboxProvider value={term} selectedValue={selected}>
<ComboboxProvider
value={selectedItem.name}
selectedValue={selectedItem.id}
>
<Combobox
placeholder="Search for your favorite OAuth provider"
className="w-full rounded-md border-2 border-gray-200 bg-neutral-100 px-4 py-2 font-medium text-neutral-800 shadow-sm md:w-96 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-300"
onChange={(e: ChangeEvent<HTMLInputElement>) =>
handleSearchItem(e.target.value)
}
value={selectedItem.name}
onChange={handleChange}
/>
<ComboboxPopover
gutter={4}
sameWidth
hideOnEscape
hideOnInteractOutside
className="z-50 mt-1 max-h-72 overflow-y-scroll rounded-md bg-neutral-100 p-2 dark:bg-neutral-900"
className="z-50 mt-1 max-h-72 overflow-y-scroll rounded-md bg-neutral-100 p-2 empty:hidden dark:bg-neutral-900"
>
{items.map((item) => (
{filteredItems.map((item) => (
<ComboboxItem
className="flex cursor-pointer flex-row items-center gap-4 px-2 py-2 aria-selected:bg-violet-200 dark:aria-selected:bg-violet-500 dark:aria-selected:text-neutral-900"
value={item.name}
key={item.name}
onClick={() => handleSelectOption(item)}
onClick={() => handleSelect(item)}
>
<img
src={`/img/providers/${item.id}.svg`}
Expand All @@ -51,85 +75,31 @@ export function OAuthProviderSelect() {
</ComboboxItem>
))}
</ComboboxPopover>
{!term ? (
{!selectedItem.name && (
<>
<p className="mt-8 rounded-md">
Or jump directly to one of the popular ones below.
</p>
<div className="mt-8 flex flex-row gap-6 overflow-x-scroll pb-8">
<div
role="button"
onClick={() =>
handleSelectOption({ id: "google", name: "Google" })
}
className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
>
<img src={`/img/providers/google.svg`} className="mt-2 w-11" />
<div className="text-center text-sm">Google</div>
</div>
<div
role="button"
className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
onClick={() =>
handleSelectOption({ id: "github", name: "GitHub" })
}
>
<img src={`/img/providers/github.svg`} className="mt-2 w-11" />
<div className="text-center text-sm">GitHub</div>
</div>
<div
role="button"
onClick={() =>
handleSelectOption({ id: "twitter", name: "Twitter" })
}
className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
>
<img src={`/img/providers/twitter.svg`} className="mt-2 w-11" />
<div className="text-center text-sm">Twitter</div>
</div>
<div
role="button"
onClick={() =>
handleSelectOption({ id: "keycloak", name: "keycloak" })
}
className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
>
<img
src={`/img/providers/keycloak.svg`}
className="mt-2 w-11"
/>
<div className="text-center text-sm">Keycloak</div>
</div>
<div
role="button"
onClick={() => handleSelectOption({ id: "okta", name: "okta" })}
className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
>
<img src={`/img/providers/okta.svg`} className="mt-2 w-11" />
<div className="text-center text-sm">Okta</div>
</div>
</div>
<PreviewProviders
className="mt-8 flex flex-row gap-6 overflow-x-scroll pb-8"
providers={previewProviders}
onSelected={handleSelect}
/>
</>
) : null}
{term && items.length === 0 ? (
)}
{!hasMatchItem && filteredItems.length === 0 && (
<p className="mt-6 rounded-md bg-violet-100 px-4 py-2 dark:bg-violet-300/50 dark:text-neutral-900">
Can't find the OAuth provider you're looking for? You can always{" "}
<Link href="/guides/configuring-oauth-providers#adding-a-new-built-in-provider">
build your own
</Link>
.
</p>
) : null}
)}
</ComboboxProvider>
{selected && term && items.length !== 0 ? (
<OAuthProviderInstructions
providerId={selected}
disabled={
term.toLowerCase() !==
manifest.providersOAuth[selected].toLowerCase()
}
/>
) : null}
{hasMatchItem && (
<OAuthProviderInstructions providerId={selectedItem.id} />
)}
</div>
)
}
12 changes: 5 additions & 7 deletions docs/components/OAuthProviderInstructions/content/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useEffect, useState } from "react"
import { type Highlighter, getHighlighter } from "shiki"
import { type Highlighter, createHighlighter } from "shiki"
import cx from "classnames"
import { Callout, Pre, Code as NXCode } from "nextra/components"

import { StepTitle } from "./components/StepTitle"
import { SetupCode } from "./components/SetupCode"
import { SignInCode } from "./components/SignInCode"
Expand All @@ -19,7 +18,7 @@ export function OAuthInstructions({ providerId, disabled = false }: Props) {
const [highlighter, setHighlighter] = useState<Highlighter | null>(null)
useEffect(() => {
;(async () => {
const hl = await getHighlighter({
const hl = await createHighlighter({
themes: ["github-light", "github-dark"],
langs: ["ts", "tsx", "bash"],
})
Expand Down Expand Up @@ -52,10 +51,9 @@ export function OAuthInstructions({ providerId, disabled = false }: Props) {

return (
<div
className={cx(
"nextra-steps mb-12 ml-4 border-l border-gray-200 pl-6 [counter-reset:step] dark:border-neutral-800",
{ "pointer-events-none opacity-40": disabled }
)}
className={cx("nextra-steps mb-12 ml-4 dark:border-neutral-800", {
"pointer-events-none opacity-40": disabled,
})}
>
{/* Step 1 */}
<StepTitle count={1}>
Expand Down

This file was deleted.

35 changes: 35 additions & 0 deletions docs/components/SearchBarProviders/PreviewProviders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export interface Provider {
id: string
name: string
}

export interface PreviewProvidersProps {
className?: string
providers: Provider[]
onSelected: (provider: Provider) => void
}

export function PreviewProviders({
className,
providers,
onSelected,
}: PreviewProvidersProps) {
return (
<section className={className}>
{providers.map((provider) => (
<div
className="flex h-32 w-32 min-w-24 flex-col items-center justify-between rounded-lg border border-solid border-neutral-200 p-4 shadow-sm transition-colors duration-300 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-950"
key={provider.id}
role="button"
onClick={() => onSelected(provider)}
>
<img
src={`/img/providers/${provider.id}.svg`}
className="mt-2 w-11"
/>
<div className="text-center text-sm">{provider.name}</div>
</div>
))}
</section>
)
}
48 changes: 48 additions & 0 deletions docs/hooks/use-select-combobox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ChangeEvent, useState } from "react"

interface SelectComboboxValue {
id: string
name: string
}

interface SelectComboboxProps {
defaultValue?: SelectComboboxValue
items: SelectComboboxValue[]
}

export const useSelectCombobox = ({
defaultValue = { id: "", name: "" },
items,
}: SelectComboboxProps) => {
const [selectedItem, setSelectedItem] =
useState<SelectComboboxValue>(defaultValue)
const [filteredItems, setFilteredItems] = useState(items)
const [hasMatchItem, setHasMatchItem] = useState(false)

const handleSelect = (value: SelectComboboxValue) => {
let hasMatchItem = false
setFilteredItems(
items.filter((item) => {
if (item.id === value.id) {
hasMatchItem = true
}
return item.name.toLowerCase().includes(value.name.toLowerCase())
})
)
setSelectedItem(value)
setHasMatchItem(hasMatchItem)
}

const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target
handleSelect({ id: value, name: value })
}

return {
selectedItem,
filteredItems,
handleSelect,
handleChange,
hasMatchItem,
}
}
3 changes: 2 additions & 1 deletion docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"@/utils/*": ["utils/*"],
"@/icons/*": ["components/Icons/*"],
"@/icons": ["components/Icons"],
"@/data/*": ["pages/data/*"]
"@/data/*": ["pages/data/*"],
"@/hooks/*": ["hooks/*"]
},
"plugins": [
{
Expand Down
Loading