Skip to content

Commit

Permalink
Add a typeahead for the transactions dropdown (solana-labs#786)
Browse files Browse the repository at this point in the history
  • Loading branch information
nramadas authored Jun 27, 2022
1 parent b4180f7 commit ef24c6a
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 22 deletions.
135 changes: 135 additions & 0 deletions components/TypeaheadSelect/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Combobox } from '@headlessui/react'
import { useState } from 'react'
import classNames from 'classnames'
import { ChevronDownIcon } from '@heroicons/react/solid'

interface Option {
key: string
text: string
}

interface Props<O extends Option> {
className?: string
placeholder?: string
options: O[]
selected?: Pick<O, 'key'>
onSelect(option?: O): void
}

export default function TypeaheadSelect<O extends Option>(props: Props<O>) {
const [query, setQuery] = useState('')
const value = props.options.find(
(option) => option.key === props.selected?.key
)
const list = props.options.filter((option) => {
if (!query) {
return true
}

return option.text.toLocaleLowerCase().includes(query.toLocaleLowerCase())
})

return (
<Combobox
value={value}
onChange={(value) => {
props.onSelect(value)
setQuery('')
}}
>
{({ open }) => {
const isOpen = !!(open && list.length)

return (
<Combobox.Button
as="div"
className={classNames('relative', props.className)}
>
<Combobox.Input
className={classNames(
'bg-bkg-1',
'border-fgd-3',
'border',
'default-transition',
'h-auto',
'pl-3',
'py-2',
'pr-10',
'rounded-md',
'text-fgd-1',
'text-sm',
'w-full',
'disabled:cursor-not-allowed',
'disabled:opacity-50',
'disabled:text-fgd-3',
'disabled:border-bkg-4',
'disabled:focus:border-bkg-4',
'disabled:hover:border-bkg-4',
'focus:border-primary-light',
'focus:outline-none',
'hover:border-primary-light'
)}
displayValue={(option: O) => option?.text || ''}
placeholder={props.placeholder}
onChange={(e) => setQuery(e.currentTarget.value)}
onFocus={(e) => e.currentTarget.select()}
/>
<ChevronDownIcon
className={classNames(
'-translate-y-1/2',
'absolute',
'default-transition',
'flex-shrink-0',
'h-5',
'mr-1',
'right-3',
'text-primary-light',
'top-1/2',
'w-5',
isOpen ? 'transform rotate-180' : 'transform rotate-360'
)}
/>
{isOpen && (
<Combobox.Options
static
className={classNames(
'absolute',
'bg-bkg-1',
'mt-1',
'py-2',
'rounded-md',
'w-full',
'z-10'
)}
>
<div
className={classNames('h-full', 'max-h-60', 'overflow-auto')}
>
{list.map((option) => (
<Combobox.Option key={option.key} value={option}>
{({ selected, active }) => (
<div
className={classNames(
'border-l-2',
'border-transparent',
'cursor-pointer',
'px-3',
'py-2',
active && 'bg-bkg-3',
selected && 'border-light-theme-primary-light'
)}
>
{option.text}
</div>
)}
</Combobox.Option>
))}
</div>
</Combobox.Options>
)}
</Combobox.Button>
)
}}
</Combobox>
)
}
51 changes: 29 additions & 22 deletions pages/dao/[symbol]/proposal/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import { PublicKey } from '@solana/web3.js'
import Button, { LinkButton, SecondaryButton } from '@components/Button'
import Input from '@components/inputs/Input'
import Select from '@components/inputs/Select'
import Textarea from '@components/inputs/Textarea'
import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWrapper'
import useGovernanceAssets from '@hooks/useGovernanceAssets'
Expand Down Expand Up @@ -83,6 +82,8 @@ import GoblinGoldWithdraw from './components/instructions/GoblinGold/GoblinGoldW
import MakeSetMarketMode from './components/instructions/Mango/MakeSetMarketMode'
import CreateGatewayPluginRegistrar from './components/instructions/GatewayPlugin/CreateRegistrar'
import MakeChangeQuoteParams from './components/instructions/Mango/MakeChangeQuoteParams'
import TypeaheadSelect from '@components/TypeaheadSelect'
import { StyledLabel } from '@components/inputs/styles'

const schema = yup.object().shape({
title: yup.string().required('Title is required'),
Expand Down Expand Up @@ -157,7 +158,7 @@ const New = () => {
}
const [instructionsData, setInstructions] = useState<
ComponentInstructionData[]
>([{ type: availableInstructions[0] }])
>([{ type: undefined }])
const handleSetInstructions = (val: any, index) => {
const newInstructions = [...instructionsData]
newInstructions[index] = { ...instructionsData[index], ...val }
Expand Down Expand Up @@ -298,8 +299,10 @@ const New = () => {
}

useEffect(() => {
setInstructions([instructionsData[0]])
}, [instructionsData[0].governedAccount?.pubkey])
if (instructionsData?.length) {
setInstructions([instructionsData[0]])
}
}, [instructionsData[0]?.governedAccount?.pubkey])

useEffect(() => {
const governedAccount = extractGovernanceAccountFromInstructionsData(
Expand Down Expand Up @@ -622,24 +625,28 @@ const New = () => {
key={idx}
className="mb-3 border border-fgd-4 p-4 md:p-6 rounded-lg"
>
<Select
className="h-12"
disabled={!getAvailableInstructionsForIndex.length}
placeholder={`${
availableInstructionsForIdx.length
? 'Select instruction'
: 'No available instructions'
}`}
label={`Transaction ${idx + 1}`}
onChange={(value) => setInstructionType({ value, idx })}
value={instruction.type?.name}
>
{availableInstructionsForIdx.map((inst, idx) => (
<Select.Option key={idx} value={inst}>
<span>{inst.name}</span>
</Select.Option>
))}
</Select>
<StyledLabel>Transaction {idx + 1}</StyledLabel>
<TypeaheadSelect
className="max-w-lg"
options={availableInstructionsForIdx.map(
(availableInstruction) => ({
data: availableInstruction,
key: availableInstruction.id.toString(),
text: availableInstruction.name,
})
)}
placeholder="Add a transaction"
selected={
instruction.type
? {
key: instruction.type.id.toString(),
}
: undefined
}
onSelect={(option) => {
setInstructionType({ value: option?.data, idx })
}}
/>
<div className="flex items-end pt-4">
<InstructionContentContainer
idx={idx}
Expand Down

0 comments on commit ef24c6a

Please sign in to comment.