Skip to content

Commit

Permalink
Created a general, yet Option specific version of matching_options
Browse files Browse the repository at this point in the history
  • Loading branch information
cchaos committed Mar 27, 2019
1 parent 5b91422 commit 3494c66
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 18 deletions.
82 changes: 82 additions & 0 deletions src/components/selectable/matching_options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Option } from './types';

export const getMatchingOptions = (
/**
* All available options to match against
*/
options: Option[],
/**
* String to match option.label against
*/
searchValue: string,
/**
* Async?
*/
isPreFiltered?: boolean,
/**
* To exclude selected options from the search list,
* pass the array of selected options
*/
selectedOptions?: Option[]
) => {
const normalizedSearchValue = searchValue.trim().toLowerCase();
const matchingOptions: Option[] = [];

options.forEach(option => {
collectMatchingOption(
matchingOptions,
option,
normalizedSearchValue,
isPreFiltered,
selectedOptions
);
});
return matchingOptions;
};

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

const collectMatchingOption = (
accumulator: Option[],
option: Option,
normalizedSearchValue: string,
isPreFiltered?: boolean,
selectedOptions?: Option[]
) => {
// Don't show options that have already been requested if
// the selectedOptions list exists
if (selectedOptions) {
const selectedOption = getSelectedOptionForSearchValue(
option.label,
selectedOptions
);
if (selectedOption) {
return false;
}
}

// If the options have already been prefiltered then we can skip filtering against the search value.
// TODO: I still don't quite understand how this works when hooked up to async
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);
}
};
8 changes: 3 additions & 5 deletions src/components/selectable/selectable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { EuiSelectableList } from './selectable_list';
// @ts-ignore
import { EuiLoadingChart } from '../loading';
// @ts-ignore
import { getMatchingOptions } from '../combo_box/matching_options';
import { getMatchingOptions } from './matching_options';
import { comboBoxKeyCodes } from '../../services';
import { TAB } from '../../services/key_codes';
import { EuiI18n } from '../i18n';
Expand Down Expand Up @@ -120,10 +120,8 @@ export class EuiSelectable extends Component<

const visibleOptions = getMatchingOptions(
options,
[],
initialSearchValue,
async,
true
async
);

// ensure that the currently selected single option is active if it is in the visibleOptions
Expand Down Expand Up @@ -235,7 +233,7 @@ export class EuiSelectable extends Component<
});
};

onSearchChange = (visibleOptions: [], searchValue: string) => {
onSearchChange = (visibleOptions: Option[], searchValue: string) => {
this.setState({
visibleOptions,
searchValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { EuiSelectableSearch } from './selectable_search';

describe('EuiSelectableSearch', () => {
test('is rendered', () => {
const component = render(<EuiSelectableSearch {...requiredProps} />);
const component = render(
<EuiSelectableSearch options={[]} {...requiredProps} />
);

expect(component).toMatchSnapshot();
});
Expand Down
18 changes: 6 additions & 12 deletions src/components/selectable/selectable_search/selectable_search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CommonProps, Omit } from '../../common';
// @ts-ignore
import { EuiFieldSearch } from '../../form/field_search';
// @ts-ignore
import { getMatchingOptions } from '../../combo_box/matching_options';
import { getMatchingOptions } from '../matching_options';
import { Option } from '../types';

export type EuiSelectableSearchProps = Omit<
Expand All @@ -15,8 +15,8 @@ export type EuiSelectableSearchProps = Omit<
/**
* Passes back (matchingOptions, searchValue)
*/
onChange?: (matchingOptions: [], searchValue: string) => void;
options?: Option[];
onChange?: (matchingOptions: Option[], searchValue: string) => void;
options: Option[];
async?: boolean;
defaultValue: string;
};
Expand Down Expand Up @@ -44,24 +44,18 @@ export class EuiSelectableSearch extends Component<
componentDidMount() {
const { options, async } = this.props;
const { searchValue } = this.state;
const matchingOptions = getMatchingOptions(
options,
[],
searchValue,
async,
true
);
const matchingOptions = getMatchingOptions(options, searchValue, async);
this.passUpMatches(matchingOptions, searchValue);
}

onSearchChange = (value: string) => {
this.setState({ searchValue: value });
const { options, async } = this.props;
const matchingOptions = getMatchingOptions(options, [], value, async, true);
const matchingOptions = getMatchingOptions(options, value, async);
this.passUpMatches(matchingOptions, value);
};

passUpMatches = (matches: [], searchValue: string) => {
passUpMatches = (matches: Option[], searchValue: string) => {
if (this.props.onChange) {
this.props.onChange(matches, searchValue);
}
Expand Down

0 comments on commit 3494c66

Please sign in to comment.