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 (