From 4cc260f3521d7ffd6c78ac2675879c53a00c1783 Mon Sep 17 00:00:00 2001 From: Denis Sikuler Date: Sat, 14 Dec 2019 02:14:57 +0300 Subject: [PATCH] Added getActiveLabel prop to SelectField (#855) --- .../Components/SelectFields/Simple.jsx | 5 +++ src/js/SelectFields/SelectField.d.ts | 39 ++++++++++++++++--- src/js/SelectFields/SelectField.js | 34 +++++++++++++++- src/js/SelectFields/__tests__/SelectField.js | 15 +++++++ 4 files changed, 85 insertions(+), 8 deletions(-) diff --git a/docs/src/components/Components/SelectFields/Simple.jsx b/docs/src/components/Components/SelectFields/Simple.jsx index 984498134c..021d10457f 100644 --- a/docs/src/components/Components/SelectFields/Simple.jsx +++ b/docs/src/components/Components/SelectFields/Simple.jsx @@ -25,6 +25,10 @@ const OBJECT_ITEMS = [{ const icon = ; +function getActiveLabel({ activeLabel, activeIndex }) { + return activeIndex > -1 ? `#${activeIndex + 1}: ${activeLabel}` : activeLabel; +} + const Simple = ({ simplifiedMenu }) => (

Normal SelectFields

@@ -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} diff --git a/src/js/SelectFields/SelectField.d.ts b/src/js/SelectFields/SelectField.d.ts index 2d75a4ef16..25960c1a61 100644 --- a/src/js/SelectFields/SelectField.d.ts +++ b/src/js/SelectFields/SelectField.d.ts @@ -9,10 +9,36 @@ import { } from '../Helpers/Layover'; import { BaseMenuProps } from '../Menus/Menu'; +export type MenuItem = number | string | Object | React.ReactElement; +export type MenuItemList = Array; + +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 { @@ -26,16 +52,17 @@ export interface SharedSelectFieldProps extends BaseMenuProps, SharedTextFieldPr defaultVisible?: boolean; visible?: boolean; onVisibilityChange?: (visible: boolean, event: React.MouseEvent) => void; - menuItems?: Array>; + 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, data: FieldDataProps) => void; + getItemProps?: (data: GetItemPropsParam) => Object; + getActiveLabel?: (data: GetActiveLabelParam) => React.ReactNode; + defaultValue?: ListValue; + value?: ListValue; + onChange?: (value: ListValue, selectedIndex: number, event: React.MouseEvent, data: FieldDataProps) => void; onClick?: (event: React.MouseEvent) => void; dropdownIcon?: React.ReactElement; toolbar?: boolean; diff --git a/src/js/SelectFields/SelectField.js b/src/js/SelectFields/SelectField.js index 197071fb51..8cc949dd34 100644 --- a/src/js/SelectFields/SelectField.js +++ b/src/js/SelectFields/SelectField.js @@ -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 @@ -992,7 +1007,7 @@ export default class SelectField extends PureComponent { index: i, active, disabled, - itemValue, + itemValue: dataValue, value, props, item, @@ -1049,6 +1064,7 @@ export default class SelectField extends PureComponent { itemValue, itemProps, getItemProps, + getActiveLabel, defaultValue, defaultVisible, onClick, @@ -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 = ( { 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(); + 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 {