Skip to content

Commit

Permalink
feat(search): update search to functional component (#10072)
Browse files Browse the repository at this point in the history
* feat(search): update search to functional component

* feat(search): remove test

* chore(search): remove ref

* chore(search): format

* chore(search): import skeleton

* test(search): add next search tests

* test(search): update tests

* test(search): update tests

* chore(search): test commit

* test(search): update tests

* test(search): update tests

* test(search): update tests

* test(search): reapply props from destructure, update tests

* refactor(search): update tests and implementation

* fix(search): stop propagation from escape keydown event

Co-authored-by: Taylor Jones <[email protected]>
Co-authored-by: Josh Black <[email protected]>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Dec 7, 2021
1 parent ab061bd commit c4b8986
Show file tree
Hide file tree
Showing 4 changed files with 552 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,3 @@ describe('ModalHeader', () => {
expect(container.firstChild).toHaveTextContent('Carbon label');
});
});

// TODO: write tests for composed modal
// TODO: write tests for modal body
// TODO: write tests for modal footer
12 changes: 10 additions & 2 deletions packages/react/src/components/Search/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
/**
* Copyright IBM Corp. 2016, 2018
* Copyright IBM Corp. 2016, 2021
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import * as FeatureFlags from '@carbon/feature-flags';
import { default as SearchNext } from './next/Search';
import { default as SearchClassic } from './Search';

const Search = FeatureFlags.enabled('enable-v11-release')
? SearchNext
: SearchClassic;

export * from './Search.Skeleton';
export default from './Search';

export default Search;
281 changes: 281 additions & 0 deletions packages/react/src/components/Search/next/Search-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import { Search16, Close16 } from '@carbon/icons-react';
import Search from './Search';
import { mount, shallow } from 'enzyme';
import { settings } from 'carbon-components';

const { prefix } = settings;

describe('Search', () => {
describe('renders as expected', () => {
const wrapper = mount(
<Search
id="test"
className="extra-class"
label="Search Field"
labelText="testlabel"
/>
);

const label = wrapper.find('label');
const textInput = wrapper.find('input');
const container = wrapper.find(`.${prefix}--search`);

describe('container', () => {
it('should add extra classes that are passed via className', () => {
expect(container.hasClass('extra-class')).toEqual(true);
});
});

describe('input', () => {
it('renders as expected', () => {
expect(textInput.length).toBe(1);
});

it('has the expected classes', () => {
expect(textInput.hasClass(`${prefix}--search-input`)).toEqual(true);
});

it('should set type as expected', () => {
expect(textInput.props().type).toEqual('text');
wrapper.setProps({ type: 'email' });
expect(wrapper.find('input').props().type).toEqual('email');
});

it('should set value as expected', () => {
expect(textInput.props().defaultValue).toEqual(undefined);
wrapper.setProps({ defaultValue: 'test' });
expect(wrapper.find('input').props().defaultValue).toEqual('test');
expect(wrapper.find('input').props().value).toEqual(undefined);
});

it('should set placeholder as expected', () => {
expect(textInput.props().placeholder).toEqual('');
wrapper.setProps({ placeholder: 'Enter text' });
expect(wrapper.find('input').props().placeholder).toEqual('Enter text');
});
});

describe('label', () => {
it('renders a label', () => {
expect(label.length).toBe(1);
});

it('has the expected classes', () => {
expect(label.hasClass(`${prefix}--label`)).toEqual(true);
});

it('should set label as expected', () => {
expect(wrapper.props().label).toEqual('Search Field');
wrapper.setProps({ label: 'Email Input' });
expect(wrapper.props().label).toEqual('Email Input');
});
});

describe('Large Search', () => {
const large = mount(
<Search
id="test"
size="lg"
className="extra-class"
label="Search Field"
labelText="testlabel"
/>
);

const largeContainer = large.find(`.${prefix}--search`);

it('renders correct search icon', () => {
const icons = large.find(Search16);
expect(icons.length).toBe(1);
});

it('should have the expected large class', () => {
expect(largeContainer.hasClass(`${prefix}--search--lg`)).toEqual(true);
});

it('should only have 1 button (clear)', () => {
const btn = large.find('button');
expect(btn.length).toEqual(1);
});

it('renders two Icons', () => {
const iconTypes = [Search16, Close16];
const icons = large.findWhere((n) => iconTypes.includes(n.type()));
expect(icons.length).toEqual(2);
});

describe('buttons', () => {
const btns = wrapper.find('button');

it('should be one button', () => {
expect(btns.length).toBe(1);
});

it('should have type="button"', () => {
const type1 = btns.first().instance().getAttribute('type');
const type2 = btns.last().instance().getAttribute('type');
expect(type1).toEqual('button');
expect(type2).toEqual('button');
});
});

describe('icons', () => {
it('renders "search" icon', () => {
const icons = wrapper.find(Search16);
expect(icons.length).toBe(1);
});

it('renders two Icons', () => {
wrapper.setProps({ size: undefined });
const iconTypes = [Search16, Close16];
const icons = wrapper.findWhere((n) => iconTypes.includes(n.type()));
expect(icons.length).toEqual(2);
});
});
});

describe('Small Search', () => {
const small = mount(
<Search
id="test"
size="sm"
className="extra-class"
label="Search Field"
labelText="testlabel"
/>
);

const smallContainer = small.find(`.${prefix}--search`);

it('renders correct search icon', () => {
const icons = small.find(Search16);
expect(icons.length).toBe(1);
});

it('should have the expected small class', () => {
expect(smallContainer.hasClass(`${prefix}--search--sm`)).toEqual(true);
});

it('should only have 1 button (clear)', () => {
const btn = small.find('button');
expect(btn.length).toEqual(1);
});

it('renders two Icons', () => {
const iconTypes = [Search16, Close16];
const icons = wrapper.findWhere((n) => iconTypes.includes(n.type()));
expect(icons.length).toEqual(2);
});
});
});

describe('events', () => {
describe('enabled textinput', () => {
const onClick = jest.fn();
const onChange = jest.fn();
const onClear = jest.fn();

const wrapper = shallow(
<Search
id="test"
labelText="testlabel"
onClick={onClick}
onChange={onChange}
onClear={onClear}
/>
);

const input = wrapper.find('input');
const eventObject = {
target: {
defaultValue: 'test',
},
};

it('should invoke onClick when input is clicked', () => {
input.simulate('click');
expect(onClick).toHaveBeenCalled();
});

it('should invoke onChange when input value is changed', () => {
input.simulate('change', eventObject);
expect(onChange).toHaveBeenCalledWith(eventObject);
});

it('should call onClear when input value is cleared', () => {
const node = document.createElement('div');
document.body.appendChild(node);

const wrapper = mount(
<Search
id="test"
labelText="testlabel"
onClear={onClear}
value="test"
/>,
{
attachTo: node,
}
);

wrapper.find('button').simulate('click', {
target: {
value: 'test',
},
});
expect(onClear).toHaveBeenCalled();
expect(wrapper.find('input').getDOMNode()).toHaveFocus();

document.body.removeChild(node);
});
});
});
});

/**
* Find the <input> element.
* @param {Enzymecontainer} wrapper
* @returns {Enzymecontainer}
*/
const getInput = (wrapper) => {
return wrapper.find(`.${prefix}--search-input`);
};

/**
* Find the value of the <input> element
* @param {EnzymeWrapper} wrapper
* @returns {number}
*/
const getInputValue = (wrapper) => {
return getInput(wrapper).prop('value');
};

describe('Detecting change in value from props', () => {
it('should have empty value', () => {
const search = shallow(
<Search id="test" className="extra-class" labelText="testlabel" />
);
expect(getInputValue(search)).toBe(undefined);
});

it('should set value if value prop is added', () => {
const search = shallow(
<Search
id="test"
className="extra-class"
labelText="testlabel"
value="foo"
/>
);

expect(getInputValue(search)).toBe('foo');
});
});
Loading

0 comments on commit c4b8986

Please sign in to comment.