Skip to content

Commit ab0703a

Browse files
authored
feat(ui): select preselected values handling add
1 parent 1d7a341 commit ab0703a

16 files changed

+1231
-276
lines changed

Diff for: src/framework/ui/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export {
6969
SelectProps,
7070
SelectElement,
7171
SelectOption,
72+
KeyExtractorType,
7273
} from './select/select.component';
7374
export { SelectOptionType } from './select/selectOption.component';
7475
export {

Diff for: src/framework/ui/radio/radio.spec.tsx.snap

+16
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ exports[`@radio: matches snapshot default 1`] = `
138138
"color-basic-default": "$color-basic-200",
139139
"color-basic-disabled": "$color-basic-300",
140140
"color-basic-focus": "$color-basic-500",
141+
"color-basic-hover": "$color-basic-200",
141142
"color-basic-transparent-100": "rgba(143, 155, 179, 0.08)",
142143
"color-basic-transparent-200": "rgba(143, 155, 179, 0.16)",
143144
"color-basic-transparent-300": "rgba(143, 155, 179, 0.24)",
@@ -157,6 +158,7 @@ exports[`@radio: matches snapshot default 1`] = `
157158
"color-danger-default": "$color-danger-500",
158159
"color-danger-disabled": "$color-danger-300",
159160
"color-danger-focus": "$color-danger-700",
161+
"color-danger-hover": "$color-danger-400",
160162
"color-danger-transparent-100": "rgba(255, 61, 113, 0.08)",
161163
"color-danger-transparent-200": "rgba(255, 61, 113, 0.16)",
162164
"color-danger-transparent-300": "rgba(255, 61, 113, 0.24)",
@@ -176,6 +178,7 @@ exports[`@radio: matches snapshot default 1`] = `
176178
"color-info-default": "$color-info-500",
177179
"color-info-disabled": "$color-info-300",
178180
"color-info-focus": "$color-info-700",
181+
"color-info-hover": "$color-info-400",
179182
"color-info-transparent-100": "rgba(0, 149, 255, 0.08)",
180183
"color-info-transparent-200": "rgba(0, 149, 255, 0.16)",
181184
"color-info-transparent-300": "rgba(0, 149, 255, 0.24)",
@@ -195,6 +198,7 @@ exports[`@radio: matches snapshot default 1`] = `
195198
"color-primary-default": "$color-primary-500",
196199
"color-primary-disabled": "$color-primary-200",
197200
"color-primary-focus": "$color-primary-700",
201+
"color-primary-hover": "$color-primary-400",
198202
"color-primary-transparent-100": "rgba(51, 102, 255, 0.08)",
199203
"color-primary-transparent-200": "rgba(51, 102, 255, 0.16)",
200204
"color-primary-transparent-300": "rgba(51, 102, 255, 0.24)",
@@ -223,6 +227,7 @@ exports[`@radio: matches snapshot default 1`] = `
223227
"color-success-default": "$color-success-500",
224228
"color-success-disabled": "$color-success-200",
225229
"color-success-focus": "$color-success-700",
230+
"color-success-hover": "$color-success-400",
226231
"color-success-transparent-100": "rgba(0, 224, 150, 0.08)",
227232
"color-success-transparent-200": "rgba(0, 224, 150, 0.16)",
228233
"color-success-transparent-300": "rgba(0, 224, 150, 0.24)",
@@ -251,6 +256,7 @@ exports[`@radio: matches snapshot default 1`] = `
251256
"color-warning-default": "$color-warning-500",
252257
"color-warning-disabled": "$color-warning-300",
253258
"color-warning-focus": "$color-warning-700",
259+
"color-warning-hover": "$color-warning-400",
254260
"color-warning-transparent-100": "rgba(255, 170, 0, 0.08)",
255261
"color-warning-transparent-200": "rgba(255, 170, 0, 0.16)",
256262
"color-warning-transparent-300": "rgba(255, 170, 0, 0.24)",
@@ -269,20 +275,30 @@ exports[`@radio: matches snapshot default 1`] = `
269275
"text-danger-active-color": "$color-danger-active",
270276
"text-danger-color": "$color-danger-default",
271277
"text-danger-disabled-color": "$color-danger-disabled",
278+
"text-danger-focus-color": "$color-danger-focus",
279+
"text-danger-hover-color": "$color-danger-hover",
272280
"text-disabled-color": "$color-basic-500",
273281
"text-hint-color": "$color-basic-600",
274282
"text-info-active-color": "$color-info-active",
275283
"text-info-color": "$color-info-default",
276284
"text-info-disabled-color": "$color-info-disabled",
285+
"text-info-focus-color": "$color-info-focus",
286+
"text-info-hover-color": "$color-info-hover",
277287
"text-primary-active-color": "$color-primary-active",
278288
"text-primary-color": "$color-primary-default",
279289
"text-primary-disabled-color": "$color-primary-disabled",
290+
"text-primary-focus-color": "$color-primary-focus",
291+
"text-primary-hover-color": "$color-primary-hover",
280292
"text-success-active-color": "$color-success-active",
281293
"text-success-color": "$color-success-default",
282294
"text-success-disabled-color": "$color-success-disabled",
295+
"text-success-focus-color": "$color-success-focus",
296+
"text-success-hover-color": "$color-success-hover",
283297
"text-warning-active-color": "$color-warning-active",
284298
"text-warning-color": "$color-warning-default",
285299
"text-warning-disabled-color": "$color-warning-disabled",
300+
"text-warning-focus-color": "$color-warning-focus",
301+
"text-warning-hover-color": "$color-warning-hover",
286302
}
287303
}
288304
>

Diff for: src/framework/ui/select/select.component.tsx

+17-5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type IconProp = (style: ImageStyle, visible: boolean) => IconElement;
5757
type SelectChildren = [SelectOptionsListElement, TextElement, ControlElement];
5858

5959
export type SelectOption = Array<SelectOptionType> | SelectOptionType;
60+
export type KeyExtractorType = (item: SelectOptionType) => string;
6061

6162
const MEASURED_CONTROL_TAG: string = 'Control';
6263

@@ -74,6 +75,7 @@ interface ComponentProps {
7475
onSelect: (option: SelectOption, event?: GestureResponderEvent) => void;
7576
status?: string;
7677
renderItem?: (item: ListRenderItemInfo<SelectOptionType>) => React.ReactElement<any>;
78+
keyExtractor?: KeyExtractorType;
7779
}
7880

7981
export type SelectProps = StyledComponentProps & TouchableOpacityProps & ComponentProps;
@@ -85,7 +87,9 @@ interface State {
8587
}
8688

8789
/**
88-
* Styled `Select` component.
90+
* Styled `Select` component. By default, the MultiSelect compares the items by reference.
91+
* To specify a field from the data object which will be used for the comparison,
92+
* implement the `keyExtractor` property.
8993
*
9094
* @extends React.Component
9195
*
@@ -125,6 +129,8 @@ interface State {
125129
*
126130
* @property {StyleProp<TextStyle>} textStyle - Customizes text style.
127131
*
132+
* @property {KeyExtractorType} keyExtractor - Used to extract a unique key for a given item;
133+
*
128134
* @property TouchableOpacityProps
129135
*
130136
* @property StyledComponentProps
@@ -137,6 +143,10 @@ interface State {
137143
*
138144
* @overview-example SelectWithGroups
139145
*
146+
* @overview-example SelectMultiPreselectedInline
147+
*
148+
* @overview-example SelectMultiPreselectedReference
149+
*
140150
* @example SelectCustomIcon
141151
*
142152
* @example SelectInlineStyling
@@ -154,7 +164,7 @@ class SelectComponent extends React.Component<SelectProps, State> {
154164
optionsListWidth: 0,
155165
};
156166

157-
private strategy: SelectionStrategy;
167+
private strategy: SelectionStrategy<SelectOption>;
158168
private iconAnimation: Animated.Value;
159169

160170
constructor(props: SelectProps) {
@@ -202,10 +212,12 @@ class SelectComponent extends React.Component<SelectProps, State> {
202212
this.setState({ optionsListWidth: width });
203213
};
204214

205-
private createSelectionStrategy = (): SelectionStrategy => {
206-
const { multiSelect, selectedOption } = this.props;
215+
private createSelectionStrategy = (): SelectionStrategy<SelectOption> => {
216+
const { multiSelect, selectedOption, keyExtractor, data } = this.props;
207217

208-
return multiSelect ? new MultiSelectStrategy(selectedOption) : new SingleSelectStrategy(selectedOption);
218+
return multiSelect ?
219+
new MultiSelectStrategy(selectedOption, data, keyExtractor) :
220+
new SingleSelectStrategy(selectedOption, data, keyExtractor);
209221
};
210222

211223
private setVisibility = (): void => {

Diff for: src/framework/ui/select/select.spec.tsx

+77-4
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,16 @@ interface Props {
5757
labelStyle?: StyleType;
5858
placeholderStyle?: StyleType;
5959
controlStyle?: StyleType;
60+
preselectedRef?: SelectOption;
61+
preselectedInline?: SelectOption;
62+
preselectedMultiRef?: SelectOption;
63+
preselectedMultiInline?: SelectOption;
6064
onSelectPress?: () => void;
6165
onSelectPressIn?: () => void;
6266
onSelectPressOut?: () => void;
6367
onSelectLongPress?: () => void;
6468
onMultiSelectPress?: () => void;
69+
keyExtractor?: (item: SelectOptionType) => string;
6570
}
6671

6772
interface State {
@@ -71,9 +76,33 @@ interface State {
7176

7277
class TestApplication extends React.Component<Props, State> {
7378

79+
private getSelectedOption = (): SelectOption => {
80+
const { preselectedRef, preselectedInline } = this.props;
81+
82+
if (preselectedInline) {
83+
return preselectedInline;
84+
} else if (preselectedRef) {
85+
return preselectedRef;
86+
} else {
87+
return null;
88+
}
89+
};
90+
91+
private getSelectedOptionMulti = (): SelectOption => {
92+
const { preselectedMultiInline, preselectedMultiRef } = this.props;
93+
94+
if (preselectedMultiInline) {
95+
return preselectedMultiInline;
96+
} else if (preselectedMultiRef) {
97+
return preselectedMultiRef;
98+
} else {
99+
return [];
100+
}
101+
};
102+
74103
public state: State = {
75-
selectSelected: null,
76-
selectMultiSelected: [],
104+
selectSelected: this.getSelectedOption(),
105+
selectMultiSelected: this.getSelectedOptionMulti(),
77106
};
78107

79108
private onSelectSelect = (selectSelected: SelectOption): void => {
@@ -106,7 +135,7 @@ class TestApplication extends React.Component<Props, State> {
106135
onSelectPressIn,
107136
onSelectPressOut,
108137
onSelectLongPress,
109-
138+
keyExtractor,
110139
} = this.props;
111140

112141
return (
@@ -123,6 +152,7 @@ class TestApplication extends React.Component<Props, State> {
123152
onPressOut={onSelectPressOut}
124153
onLongPress={onSelectLongPress}
125154
onSelect={this.onSelectSelect}
155+
keyExtractor={keyExtractor}
126156
/>
127157
<Select
128158
disabled={multiSelectDisabled}
@@ -134,11 +164,11 @@ class TestApplication extends React.Component<Props, State> {
134164
icon={this.renderIcon}
135165
onPress={onMultiSelectPress}
136166
onSelect={this.onSelectMultiSelect}
167+
keyExtractor={keyExtractor}
137168
/>
138169
</ApplicationProvider>
139170
);
140171
}
141-
142172
}
143173

144174

@@ -285,4 +315,47 @@ describe('@ select component checks', () => {
285315
expect(placeholder).toBe(passedPlaceholder);
286316
});
287317

318+
it('* preselected item by reference objects equality checks', () => {
319+
const preselectedDefault: SelectOption = data[3];
320+
const preselectedMulti: SelectOption = [data[3], data[4]];
321+
const application: RenderAPI = render(
322+
<TestApplication
323+
preselectedRef={preselectedDefault}
324+
preselectedMultiRef={preselectedMulti}
325+
/>,
326+
);
327+
328+
const { selectedOption: defaultSelect } = application.getAllByType(TouchableOpacity)[0].props;
329+
const { selectedOption: multiSelect } = application.getAllByType(TouchableOpacity)[1].props;
330+
331+
expect(defaultSelect).toEqual(preselectedDefault);
332+
expect(multiSelect).toEqual(preselectedMulti);
333+
});
334+
335+
it('* preselected item inline objects equality checks', () => {
336+
const preselectedMulti: SelectOption = [{ text: 'Option 4' }, { text: 'Option 6' }];
337+
const application: RenderAPI = render(
338+
<TestApplication
339+
preselectedMultiInline={preselectedMulti}
340+
keyExtractor={(item: SelectOptionType) => item.text}
341+
/>,
342+
);
343+
344+
fireEvent.press(application.getAllByType(TouchableOpacity)[1]);
345+
fireEvent(application.getAllByType(CheckBox)[6], 'onChange');
346+
347+
const { selectedOption: multiSelect } = application.getAllByType(TouchableOpacity)[1].props;
348+
349+
expect(multiSelect.length).toBe(1);
350+
expect(stringify(multiSelect)).toBe(stringify([{ text: 'Option 6' }]));
351+
});
352+
353+
it('* unexpected preselected', () => {
354+
const preselectedDefault: SelectOption = { text: 'Option 4545'};
355+
356+
expect(() => {
357+
render(<TestApplication preselectedInline={preselectedDefault}/>);
358+
}).toThrowError();
359+
});
360+
288361
});

Diff for: src/framework/ui/select/selectGroupOption.component.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ import {
2020
SelectOptionProps,
2121
SelectOptionType,
2222
} from './selectOption.component';
23+
import { SelectOption as SelectOptionProp} from './select.component';
2324
import { SelectionStrategy } from './selection.strategy';
2425

2526
interface ComponentProps {
2627
multiSelect?: boolean;
27-
strategy: SelectionStrategy;
28+
strategy: SelectionStrategy<SelectOptionProp>;
2829
renderItem?: (item: ListRenderItemInfo<SelectOptionType>) => React.ReactElement<any>;
2930
}
3031

Diff for: src/framework/ui/select/selectOption.component.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ class SelectOptionComponent extends React.Component<SelectOptionProps> {
8989
textFontWeight,
9090
textLineHeight,
9191
textMarginHorizontal,
92-
multiSelectBackgroundColor,
93-
multiSelectTextColor,
9492
...containerStyles
9593
} = source;
9694

@@ -124,7 +122,6 @@ class SelectOptionComponent extends React.Component<SelectOptionProps> {
124122
<TouchableOpacity
125123
activeOpacity={1.0}
126124
{...restProps}
127-
disabled={item.disabled}
128125
style={[styles.container, container, style]}
129126
onPress={this.onPress}
130127
onPressIn={this.onPressIn}
@@ -145,7 +142,7 @@ class SelectOptionComponent extends React.Component<SelectOptionProps> {
145142
style={[styles.container, container, style]}>
146143
<CheckBox
147144
text={item.text}
148-
textStyle={[text, item.textStyle]}
145+
textStyle={[text, item.textStyle, styles.multiSelectText]}
149146
disabled={disabled}
150147
checked={selected}
151148
indeterminate={indeterminate}
@@ -167,6 +164,9 @@ const styles = StyleSheet.create({
167164
},
168165
icon: {},
169166
text: {},
167+
multiSelectText: {
168+
width: '100%',
169+
},
170170
});
171171

172172
export const SelectOption = styled<SelectOptionProps>(SelectOptionComponent);

Diff for: src/framework/ui/select/selectOptionsList.component.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
SelectGroupOption,
2424
SelectGroupOptionElement,
2525
} from './selectGroupOption.component';
26+
import { SelectOption as SelectOptionProp} from './select.component';
2627
import { SelectionStrategy } from './selection.strategy';
2728

2829
type DefaultMenuItemElement = SelectOptionElement | SelectGroupOptionElement;
@@ -31,7 +32,7 @@ type MenuItemElement = DefaultMenuItemElement | React.ReactElement<any>;
3132
export interface ComponentProps {
3233
data: SelectOptionType[];
3334
multiSelect?: boolean;
34-
strategy: SelectionStrategy;
35+
strategy: SelectionStrategy<SelectOptionProp>;
3536
renderItem?: (item: ListRenderItemInfo<SelectOptionType>) => React.ReactElement<any>;
3637
onSelect: (option: SelectOptionType, event?: GestureResponderEvent) => void;
3738
}

0 commit comments

Comments
 (0)