diff --git a/src/components/feedback/LinearProgress/LinearProgressStyles.ts b/src/components/feedback/LinearProgress/LinearProgressStyles.ts
index f7e02fd8..1dd4710d 100644
--- a/src/components/feedback/LinearProgress/LinearProgressStyles.ts
+++ b/src/components/feedback/LinearProgress/LinearProgressStyles.ts
@@ -11,7 +11,6 @@ export const LinearProgress: any = styled(MuiLinearProgress, {
},
'&.MuiLinearProgress-root': {
height: '4px',
- marginBottom: '20px',
overflow: 'hidden'
}
}))
diff --git a/src/components/inputs/Autocomplete/Autocomplete.test.tsx b/src/components/inputs/Autocomplete/Autocomplete.test.tsx
index 4d5952e8..643dbf27 100644
--- a/src/components/inputs/Autocomplete/Autocomplete.test.tsx
+++ b/src/components/inputs/Autocomplete/Autocomplete.test.tsx
@@ -70,44 +70,48 @@ describe('Single-value Autocomplete', () => {
describe('creatable', () => {
test('displays created label text after typing some characters', () => {
- render()
+ render()
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'new' } })
expect(screen.getByText('Add "new"')).toBeInTheDocument()
})
- test('does not show "Add" option for the ones that already exist', () => {
- render()
- fireEvent.change(screen.getByRole('combobox'), { target: { value: 'first option' } })
- expect(screen.queryByText('Add "first option"')).not.toBeInTheDocument()
- })
-
- test('calls onChange with the new option', async () => {
- const mockOnChange = jest.fn()
+ test('displays created label text after typing some characters with multi select', () => {
render(
)
+ fireEvent.change(screen.getByRole('combobox'), { target: { value: 'new option' } })
+ expect(screen.getByText('Add "new option"')).toBeInTheDocument()
+ })
+
+ test('displays created label text after typing some characters with load options', async () => {
+ const promise = Promise.resolve(basicOptions)
+ const mockLoadOptions = jest.fn(() => promise)
+ render()
+
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'new' } })
- act(() => userClick(screen.getByText('Add "new"')))
+ expect(screen.getByText('Add "new"')).toBeInTheDocument()
+ })
- await waitFor(() => {
- expect(mockOnChange).toBeCalledWith('new', expect.anything(), 'createOption', expect.anything())
- })
+ test('does not show "Add" option for the ones that already exist', () => {
+ render()
+ fireEvent.change(screen.getByRole('combobox'), { target: { value: 'first option' } })
+ expect(screen.queryByText('Add "first option"')).not.toBeInTheDocument()
})
- test('calls onChange with the new value', async () => {
+ test('calls onChange with the new option', async () => {
const mockOnChange = jest.fn()
render(
{
})
})
- describe('with simpleValue={true}', () => {
+ describe('with value "primitive"', () => {
test('displays value of [labelKey] property', () => {
render()
expect(screen.getByRole('combobox').value).toBe('First Option')
@@ -171,7 +175,7 @@ describe('Single-value Autocomplete', () => {
})
})
- describe('with simpleValue={false} (default)', () => {
+ describe('with object values', () => {
test('displays value of labelKey property', () => {
render()
expect(screen.getByRole('combobox').value).toBe('First Option')
@@ -262,33 +266,7 @@ describe('Multi-value Autocomplete', () => {
})
})
- describe('creatable', () => {
- test('displays created label text after typing some characters (simpleValue={false})', () => {
- render(
-
- )
- fireEvent.change(screen.getByRole('combobox'), { target: { value: 'new' } })
- fireEvent.change(screen.getByRole('combobox'), { target: { value: 'new option' } })
- expect(screen.getByText('Add "new option"')).toBeInTheDocument()
- })
- })
-
- describe('with simpleValue={true}', () => {
- test('displays values of [labelKey] property', () => {
- render(
-
- )
- expect(screen.getByText('First Option')).toBeInTheDocument()
- expect(screen.getByText('Second Option')).toBeInTheDocument()
- })
-
+ describe('display from option when value is just the "id" (valueKey)', () => {
test('displays values of name by default', () => {
render()
expect(screen.getByText('first option')).toBeInTheDocument()
@@ -303,11 +281,6 @@ describe('Multi-value Autocomplete', () => {
expect(mockOnChange).toBeCalledWith([basicOptions[0]], expect.anything(), 'selectOption', expect.anything())
})
-
- test('can display a value that is number', () => {
- render()
- expect(screen.getByText('1')).toBeInTheDocument()
- })
})
describe('default behavior when you send simple values', () => {
@@ -353,7 +326,6 @@ describe('Async Autocomplete', () => {
expect(mockLoadOptions).not.toBeCalled()
act(() => userClick(screen.getByTitle('Open')))
expect(mockLoadOptions).toBeCalled()
- await act(() => promise)
})
// not yet implemented
@@ -363,7 +335,6 @@ describe('Async Autocomplete', () => {
render()
await waitFor(() => expect(mockLoadOptions).toBeCalled())
- await act(() => promise)
})
test('has loading state', async () => {
@@ -386,12 +357,11 @@ describe('Async Autocomplete', () => {
expect(mockLoadOptions.mock.calls[0]).toHaveLength(3)
})
- describe('with simpleValue={false}', () => {
- test('displays initial value - when defaultOptions={false}', async () => {
+ describe('loadOptions', () => {
+ test('displays initial value even when there are no options', async () => {
const promise = Promise.resolve(basicOptions)
const mockLoadOptions = jest.fn(() => promise)
render()
- await act(() => promise)
expect(screen.getByRole('combobox').value).toBe('first option')
})
@@ -403,91 +373,6 @@ describe('Async Autocomplete', () => {
fireEvent.click(screen.getByRole('button'))
expect(mockLoadOptions).toBeCalledWith('first option', expect.anything(), null)
expect(mockLoadOptions.mock.calls[0]).toHaveLength(3)
- await act(() => promise)
- })
- })
- describe.skip('with simpleValue={true}', () => {
- test('calls loadOptions with input value', async () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- await act(() => promise)
-
- expect(mockLoadOptions).toBeCalledWith(undefined)
- await act(() => promise)
-
- expect(mockLoadOptions).toBeCalledWith('first option')
- expect(mockLoadOptions).toBeCalledTimes(2)
})
-
- test('displays initial value', async () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- await act(() => promise)
- expect(screen.getByRole('combobox').value).toBe('first option')
- })
-
- test('calls loadOptions with input value - when defaultOptions is an array', async () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- expect(mockLoadOptions).toBeCalledWith('first option')
- expect(mockLoadOptions.mock.calls[0]).toHaveLength(1)
- await act(() => promise)
- })
-
- test('does not call loadOptions at render if there is no initial value', async () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- expect(mockLoadOptions).not.toBeCalled()
- await act(() => promise)
- })
-
- test('does not call loadOptions at render for initial value', async () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- expect(mockLoadOptions).not.toBeCalled()
- await act(() => promise)
- })
- })
-
- describe('creatable', () => {
- test('displays created label text after typing some characters', async () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
-
- await act(() => promise)
- fireEvent.change(screen.getByRole('combobox'), { target: { value: 'new' } })
- await act(() => promise)
- expect(screen.getByText('Add "new"')).toBeInTheDocument()
- })
- })
-})
-
-describe.skip('Async Multi-value Autocomplete', () => {
- test('calls loadOptions at render when it has initial value', async () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- await act(() => promise)
- expect(mockLoadOptions).toBeCalledTimes(1)
- })
-
- test('does not call loadOptions if no initial value was provided', () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- expect(mockLoadOptions).not.toBeCalled()
- })
-
- test('does not call loadOptions if no initial value was provided', () => {
- const promise = Promise.resolve(basicOptions)
- const mockLoadOptions = jest.fn(() => promise)
- render()
- expect(mockLoadOptions).not.toBeCalled()
})
})
diff --git a/src/components/inputs/Autocomplete/Autocomplete.tsx b/src/components/inputs/Autocomplete/Autocomplete.tsx
index 603db06d..dd56fc01 100644
--- a/src/components/inputs/Autocomplete/Autocomplete.tsx
+++ b/src/components/inputs/Autocomplete/Autocomplete.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useState } from 'react'
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
import PropTypes from 'prop-types'
import {
AutocompleteChangeDetails,
@@ -35,10 +35,17 @@ const Autocomplete: React.FC<
isMultiSelection = false,
withCheckboxes = false,
isClearable = false,
- renderOption,
creatable = false,
createdLabel = 'Add',
onChange,
+ loadingText = ,
+ loading,
+ loadOptions,
+ open,
+ onOpen,
+ onClose,
+ onInputChange,
+ isPaginated,
// ---------------- input field props ----------------
label,
placeholder,
@@ -48,16 +55,13 @@ const Autocomplete: React.FC<
isSearchable = true,
inputTextFieldProps,
// ---------------------------------------------------
- loadingText = ,
- loading,
- loadOptions,
- open,
- onOpen,
- onClose,
- onInputChange,
- isPaginated,
...rest
}) => {
+ /**
+ * handle both string and function from valueKey and labelKey.
+ */
+ const getValue = useMemo(() => (valueKey instanceof Function ? valueKey : prop(valueKey)), [valueKey])
+ const getLabel = useMemo(() => (labelKey instanceof Function ? labelKey : prop(labelKey)), [labelKey])
/**
* Handle the internal options to aid lazy loading.
*/
@@ -79,8 +83,6 @@ const Autocomplete: React.FC<
(option: unknown, showLabel?: boolean | never) => {
if (getOptionLabel) return getOptionLabel(option)
const label = showLabel ? internalLabel : internalValue
- const getValue = valueKey instanceof Function ? valueKey : prop(valueKey)
- const getLabel = labelKey instanceof Function ? labelKey : prop(labelKey)
const convertedOption: unknown = convertValueToOption(
option,
@@ -90,7 +92,7 @@ const Autocomplete: React.FC<
return extractFirstValue([getLabel, getValue, label, identity], convertedOption)
},
- [allOptions, getOptionLabel, labelKey, valueKey]
+ [allOptions, getLabel, getOptionLabel, getValue]
)
/**
@@ -200,7 +202,7 @@ const Autocomplete: React.FC<
liProps: React.HTMLAttributes & { key: any },
option: unknown,
state: AutocompleteRenderOptionState,
- ownerState: AutocompleteOwnerState<
+ _ownerState: AutocompleteOwnerState<
unknown,
boolean | undefined,
boolean | undefined,
@@ -230,8 +232,7 @@ const Autocomplete: React.FC<
/**
* This is the default option rendering that can handle new created options.
*/
- const getValue = valueKey instanceof Function ? valueKey : prop(valueKey)
- const result = (
+ return (
)
- /**
- * If a custom renderOption is provided we will use it.
- */
- return renderOption ? renderOption(liProps, option, state, ownerState) : result
},
- [handleGetOptionLabel, loadingText, ref, renderOption, valueKey, withCheckboxes]
+ [getValue, handleGetOptionLabel, loadingText, ref, withCheckboxes]
)
const handleFilterOptions = useCallback(
@@ -289,11 +286,10 @@ const Autocomplete: React.FC<
*/
const handleOptionEqualToValue = useCallback(
(option: unknown, value: unknown) => {
- const getValue = valueKey instanceof Function ? valueKey : prop(valueKey)
const equalFn = extractFirstValue([getValue, internalValue, identity])
return eqBy(equalFn, option, value)
},
- [valueKey]
+ [getValue]
)
/**
@@ -336,8 +332,6 @@ const Autocomplete: React.FC<
>
) => {
return value.map((option, index) => {
- const getValue = valueKey instanceof Function ? valueKey : prop(valueKey)
-
const convertedOption = convertValueToOption(
option,
allOptions,
@@ -355,20 +349,12 @@ const Autocomplete: React.FC<
)
})
},
- [allOptions, handleGetOptionLabel, valueKey]
+ [allOptions, getValue, handleGetOptionLabel]
)
/**
* Handle the default input field.
*/
- const handleClick = useCallback(
- (event: React.MouseEvent) => {
- if (inputTextFieldProps?.onClick) {
- inputTextFieldProps.onClick(event)
- }
- },
- [inputTextFieldProps]
- )
const handleRenderInput = useCallback(
(params: AutocompleteRenderInputParams) => (
),
- [error, handleClick, helperText, inputTextFieldProps, isSearchable, label, placeholder, required]
+ [error, helperText, inputTextFieldProps, isSearchable, label, placeholder, required]
)
/**
@@ -436,10 +421,17 @@ Autocomplete.propTypes = {
isMultiSelection: PropTypes.bool,
withCheckboxes: PropTypes.bool,
isClearable: PropTypes.bool,
- renderOption: PropTypes.func,
creatable: PropTypes.bool,
createdLabel: PropTypes.string,
onChange: PropTypes.func,
+ loadingText: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
+ loading: PropTypes.bool,
+ loadOptions: PropTypes.func,
+ open: PropTypes.bool,
+ onOpen: PropTypes.func,
+ onClose: PropTypes.func,
+ onInputChange: PropTypes.func,
+ isPaginated: PropTypes.bool,
// ---------------- input field props ----------------
label: PropTypes.string,
placeholder: PropTypes.string,
@@ -447,16 +439,8 @@ Autocomplete.propTypes = {
helperText: PropTypes.string,
required: PropTypes.bool,
isSearchable: PropTypes.bool,
- inputTextFieldProps: PropTypes.object,
+ inputTextFieldProps: PropTypes.object
// ---------------------------------------------------
- loadingText: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
- loading: PropTypes.bool,
- loadOptions: PropTypes.func,
- open: PropTypes.bool,
- onOpen: PropTypes.func,
- onClose: PropTypes.func,
- onInputChange: PropTypes.func,
- isPaginated: PropTypes.bool
}
export default Autocomplete
diff --git a/src/components/inputs/Autocomplete/types.ts b/src/components/inputs/Autocomplete/types.ts
index b63e879b..61994771 100644
--- a/src/components/inputs/Autocomplete/types.ts
+++ b/src/components/inputs/Autocomplete/types.ts
@@ -3,9 +3,7 @@ import {
AutocompleteChangeReason,
AutocompleteCloseReason,
AutocompleteInputChangeReason,
- AutocompleteOwnerState,
AutocompleteRenderInputParams,
- AutocompleteRenderOptionState,
AutocompleteValue,
ChipTypeMap,
AutocompleteProps as MuiAutocompleteProps
@@ -56,7 +54,6 @@ export interface AutocompleteProps<
MuiAutocompleteProps,
| 'options'
| 'getOptionLabel'
- | 'renderOption'
| 'onChange'
| 'loadingText'
| 'loading'
@@ -103,21 +100,6 @@ export interface AutocompleteProps<
* @default false
*/
isClearable?: boolean
- /**
- * Render the option, use `getOptionLabel` by default.
- *
- * @param {object} props The props to apply on the li element.
- * @param {T} option The option to render.
- * @param {object} state The state of each option.
- * @param {object} ownerState The state of the Autocomplete component.
- * @returns {ReactNode}
- */
- renderOption?: (
- props: React.HTMLAttributes & { key: any },
- option: T,
- state: AutocompleteRenderOptionState,
- ownerState: AutocompleteOwnerState
- ) => React.ReactNode
/**
* If true, the Autocomplete is free solo, meaning that the user input is not bound to provided options and can add
* his own values.
@@ -142,37 +124,6 @@ export interface AutocompleteProps<
reason: AutocompleteChangeReason,
details?: AutocompleteChangeDetails
) => void
- /**
- * Label to be displayed in the heading component.
- */
- label?: string
- /**
- * Text to be displayed as a placeholder in the text field.
- */
- placeholder?: string
- /**
- * If true, the helper text is displayed when an error pops up.
- * @default false
- */
- error?: boolean
- /**
- * The content of the helper under the input.
- */
- helperText?: React.ReactNode
- /**
- * If `true`, the label is displayed as required and the `input` element is required.
- * @default false
- */
- required?: boolean
- /**
- * If false, the user cannot type in Autocomplete, filter options or create new ones.
- * @default true
- */
- isSearchable?: boolean
- /**
- * Properties that will be passed to the rendered input. This is a TextField.
- */
- inputTextFieldProps?: Partial
/**
* Text to display when in a loading state.
*
@@ -231,4 +182,35 @@ export interface AutocompleteProps<
* @returns {ReactNode}
*/
renderInput?: (params: AutocompleteRenderInputParams) => React.ReactNode
+ /**
+ * Label to be displayed in the heading component.
+ */
+ label?: string
+ /**
+ * Text to be displayed as a placeholder in the text field.
+ */
+ placeholder?: string
+ /**
+ * If true, the helper text is displayed when an error pops up.
+ * @default false
+ */
+ error?: boolean
+ /**
+ * The content of the helper under the input.
+ */
+ helperText?: React.ReactNode
+ /**
+ * If `true`, the label is displayed as required and the `input` element is required.
+ * @default false
+ */
+ required?: boolean
+ /**
+ * If false, the user cannot type in Autocomplete, filter options or create new ones.
+ * @default true
+ */
+ isSearchable?: boolean
+ /**
+ * Properties that will be passed to the rendered input. This is a TextField.
+ */
+ inputTextFieldProps?: Partial
}
diff --git a/src/stories/inputs/Autocomplete/StylingPreview.tsx b/src/stories/inputs/Autocomplete/StylingPreview.tsx
deleted file mode 100644
index 19bb4af4..00000000
--- a/src/stories/inputs/Autocomplete/StylingPreview.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) TotalSoft.
-// This source code is licensed under the MIT license.
-
-import React, { useState } from 'react'
-import Grid from '@mui/material/Grid2'
-import ColumnHeader from './components/ColumnHeader'
-import { Autocomplete, Typography } from 'components'
-
-export const StylingPreview = () => {
- const [inputColorValue, setInputColorValue] = useState(1)
- const [typographyColorValue, setTypographyColorValue] = useState()
-
- return (
-
-
-
- {'Component'}
-
-
- {'Prop'}
-
-
- {'Value'}
-
-
-
-
-
-
-
- {'inputSelectedColor'}
-
-
- {'#26C6DA'}
-
-
-
-
-
-
-
- {'typographyContentColor'}
-
-
- {'error'}
-
-
-
- )
-}