Skip to content

Commit

Permalink
chore: improve code related to OAuth section (#11954)
Browse files Browse the repository at this point in the history
* chore: improve code related to `OAuth` section

- Removed the `useOAuthProviderSelect` hook
- Cleaned up the `OAuthProviderInstructions` component
- Introduced the new `PreviewProviders` component
- Added the `useSelectCombobox` hook
- Created a `hooks` folder and update the `tsconfig.json` paths

* chore: update `highlighter` function

- Replace `getHighlighter` with `createHighlighter`
- Ensure compatibility with existing functionality
  • Loading branch information
halvaradop authored Dec 3, 2024
1 parent 2922e28 commit 00b16ae
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 118 deletions.
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

0 comments on commit 00b16ae

Please sign in to comment.