-
Notifications
You must be signed in to change notification settings - Fork 350
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
1,745 additions
and
0 deletions.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
packages/patternfly-4/react-core/src/components/Select/Select.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { HTMLProps, FormEvent } from 'react'; | ||
|
||
export interface SelectProps extends HTMLProps<HTMLOptionElement> { | ||
isExpanded?: boolean; | ||
onToggle(value: boolean): void; | ||
placeholderText?: string; | ||
selectOptions?: ReactNode[]; | ||
selections?: string; | ||
variant?: string; | ||
width?: string | number; | ||
} | ||
|
||
declare const Select: React.FunctionComponent<SelectProps>; | ||
|
||
export default Select; |
11 changes: 11 additions & 0 deletions
11
packages/patternfly-4/react-core/src/components/Select/Select.docs.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Select, SelectOption } from '@patternfly/react-core'; | ||
import SingleSelectInput from './examples/SingleSelectInput'; | ||
|
||
export default { | ||
title: 'Select', | ||
components: { | ||
Select, | ||
SelectOption | ||
}, | ||
examples: [{ component: SingleSelectInput, title: 'Single Select Input' }] | ||
}; |
119 changes: 119 additions & 0 deletions
119
packages/patternfly-4/react-core/src/components/Select/Select.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import React from 'react'; | ||
import styles from '@patternfly/patternfly/components/Select/select.css'; | ||
import { css } from '@patternfly/react-styles'; | ||
import PropTypes from 'prop-types'; | ||
import SingleSelect from './SingleSelect'; | ||
import SelectToggle from './SelectToggle'; | ||
import { SelectContext } from './selectConstants'; | ||
|
||
// seed for the aria-labelledby ID | ||
let currentId = 0; | ||
|
||
const propTypes = { | ||
/** Content rendered inside the Select */ | ||
children: PropTypes.node, | ||
/** Classes applied to the root of the Select */ | ||
className: PropTypes.string, | ||
/** Flag to indicate if select is expanded */ | ||
isExpanded: PropTypes.bool, | ||
/** Placeholder text of Select */ | ||
placeholderText: PropTypes.string, | ||
/** Array of SelectOption nodes that will be rendered */ | ||
selectOptions: PropTypes.array, | ||
/** Selected item(s) structure */ | ||
selections: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), | ||
/** Callback for selection behavior */ | ||
onSelect: PropTypes.func.isRequired, | ||
/** Callback for toggle button behavior */ | ||
onToggle: PropTypes.func.isRequired, | ||
/** Variant of rendered Select */ | ||
variant: PropTypes.oneOf(['single']), | ||
/** Width of the select container as a number of px or string percentage */ | ||
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | ||
/** Additional props are spread to the container <ul> */ | ||
'': PropTypes.any | ||
}; | ||
|
||
const defaultProps = { | ||
children: null, | ||
className: '', | ||
isExpanded: false, | ||
selectOptions: null, | ||
selections: null, | ||
placeholderText: null, | ||
variant: 'single', | ||
width: '100%' | ||
}; | ||
|
||
class Select extends React.Component { | ||
parentRef = React.createRef(); | ||
state = { openedOnEnter: false }; | ||
|
||
onEnter = () => { | ||
this.setState({ openedOnEnter: true }); | ||
}; | ||
|
||
onClose = () => { | ||
this.setState({ openedOnEnter: false }); | ||
}; | ||
|
||
render() { | ||
const { | ||
children, | ||
className, | ||
variant, | ||
onToggle, | ||
onSelect, | ||
isExpanded, | ||
selectOptions, | ||
selections, | ||
placeholderText, | ||
width, | ||
...props | ||
} = this.props; | ||
const { openedOnEnter } = this.state; | ||
const renderedChildren = children || selectOptions; | ||
let childPlaceholderText = null; | ||
if (!selections && !placeholderText) { | ||
const childPlaceholder = renderedChildren.filter(child => child.props.isPlaceholder === true); | ||
childPlaceholderText = | ||
(childPlaceholder[0] && childPlaceholder[0].props.value) || | ||
(renderedChildren[0] && renderedChildren[0].props.value); | ||
} | ||
|
||
return ( | ||
<div | ||
className={css(styles.select, isExpanded && styles.modifiers.expanded, className)} | ||
ref={this.parentRef} | ||
style={{ width }} | ||
> | ||
<SelectContext.Provider value={onSelect}> | ||
{variant === 'single' && ( | ||
<React.Fragment> | ||
<SelectToggle | ||
id={`pf-toggle-id-${currentId++}`} | ||
parentRef={this.parentRef.current} | ||
isExpanded={isExpanded} | ||
onToggle={onToggle} | ||
onEnter={this.onEnter} | ||
onClose={this.onClose} | ||
> | ||
{selections || placeholderText || childPlaceholderText} | ||
</SelectToggle> | ||
{isExpanded && ( | ||
<SingleSelect {...props} selected={selections} openedOnEnter={openedOnEnter}> | ||
{renderedChildren} | ||
</SingleSelect> | ||
)} | ||
</React.Fragment> | ||
)} | ||
</SelectContext.Provider> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
Select.propTypes = propTypes; | ||
Select.defaultProps = defaultProps; | ||
|
||
export default Select; |
80 changes: 80 additions & 0 deletions
80
packages/patternfly-4/react-core/src/components/Select/Select.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import React from 'react'; | ||
import { mount } from 'enzyme'; | ||
import Select from './Select'; | ||
import SelectOption from './SelectOption'; | ||
|
||
const selectOptions = [ | ||
<SelectOption value="Mr" />, | ||
<SelectOption value="Mrs" />, | ||
<SelectOption value="Ms" />, | ||
<SelectOption value="Other" /> | ||
]; | ||
|
||
describe('select', () => { | ||
describe('single select', () => { | ||
test('renders closed successfully', () => { | ||
const view = mount(<Select variant="single">{selectOptions}</Select>); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
|
||
test('renders expanded successfully', () => { | ||
const view = mount( | ||
<Select variant="single" isExpanded> | ||
{selectOptions} | ||
</Select> | ||
); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
|
||
test('renders with selectOptions parameter', () => { | ||
const view = mount(<Select variant="single" selectOptions={selectOptions} />); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('API', () => { | ||
test('click on item', () => { | ||
const mockToggle = jest.fn(); | ||
const mockSelect = jest.fn(); | ||
const view = mount( | ||
<Select variant="single" onToggle={mockToggle} onSelect={mockSelect} selectOptions={selectOptions} isExpanded /> | ||
); | ||
view | ||
.find('button') | ||
.first() | ||
.simulate('click'); | ||
expect(mockToggle.mock.calls).toHaveLength(0); | ||
expect(mockSelect.mock.calls).toHaveLength(1); | ||
}); | ||
|
||
test('selectOptions and children console error ', () => { | ||
const myMock = jest.fn(); | ||
global.console = { error: myMock }; | ||
mount( | ||
<Select variant="single" selectOptions={selectOptions} isExpanded> | ||
<div> child test </div> | ||
<div> child test </div> | ||
</Select> | ||
); | ||
expect(myMock).toBeCalled(); | ||
}); | ||
|
||
test('selectOptions only, no console error ', () => { | ||
const myMock = jest.fn(); | ||
global.console = { error: myMock }; | ||
mount(<Select variant="single" selectOptions={selectOptions} isExpanded />); | ||
expect(myMock).not.toBeCalled(); | ||
}); | ||
|
||
test('children only, no console error', () => { | ||
const myMock = jest.fn(); | ||
global.console = { error: myMock }; | ||
mount( | ||
<Select variant="single" isExpanded> | ||
{selectOptions} | ||
</Select> | ||
); | ||
expect(myMock).not.toBeCalled(); | ||
}); | ||
}); |
15 changes: 15 additions & 0 deletions
15
packages/patternfly-4/react-core/src/components/Select/SelectOption.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { HTMLProps, FormEvent } from 'react'; | ||
import { Omit } from '../../typeUtils'; | ||
|
||
export interface SelectOptionProps extends Omit<HTMLProps<HTMLOptionElement>, 'disabled'> { | ||
value?: string; | ||
isValid?: boolean; | ||
isDisabled?: boolean; | ||
isPlaceholder?: boolean; | ||
onClick?: Function; | ||
sendRef?: Function; | ||
} | ||
|
||
declare const SelectOption: React.FunctionComponent<SelectOptionProps>; | ||
|
||
export default SelectOption; |
103 changes: 103 additions & 0 deletions
103
packages/patternfly-4/react-core/src/components/Select/SelectOption.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import React from 'react'; | ||
import styles from '@patternfly/patternfly/components/Select/select.css'; | ||
import { css } from '@patternfly/react-styles'; | ||
import PropTypes from 'prop-types'; | ||
import { SelectContext } from './selectConstants'; | ||
|
||
const propTypes = { | ||
/** the value for the option */ | ||
children: PropTypes.string, | ||
/** additional classes added to the Select Option */ | ||
className: PropTypes.string, | ||
/** the value for the option */ | ||
value: PropTypes.string, | ||
/** flag indicating if the option is disabled */ | ||
isDisabled: PropTypes.bool, | ||
/** flag indicating if the option acts as a placeholder */ | ||
isPlaceholder: PropTypes.bool, | ||
/** Optional on click callback */ | ||
onClick: PropTypes.func, | ||
/** Callback for ref tracking */ | ||
sendRef: PropTypes.func, | ||
/** Additional props are spread to the container <button> */ | ||
'': PropTypes.any | ||
}; | ||
|
||
const defaultProps = { | ||
children: null, | ||
className: '', | ||
value: null, | ||
isDisabled: false, | ||
isPlaceholder: false, | ||
onClick: Function.prototype | ||
}; | ||
|
||
class SelectOption extends React.Component { | ||
ref = React.createRef(); | ||
|
||
componentDidMount() { | ||
this.props.sendRef(this.ref, this.props.index); | ||
} | ||
|
||
onKeyDown = event => { | ||
if (event.key === 'Tab') return; | ||
event.preventDefault(); | ||
if (event.key === 'ArrowUp') { | ||
this.props.keyHandler(this.props.index, 'up'); | ||
} else if (event.key === 'ArrowDown') { | ||
this.props.keyHandler(this.props.index, 'down'); | ||
} else if (event.key === 'Enter') { | ||
this.ref.current.click && this.ref.current.click(); | ||
} | ||
}; | ||
|
||
render() { | ||
const { | ||
children, | ||
className, | ||
value, | ||
onClick, | ||
isDisabled, | ||
isPlaceholder, | ||
selected, | ||
sendRef, | ||
keyHandler, | ||
index, | ||
...props | ||
} = this.props; | ||
return ( | ||
<SelectContext.Consumer> | ||
{onSelect => ( | ||
<li> | ||
<button | ||
{...props} | ||
className={css( | ||
styles.selectMenuItem, | ||
selected && styles.selectMenuItemMatch, | ||
isDisabled && styles.modifiers.disabled, | ||
className | ||
)} | ||
onClick={event => { | ||
if (!isDisabled) { | ||
onClick && onClick(event); | ||
onSelect && onSelect(event, value || children, isPlaceholder); | ||
} | ||
}} | ||
role="option" | ||
aria-selected={selected || null} | ||
ref={this.ref} | ||
onKeyDown={this.onKeyDown} | ||
> | ||
{value || children} | ||
</button> | ||
</li> | ||
)} | ||
</SelectContext.Consumer> | ||
); | ||
} | ||
} | ||
|
||
SelectOption.propTypes = propTypes; | ||
SelectOption.defaultProps = defaultProps; | ||
|
||
export default SelectOption; |
29 changes: 29 additions & 0 deletions
29
packages/patternfly-4/react-core/src/components/Select/SelectOption.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
import SelectOption from './SelectOption'; | ||
|
||
describe('select options', () => { | ||
test('renders with value parameter successfully', () => { | ||
const view = shallow(<SelectOption value="test" sendRef={jest.fn()} />); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
|
||
test('renders with children successfully', () => { | ||
const view = shallow(<SelectOption sendRef={jest.fn()}>test</SelectOption>); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
|
||
describe('hover', () => { | ||
test('renders with hover successfully', () => { | ||
const view = shallow(<SelectOption isHovered value="test" sendRef={jest.fn()} />); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
}); | ||
|
||
describe('disabled', () => { | ||
test('renders disabled successfully', () => { | ||
const view = shallow(<SelectOption isDisabled value="test" sendRef={jest.fn()} />); | ||
expect(view).toMatchSnapshot(); | ||
}); | ||
}); | ||
}); |
19 changes: 19 additions & 0 deletions
19
packages/patternfly-4/react-core/src/components/Select/SelectToggle.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { HTMLProps, ReactType, ReactNode } from 'react'; | ||
|
||
export interface SelectToggleProps extends HTMLProps<HTMLButtonElement> { | ||
id?: string; | ||
children?: ReactNode; | ||
isExpanded?: boolean; | ||
onToggle?: Function; | ||
parentRef?: HTMLElement; | ||
isFocused?: boolean; | ||
isHovered?: boolean; | ||
isActive?: boolean; | ||
isPlain?: boolean; | ||
type?: string; | ||
iconComponent?: ReactType; | ||
} | ||
|
||
declare const SelectToggle: React.FunctionComponent<SelectToggleProps>; | ||
|
||
export default SelectToggle; |
Oops, something went wrong.