Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,71 @@ describe('Autocomplete', () => {
expect(child).toHaveClass('additional-class');
});

describe('getTextFieldChild', () => {
it('renders correctly when a single TextField is provided', () => {
renderAutocomplete({
children: <TextField label="Autocomplete field" name="autocomplete_field" />,
});

const input = screen.getByLabelText('Autocomplete field');
expect(input).toBeInTheDocument();

expect(input.tagName).toBe('INPUT');

expect(screen.getByRole('combobox', { name: /autocomplete field/i })).toBeInTheDocument();
});

it('renders only the last TextField when multiple are provided', () => {
renderAutocomplete({
children: [
<TextField key="1" label="First field" name="first_field" />,
<TextField key="2" label="Second field" name="second_field" />,
],
});

expect(screen.queryByLabelText('First field')).not.toBeInTheDocument();
expect(screen.getByLabelText('Second field')).toBeInTheDocument();
});

it('renders correctly when children include one TextField and one non-TextField element', () => {
renderAutocomplete({
children: [
<div key="1" data-testid="non-text-field">
Not a TextField
</div>,
<TextField key="2" label="Valid field" name="valid_field" />,
],
});

const input = screen.getByLabelText('Valid field');
expect(input).toBeInTheDocument();

expect(screen.queryByTestId('non-text-field')).not.toBeInTheDocument();
});

describe('error cases', () => {
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation(() => {});
});

afterEach(() => {
(console.error as jest.Mock).mockRestore();
});

it('throws an error when no TextField child is provided', () => {
expect(() =>
renderAutocomplete({
children: <div>Not a text field</div>,
})
).toThrow();
});

it('throws an error when children are missing entirely', () => {
expect(() => renderAutocomplete({ children: undefined })).toThrow();
});
});
});

describe('default props', () => {
describe('ariaClearLabel deprecation', () => {
const originalEnv = process.env.NODE_ENV;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ export const Autocomplete = (props: AutocompleteProps) => {
statusMessage = renderStatusMessage(noResultsMessage ?? t('autocomplete.noResultsMessage'));
}

const textField = getTextFieldChild(children);
const textField = getTextFieldChild(children) as React.ReactElement<any>;
const size = textField.props.size;
const labelId = textField.props.labelId ?? `${id}__label`;

Expand Down
12 changes: 7 additions & 5 deletions packages/design-system/src/components/Autocomplete/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isValidElement, Children, ReactElement, ReactNode } from 'react';
import { isValidElement, ReactElement, ReactNode } from 'react';
import {
AutocompleteProps,
AutocompleteItem,
Expand Down Expand Up @@ -58,13 +58,15 @@ function isTextField(child?: ReactNode): child is ReactElement {
return child.type === TextField || componentName === 'TextField';
}

export function getTextFieldChild(children: ReactNode): ReactElement<any> | undefined {
let textField: ReactElement<any> | undefined;
Children.forEach(children, (child) => {
export function getTextFieldChild(children: ReactNode): ReactElement | undefined {
const all = Array.isArray(children) ? children : [children];

let textField;
for (const child of all) {
if (isTextField(child)) {
textField = child;
}
});
}
return textField;
}

Expand Down