Skip to content

Commit

Permalink
matching_options converted to TS+tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Theo committed Apr 12, 2019
1 parent ce003eb commit 8343f4a
Show file tree
Hide file tree
Showing 2 changed files with 276 additions and 0 deletions.
171 changes: 171 additions & 0 deletions src/components/combo_box/matching_options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import {
EuiComboBoxOption,
flattenOptionGroups,
getSelectedOptionForSearchValue,
getMatchingOptions,
} from './matching_options';

const options: EuiComboBoxOption[] = [
{
label: 'Titan',
'data-test-subj': 'titanOption',
},
{
label: 'Saturn',
'data-test-subj': 'saturnOption',
},
{
label: 'Mimas',
},
];

describe('flattenOptionGroups', () => {
test('it flattens one level of options', () => {
// Assemble
const input = [
{
label: 'Titan',
'data-test-subj': 'titanOption',
},
{
label: 'Enceladus',
options: [
{
label: 'Saturn',
'data-test-subj': 'saturnOption',
},
],
},
{
label: 'Mimas',
},
];
const expected = options;
// Act
const got = flattenOptionGroups(input);
// Assert
expect(got).toMatchObject(expected);
});
});

describe('getSelectedOptionForSearchValue', () => {
test('gets the first matching selected option for search value', () => {
// Assemble
const expected: EuiComboBoxOption = {
label: 'Saturn',
'data-test-subj': 'saturnOption',
};
// Act
const got = getSelectedOptionForSearchValue('saturn', options);
// Assert
expect(got).toMatchObject(expected);
});
});

describe('getSelectedOptionForSearchValue', () => {
test('returns undefined when no matching option found for search value', () => {
// Act
const got = getSelectedOptionForSearchValue('Pluto', options);
// Assert
expect(got).toBeUndefined();
});
test('gets the first matching selected option for search value', () => {
// Assemble
const expected: EuiComboBoxOption = {
label: 'Saturn',
'data-test-subj': 'saturnOption',
};
// Act
const got = getSelectedOptionForSearchValue('saturn', options);
// Assert
expect(got).toMatchObject(expected);
});
});

interface GetMatchingOptionsTestCase {
options: EuiComboBoxOption[];
selectedOptions: EuiComboBoxOption[];
searchValue: string;
isPreFiltered: boolean;
showPrevSelected: boolean;
expected: EuiComboBoxOption[];
}

const testCases: GetMatchingOptionsTestCase[] = [
{
options,
selectedOptions: [
{
label: 'Saturn',
'data-test-subj': 'saturnOption',
},
],
searchValue: 'saturn',
isPreFiltered: false,
showPrevSelected: false,
expected: [],
},
{
options,
selectedOptions: [
{
label: 'Saturn',
'data-test-subj': 'saturnOption',
},
],
searchValue: 'saturn',
isPreFiltered: true,
showPrevSelected: false,
expected: [
{ 'data-test-subj': 'titanOption', label: 'Titan' },
{ label: 'Mimas' },
],
},
{
options,
selectedOptions: [
{
label: 'Saturn',
'data-test-subj': 'saturnOption',
},
],
searchValue: 'saturn',
isPreFiltered: false,
showPrevSelected: true,
expected: [{ 'data-test-subj': 'saturnOption', label: 'Saturn' }],
},
{
options,
selectedOptions: [
{
label: 'Saturn',
'data-test-subj': 'saturnOption',
},
],
searchValue: 'saturn',
isPreFiltered: true,
showPrevSelected: true,
expected: [
{ 'data-test-subj': 'titanOption', label: 'Titan' },
{ 'data-test-subj': 'saturnOption', label: 'Saturn' },
{ label: 'Mimas' },
],
},
];

describe('getMatchingOptions', () => {
test.each(testCases)(
'.getMatchingOptions(%o)',
(testCase: GetMatchingOptionsTestCase) => {
expect(
getMatchingOptions(
testCase.options,
testCase.selectedOptions,
testCase.searchValue,
testCase.isPreFiltered,
testCase.showPrevSelected
)
).toMatchObject(testCase.expected);
}
);
});
105 changes: 105 additions & 0 deletions src/components/combo_box/matching_options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
export interface EuiComboBoxOption {
label: string;
[prop: string]: any;
}

export const flattenOptionGroups = (optionsOrGroups: EuiComboBoxOption[]) => {
return optionsOrGroups.reduce(
(options: EuiComboBoxOption[], optionOrGroup: EuiComboBoxOption) => {
if (optionOrGroup.options) {
options.push(...optionOrGroup.options);
} else {
options.push(optionOrGroup);
}
return options;
},
[]
);
};

export const getSelectedOptionForSearchValue = (
searchValue: string,
selectedOptions: EuiComboBoxOption[]
) => {
const normalizedSearchValue = searchValue.toLowerCase();
return selectedOptions.find(
(option: any) => option.label.toLowerCase() === normalizedSearchValue
);
};

const collectMatchingOption = (
accumulator: EuiComboBoxOption[],
option: EuiComboBoxOption,
selectedOptions: EuiComboBoxOption[],
normalizedSearchValue: string,
isPreFiltered: boolean,
showPrevSelected: boolean
) => {
// Only show options which haven't yet been selected unless requested.
const selectedOption = getSelectedOptionForSearchValue(
option.label,
selectedOptions
);
if (selectedOption && !showPrevSelected) {
return false;
}

// If the options have already been pre-filtered then we can skip filtering against the search value.
if (isPreFiltered) {
accumulator.push(option);
return;
}

if (!normalizedSearchValue) {
accumulator.push(option);
return;
}

const normalizedOption = option.label.trim().toLowerCase();
if (normalizedOption.includes(normalizedSearchValue)) {
accumulator.push(option);
}
};

export const getMatchingOptions = (
options: EuiComboBoxOption[],
selectedOptions: EuiComboBoxOption[],
searchValue: string,
isPreFiltered: boolean,
showPrevSelected: boolean
) => {
const normalizedSearchValue = searchValue.trim().toLowerCase();
const matchingOptions: EuiComboBoxOption[] = [];

options.forEach(option => {
if (option.options) {
const matchingOptionsForGroup: EuiComboBoxOption[] = [];
option.options.forEach((groupOption: EuiComboBoxOption) => {
collectMatchingOption(
matchingOptionsForGroup,
groupOption,
selectedOptions,
normalizedSearchValue,
isPreFiltered,
showPrevSelected
);
});
if (matchingOptionsForGroup.length > 0) {
// Add option for group label
matchingOptions.push({ label: option.label, isGroupLabelOption: true });
// Add matching options for group
matchingOptions.push(...matchingOptionsForGroup);
}
} else {
collectMatchingOption(
matchingOptions,
option,
selectedOptions,
normalizedSearchValue,
isPreFiltered,
showPrevSelected
);
}
});
return matchingOptions;
};

0 comments on commit 8343f4a

Please sign in to comment.