Skip to content

Commit

Permalink
Merge pull request #96 from osstotalsoft/bugfix/fix-autocomplete-prim…
Browse files Browse the repository at this point in the history
…itive-creatable

fix autocomplete with creatable
  • Loading branch information
alexandra-c authored Dec 4, 2023
2 parents eb5c38a + 9af431e commit 66ec9d4
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 26 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ jobs:
run: yarn install
- name: Build
run: yarn build
# - name: Run tests
# run: yarn test
- name: Bump version
run: yarn run version
- name: Set npm authToken from env
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
with:
node-version: 16.x
- name: Install Dependencies
run: yarn install
run: yarn install
- name: Run tests
run: yarn test
- name: Run build test
run: yarn run build-test
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"request": "launch",
"runtimeExecutable": "yarn",
"runtimeArgs": ["run", "test"],
"args": ["--runInBand", "${fileBasenameNoExtension}"],
"args": ["--verbose", "-i", "--no-cache", "--testPathPattern", "${fileBasename}"],
"cwd": "${fileDirname}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"linter": "eslint . --fix --ext .ts,.tsx",
"publish": "yarn npm publish",
"storybook": "storybook dev -p 6006",
"test": "jest --collectCoverage --passWithNoTests--silent=false --runInBand && run build-test",
"test": "jest --collectCoverage --passWithNoTests--silent=false --runInBand",
"version": "yarn version -i $(git describe --abbrev=0 --tags)"
},
"browserslist": {
Expand Down
34 changes: 23 additions & 11 deletions src/components/inputs/Autocomplete/Autocomplete.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const basicOptions = [
{ id: 3, name: 'third option', displayName: 'Third Option' }
]

const primitiveOptions = ['first option', 'second option', 'third option']
const stringOptions = ['first option', 'second option', 'third option']

const numericOptions = [1, 2, 3]

Expand All @@ -23,7 +23,7 @@ describe('Single-value Autocomplete', () => {
expect(screen.getByTitle('Open')).toBeInTheDocument()
})

it('does not break when options are undefined and displays \'No options\' message', () => {
it('does not break when options are undefined and displays "No options" message', () => {
render(<Autocomplete open onChange={jest.fn()} />)
expect(screen.getByText('No options')).toBeInTheDocument()
})
Expand All @@ -47,7 +47,7 @@ describe('Single-value Autocomplete', () => {
expect(screen.getByRole('combobox')).toHaveAttribute('readonly')
})

it('sets \'No option\' text to typographyContentColor', () => {
it('sets "No option" text to typographyContentColor', () => {
render(<Autocomplete open options={[]} onChange={jest.fn()} typographyContentColor={'secondary'} />)
expect(screen.getByText('No options')).toHaveStyle(`color: ${theme.palette.secondary.main}`)
})
Expand All @@ -73,7 +73,7 @@ describe('Single-value Autocomplete', () => {
expect(screen.getByText('Add "new"')).toBeInTheDocument()
})

test('doesn\'t show \'Add\' option for the ones that already exist', () => {
test('does not show "Add" option for the ones that already exist', () => {
render(<Autocomplete open creatable createdLabel={'Add'} onChange={jest.fn()} options={basicOptions} />)
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'first option' } })
expect(screen.queryByText('Add "first option"')).not.toBeInTheDocument()
Expand Down Expand Up @@ -197,13 +197,13 @@ describe('Single-value Autocomplete', () => {

describe('with primitive options', () => {
test('displays selected option in input', () => {
render(<Autocomplete value={'first option'} options={primitiveOptions} onChange={jest.fn()} />)
render(<Autocomplete value={'first option'} options={stringOptions} onChange={jest.fn()} />)
expect(screen.getByRole<HTMLInputElement>('combobox').value).toBe('first option')
})

test('calls onChange with selected option', () => {
const mockOnChange = jest.fn()
render(<Autocomplete open options={primitiveOptions} onChange={mockOnChange} />)
render(<Autocomplete open options={stringOptions} onChange={mockOnChange} />)
const options = screen.getAllByRole('option')
act(() => userClick(options[0]))

Expand All @@ -222,6 +222,18 @@ describe('Single-value Autocomplete', () => {
act(() => userClick(options[0]))
expect(mockOnChange).toBeCalledWith(1, expect.anything(), 'selectOption')
})

test('does not show "Add" option for the one that already exist with numeric options', () => {
render(<Autocomplete open creatable createdLabel={'Add'} onChange={jest.fn()} options={numericOptions} />)
fireEvent.change(screen.getByRole('combobox'), { target: { value: 1 } })
expect(screen.queryByText('Add "1"')).not.toBeInTheDocument()
})

test('does not show "Add" option for the one that already exist with string options', () => {
render(<Autocomplete open creatable createdLabel={'Add'} onChange={jest.fn()} options={stringOptions} />)
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'first option' } })
expect(screen.queryByText('Add "first option"')).not.toBeInTheDocument()
})
})
})

Expand All @@ -248,7 +260,7 @@ describe('Multi-value Autocomplete', () => {
expect(screen.getByText('second option').parentElement).toHaveClass('Mui-disabled')
})

it('doesn\'t clear disabled selections when `Clear` button is clicked', async () => {
it('does not clear disabled selections when "Clear" button is clicked', async () => {
const mockGetOptionDisabled = jest.fn(option => option.isDisabled)
const mockOnChange = jest.fn()
render(
Expand Down Expand Up @@ -520,15 +532,15 @@ describe('Async Autocomplete', () => {
await act(() => promise)
})

test('doesn\'t call loadOptions at render if defaultOptions is not true', async () => {
test('does not call loadOptions at render if defaultOptions is not true', async () => {
const promise = Promise.resolve(basicOptions)
const mockLoadOptions = jest.fn(() => promise)
render(<Autocomplete simpleValue loadOptions={mockLoadOptions} onChange={jest.fn()} />)
expect(mockLoadOptions).not.toBeCalled()
await act(() => promise)
})

test('doesn\'t call loadOptions at render for initial value if defaultOptions={false}', async () => {
test('does not call loadOptions at render for initial value if defaultOptions={false}', async () => {
const promise = Promise.resolve(basicOptions)
const mockLoadOptions = jest.fn(() => promise)
render(
Expand Down Expand Up @@ -594,14 +606,14 @@ describe('Async Multi-value Autocomplete', () => {
expect(mockLoadOptions).toBeCalledTimes(1)
})

test('doesn\'t call loadOptions if no initial value was provided - when simpleValue={true}', () => {
test('does not call loadOptions if no initial value was provided - when simpleValue={true}', () => {
const promise = Promise.resolve(basicOptions)
const mockLoadOptions = jest.fn(() => promise)
render(<Autocomplete isMultiSelection simpleValue loadOptions={mockLoadOptions} value={[]} onChange={jest.fn()} />)
expect(mockLoadOptions).not.toBeCalled()
})

test('doesn\'t call loadOptions if no initial value was provided - when simpleValue={false}', () => {
test('does not call loadOptions if no initial value was provided - when simpleValue={false}', () => {
const promise = Promise.resolve(basicOptions)
const mockLoadOptions = jest.fn(() => promise)
render(<Autocomplete isMultiSelection loadOptions={mockLoadOptions} value={[]} onChange={jest.fn()} />)
Expand Down
11 changes: 9 additions & 2 deletions src/components/inputs/Autocomplete/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createFilterOptions } from '@mui/material/Autocomplete'
import { prop, map, innerJoin, find, propEq, all, includes, is, isEmpty, isNil, props, omit } from 'ramda'
import { prop, map, innerJoin, find, propEq, all, includes, is, isEmpty, isNil, props, omit, equals, any } from 'ramda'
import { AutocompleteValue, FilterOptionsState } from '@mui/material'

export const findFirstNotNil = (propNames: string[], option: any) => find(x => !isNil(x), props(propNames, option))
Expand All @@ -14,7 +14,14 @@ export const filterOptions =
const { inputValue } = params

// Suggest the creation of a new value if it's not empty and it doesn't already exist
const exists = find(propEq(inputValue, labelKey), options)
const exists = any(
(option: any) =>
is(Object, option)
? equals(inputValue, is(String, option?.[labelKey]) ? option?.[labelKey] : JSON.stringify(option?.[labelKey]))
: equals(inputValue, is(String, option) ? option : JSON.stringify(option)),
options
)

if (creatable && !(isEmpty(inputValue) || isNil(inputValue)) && !exists) {
filtered.push(
hasStringOptions(options)
Expand Down
31 changes: 24 additions & 7 deletions src/stories/inputs/Autocomplete/CreatablePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import Grid from '@mui/material/Grid'
import ControlledCheckbox from './components/ControlledCheckBox'
import FormattedJson from './components/FormattedJson'
import ColumnHeader from './components/ColumnHeader'
import { options, primitiveOptions, numericOptions } from './_mocks'
import { options, primitiveStringOptions, primitiveNumericOptions, numericOptions } from './_mocks'
import { Autocomplete } from 'components'

export const CreatablePreview = () => {
const [simpleValueBasic, setSimpleValueBasic] = useState(false)
const [basicValue, setBasicValue] = useState()

const [primitiveValue, setPrimitiveValue] = useState()
const [primitiveStringValue, setPrimitiveStringValue] = useState()
const [primitiveNumericValue, setPrimitiveNumericValue] = useState()

const [simpleValueNumeric, setSimpleValueNumeric] = useState(false)
const [numericValue, setNumericValue] = useState()
Expand Down Expand Up @@ -53,17 +54,33 @@ export const CreatablePreview = () => {
<Grid item xs={12} container spacing={4}>
<Grid item xs={3}>
<Autocomplete
label="Primitive Autocomplete"
value={primitiveValue}
label="Primitive String Autocomplete"
value={primitiveStringValue}
creatable
onChange={setPrimitiveValue}
onChange={setPrimitiveStringValue}
isClearable={true}
options={primitiveOptions}
options={primitiveStringOptions}
/>
</Grid>
<Grid item xs={3}></Grid>
<Grid item xs={3}>
<FormattedJson>{primitiveValue}</FormattedJson>
<FormattedJson>{primitiveStringValue}</FormattedJson>
</Grid>
</Grid>
<Grid item xs={12} container spacing={4}>
<Grid item xs={3}>
<Autocomplete
label="Primitive Numeric Autocomplete"
value={primitiveNumericValue}
creatable
onChange={setPrimitiveNumericValue}
isClearable={true}
options={primitiveNumericOptions}
/>
</Grid>
<Grid item xs={3}></Grid>
<Grid item xs={3}>
<FormattedJson>{primitiveNumericValue}</FormattedJson>
</Grid>
</Grid>
<Grid item xs={12} container spacing={4}>
Expand Down
3 changes: 2 additions & 1 deletion src/stories/inputs/Autocomplete/_mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export const customOptions = [
{ id: 3, name: 'Turtle', icon: BugReport, description: 'Turtles are slow' },
{ id: 4, name: 'Horse', icon: BedroomBaby, description: 'Horses are elegant.' }
]
export const primitiveOptions = ['first option', 'second option', 'third option']
export const primitiveStringOptions = ['first option', 'second option', 'third option']
export const primitiveNumericOptions = [1, 2, 3]
export const numericOptions = [{ period: 1 }, { period: 2 }, { period: 3 }]

function sleep(delay = 0) {
Expand Down

0 comments on commit 66ec9d4

Please sign in to comment.