Skip to content

Commit

Permalink
feat: input
Browse files Browse the repository at this point in the history
  • Loading branch information
bitterteasweetorange committed Nov 2, 2023
1 parent 94f8425 commit c3325bf
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 95 deletions.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@
"react-dom": ">=16.8"
},
"dependencies": {
"@mui/icons-material": "^5.14.0",
"rxjs": "^7.8.1",
"rxjs-hooks": "0.8.0-alpha.0"
"use-debounce": "^9.0.4",
"@mui/icons-material": "^5.14.0"
}
}
44 changes: 8 additions & 36 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions src/EasyCascader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CascaderProps, EasyCascaderBaseNode, Id } from '../types'
export function EasyCascader<T extends EasyCascaderBaseNode>({
selectedId,
data,
search: debouncedSearch,
search,
endAdornment,
setSelectedId,
startAdornment,
Expand All @@ -29,10 +29,8 @@ export function EasyCascader<T extends EasyCascaderBaseNode>({
const [searchText, setSearchText] = useState('')

useEffect(() => {
if (debouncedSearch) {
setSearchText(debouncedSearch)
}
}, [debouncedSearch])
setSearchText(search || '')
}, [search])

const flattenNodes: T[] = useMemo(() => {
if (!searchText) return []
Expand Down
27 changes: 27 additions & 0 deletions src/EasyCascaderInput/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Chip } from '@mui/material'
import type { Meta } from '@storybook/react'
import { useState } from 'react'
import { EasyCascaderInput } from '.'
import { MockObject, mockObjectNodes } from '../mock'

const meta = {
title: 'CascaderInput',
component: EasyCascaderInput,
} satisfies Meta<typeof EasyCascaderInput>

export default meta

export const Defalut = () => {
const [value, onChange] = useState<MockObject | null>(mockObjectNodes[2])

return (
<EasyCascaderInput<MockObject>
data={mockObjectNodes}
getNodeLabel={(node) => node.name}
endAdornment={(node) => node.age && <Chip label={node.age} />}
label={'label'}
value={value}
onChange={onChange}
/>
)
}
52 changes: 25 additions & 27 deletions src/EasyCascaderInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,33 @@ import { Box, TextField } from '@mui/material'
import { RefObject, useRef, useState } from 'react'
import { EasyCascader } from 'src/EasyCascader'
import { EasyPopper } from 'src/EasyPopper'
import { getLabel } from 'src/utils/getNodeLabel'
import { CascaderInputProps } from '../types'
import { useDebounce } from 'use-debounce'
import { CascaderInputProps, EasyCascaderBaseNode, Id } from '../types'

export function EasyCascaderInput<T>(props: CascaderInputProps<T>) {
export function EasyCascaderInput<T extends EasyCascaderBaseNode>(
props: CascaderInputProps<T>,
) {
const [isSearch, setIsSearch] = useState<boolean>(false)
const [search, setSearch] = useState<string>('')

const [debouncedSearch] = useDebounce(search, 500)
const [focused, setFocused] = useState<boolean>(false)
const anchorRef = useRef<HTMLDivElement>(null)
const {
value,
onChange,
getNodeLabel,
nodes,
renderNode: render,
isEqual,
data,
endAdornment,
startAdornment,
label,
error,
disabled,
required,
helperText,
} = props

const [selected, onSelect] = useState<T | null>(value)
const [selectedId, onSelectedId] = useState<Id | null>(value?.id ?? null)

return (
<>
Expand All @@ -40,19 +43,11 @@ export function EasyCascaderInput<T>(props: CascaderInputProps<T>) {
onFocus={() => {
setFocused(true)
setIsSearch(true)
onSelect(value)
onSelectedId(value?.id ?? null)
}}
ref={anchorRef}
placeholder={
isSearch ? (value === null ? '' : getLabel(value, getNodeLabel)) : ''
}
value={
isSearch
? search
: value === null
? ''
: getLabel(value, getNodeLabel)
}
placeholder={isSearch && value ? getNodeLabel(value) : ''}
value={isSearch ? search : value === null ? '' : getNodeLabel(value)}
onChange={(e) => {
setSearch(e.target.value)
}}
Expand All @@ -69,22 +64,25 @@ export function EasyCascaderInput<T>(props: CascaderInputProps<T>) {
<Box>
<EasyCascader<T>
getNodeLabel={getNodeLabel}
renderNode={render}
nodes={nodes}
isEqual={isEqual}
selected={selected}
onSelect={(nextSelect, isLeaf) => {
onSelect(nextSelect)
startAdornment={startAdornment}
endAdornment={endAdornment}
selectedId={selectedId}
data={data}
setSelectedId={(id) => {
const node = data.find((node) => node.id === id)
if (!node) return
const isLeaf = node?.childrenId?.length === 0 || !node?.childrenId
onSelectedId(id)
if (isLeaf) {
setFocused(false)
onChange(nextSelect)
onChange(node)
setIsSearch(false)
setSearch('')
} else {
onSelect(nextSelect)
onSelectedId(id)
}
}}
search={search}
search={debouncedSearch}
/>
</Box>
</EasyPopper>
Expand Down
2 changes: 1 addition & 1 deletion src/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ export const mockObjectNodes: MockObject[] = [
id: 2,
name: 'children-1',
pathId: [0],
age: 10,
},
{
id: 3,
name: 'parent-1',
age: 200,
},
]
12 changes: 6 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReactNode } from 'react'

export type CascaderInputProps<T extends EasyCascaderBaseNode> =
CascaderProps<T> & {
EasyCascaderDuplicatedProps<T> & {
value: T | null
onChange: (value: T | null) => void
label?: ReactNode
Expand All @@ -23,11 +23,11 @@ export type EasyCascaderDuplicatedProps<T> = {
getNodeLabel: (node: T) => string
startAdornment?: (node: T) => ReactNode
endAdornment?: (node: T) => ReactNode
// hover to show
selectedId: Id | null
setSelectedId: (id: Id | null) => void
search?: string
}

export type CascaderProps<T extends EasyCascaderBaseNode> =
EasyCascaderDuplicatedProps<T>
EasyCascaderDuplicatedProps<T> & {
search?: string
selectedId: Id | null
setSelectedId: (id: Id | null) => void
}
21 changes: 4 additions & 17 deletions tests/CascaderInput.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { expect, test } from '@playwright/test'

const path = '/iframe.html?id=input-cascaderinput--defalut&viewMode=story'
const path = '/iframe.html?id=cascaderinput--defalut&viewMode=story'
test('default value', async ({ page }) => {
await page.goto(path)

const input = page.locator('input')
await expect(input).toHaveValue('children-1(10)')
await expect(input).toHaveValue('children-1')
})

test('use value as placeholder when focused', async ({ page }) => {
Expand All @@ -15,10 +15,10 @@ test('use value as placeholder when focused', async ({ page }) => {
await input.focus()

await expect(input).toHaveValue('')
await expect(input).toHaveAttribute('placeholder', 'children-1(10)')
await expect(input).toHaveAttribute('placeholder', 'children-1')

page.click('body')
await expect(input).toHaveValue('children-1(10)')
await expect(input).toHaveValue('children-1')
})

test('select leaf', async ({ page }) => {
Expand Down Expand Up @@ -46,16 +46,3 @@ test('render node', async ({ page }) => {
await expect(li.first()).toHaveText('100')
await expect(li.nth(1)).toHaveText('10')
})

// TODO
test('search', async ({ page }) => {
await page.goto(path)

const input = page.locator('input')
await input.scrollIntoViewIfNeeded()
await input.type('10')

const items = page.getByRole('menuitem')
const length = (await items.all()).length
expect(length).toBe(2)
})

0 comments on commit c3325bf

Please sign in to comment.