Skip to content

Commit

Permalink
Added getActiveLabel prop to SelectField (#855)
Browse files Browse the repository at this point in the history
  • Loading branch information
gamtiq authored and mlaursen committed Dec 13, 2019
1 parent d17c1ec commit 4cc260f
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 8 deletions.
5 changes: 5 additions & 0 deletions docs/src/components/Components/SelectFields/Simple.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const OBJECT_ITEMS = [{

const icon = <SVGIcon use={arrowDropDown.url} />;

function getActiveLabel({ activeLabel, activeIndex }) {
return activeIndex > -1 ? `#${activeIndex + 1}: ${activeLabel}` : activeLabel;
}

const Simple = ({ simplifiedMenu }) => (
<div className="md-grid">
<h4 className="md-cell md-cell--12">Normal SelectFields</h4>
Expand Down Expand Up @@ -101,6 +105,7 @@ const Simple = ({ simplifiedMenu }) => (
placeholder="Strings disabled"
className="md-cell md-cell--bottom"
menuItems={STRING_ITEMS}
getActiveLabel={getActiveLabel}
disabled
dropdownIcon={icon}
simplifiedMenu={simplifiedMenu}
Expand Down
39 changes: 33 additions & 6 deletions src/js/SelectFields/SelectField.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,36 @@ import {
} from '../Helpers/Layover';
import { BaseMenuProps } from '../Menus/Menu';

export type MenuItem = number | string | Object | React.ReactElement<any>;
export type MenuItemList = Array<MenuItem>;

export type ListValue = number | string;

export interface GetItemPropsParam {
index: number;
active: boolean;
disabled: boolean;
itemValue: any;
value: ListValue;
props: Object;
item: number | string;
field: SelectFieldComponent;
}

export interface GetActiveLabelParam {
activeItem: MenuItem;
activeIndex: number;
activeLabel: string;
activeValue: any;
value: ListValue;
menuItems: MenuItemList;
field: SelectFieldComponent;
}

export interface FieldDataProps {
id: string;
name: string;
value: number | string;
value: ListValue;
}

export interface SharedSelectFieldProps extends BaseMenuProps, SharedTextFieldProps {
Expand All @@ -26,16 +52,17 @@ export interface SharedSelectFieldProps extends BaseMenuProps, SharedTextFieldPr
defaultVisible?: boolean;
visible?: boolean;
onVisibilityChange?: (visible: boolean, event: React.MouseEvent<HTMLElement>) => void;
menuItems?: Array<number | string | Object | React.ReactElement<any>>;
menuItems?: MenuItemList;
keyboardMatchingTimeout?: number;
itemLabel?: string;
itemValue?: string;
itemProps?: string;
name?: string;
getItemProps?: (data: Object) => Object;
defaultValue?: number | string;
value?: number | string;
onChange?: (value: number | string, selectedIndex: number, event: React.MouseEvent<HTMLElement>, data: FieldDataProps) => void;
getItemProps?: (data: GetItemPropsParam) => Object;
getActiveLabel?: (data: GetActiveLabelParam) => React.ReactNode;
defaultValue?: ListValue;
value?: ListValue;
onChange?: (value: ListValue, selectedIndex: number, event: React.MouseEvent<HTMLElement>, data: FieldDataProps) => void;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
dropdownIcon?: React.ReactElement<any>;
toolbar?: boolean;
Expand Down
34 changes: 32 additions & 2 deletions src/js/SelectFields/SelectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,21 @@ export default class SelectField extends PureComponent {
*/
getItemProps: PropTypes.func,

/**
* An optional function to get a node that will be rendered as the label of active item.
* By default a string will be used that is retrieved from active item's data.
*
* An object with the following fields will be passed into the function:
* - `activeItem` - active item's data
* - `activeIndex` - active item's index
* - `activeLabel` - active item's label (that is used by default when `getActiveLabel` prop is not specified)
* - `activeValue` - active item's value
* - `value` - current list value
* - `menuItems` - value of `menuItems` prop
* - `field` - reference to the component instance
*/
getActiveLabel: PropTypes.func,

/**
* The default value to use for the select field. If this is set, it should either match
* one of the `number` or `string` in your `menuItems` list or be the empty string. If
Expand Down Expand Up @@ -992,7 +1007,7 @@ export default class SelectField extends PureComponent {
index: i,
active,
disabled,
itemValue,
itemValue: dataValue,
value,
props,
item,
Expand Down Expand Up @@ -1049,6 +1064,7 @@ export default class SelectField extends PureComponent {
itemValue,
itemProps,
getItemProps,
getActiveLabel,
defaultValue,
defaultVisible,
onClick,
Expand Down Expand Up @@ -1082,12 +1098,26 @@ export default class SelectField extends PureComponent {
listId = `${menuId}-options`;
}

const { listProps, active, activeLabel } = this.state;
const { listProps, active, activeIndex } = this.state;
const below = position === SelectField.Positions.BELOW;
const visible = typeof isOpen !== 'undefined' ? isOpen : getField(this.props, this.state, 'visible');
const value = getField(this.props, this.state, 'value');
const useSameWidth = typeof sameWidth !== 'undefined' ? sameWidth : below;

let { activeLabel } = this.state;
if (typeof getActiveLabel === 'function') {
const activeItem = activeIndex > -1 ? menuItems[activeIndex] : null;
activeLabel = getActiveLabel({
activeItem,
activeIndex,
activeLabel,
activeValue: activeItem ? this._getItemPart(activeItem, itemLabel, itemValue) : '',
value,
menuItems,
field: this,
});
}

const toggle = (
<SelectFieldToggle
{...props}
Expand Down
15 changes: 15 additions & 0 deletions src/js/SelectFields/__tests__/SelectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,21 @@ describe('SelectField', () => {
expect(items.at(2).prop('secondaryText')).toBe(lastText);
});

it('should render active item\'s label that is returned by specified function', () => {
function getLabel({ activeLabel, activeIndex, menuItems }) {
return activeIndex > -1 ? `${activeLabel} (${activeIndex + 1}/${menuItems.length})` : activeLabel;
}

const menuItems = ['a', 'b', 'c', 'd'];
const field = mount(<SelectField id="test" menuItems={menuItems} value="c" getActiveLabel={getLabel} onChange={jest.fn()} />);
let input = field.find(SelectFieldInput).first();
expect(input.text()).toContain(getLabel({ activeLabel: 'c', activeIndex: 2, menuItems }));

field.setProps({ value: 'a' });
input = field.find(SelectFieldInput).first();
expect(input.text()).toContain(getLabel({ activeLabel: 'a', activeIndex: 0, menuItems }));
});

it('should still have the correct label if the menuItems are defined as a list in the render and the parent component rerenders', () => {
let renderCount = 0;
class Test extends React.Component {
Expand Down

0 comments on commit 4cc260f

Please sign in to comment.