diff --git a/public/app/core/components/FilterInput/FilterInput.tsx b/public/app/core/components/FilterInput/FilterInput.tsx new file mode 100644 index 00000000000..b69f7d9651d --- /dev/null +++ b/public/app/core/components/FilterInput/FilterInput.tsx @@ -0,0 +1,51 @@ +import React, { forwardRef } from 'react'; + +const specialChars = ['(', '[', '{', '}', ']', ')', '|', '*', '+', '-', '.', '?', '<', '>', '#', '&', '^', '$']; + +export const escapeStringForRegex = (value: string) => { + if (!value) { + return value; + } + + const newValue = specialChars.reduce( + (escaped, currentChar) => escaped.replace(currentChar, '\\' + currentChar), + value + ); + + return newValue; +}; + +export const unEscapeStringFromRegex = (value: string) => { + if (!value) { + return value; + } + + const newValue = specialChars.reduce( + (escaped, currentChar) => escaped.replace('\\' + currentChar, currentChar), + value + ); + + return newValue; +}; + +export interface Props { + value: string | undefined; + placeholder?: string; + labelClassName?: string; + inputClassName?: string; + onChange: (value: string) => void; +} + +export const FilterInput = forwardRef((props, ref) => ( + +)); diff --git a/public/app/core/components/OrgActionBar/OrgActionBar.tsx b/public/app/core/components/OrgActionBar/OrgActionBar.tsx index b6b2046736f..f83d5c957f7 100644 --- a/public/app/core/components/OrgActionBar/OrgActionBar.tsx +++ b/public/app/core/components/OrgActionBar/OrgActionBar.tsx @@ -1,5 +1,6 @@ import React, { PureComponent } from 'react'; import LayoutSelector, { LayoutMode } from '../LayoutSelector/LayoutSelector'; +import { FilterInput } from '../FilterInput/FilterInput'; export interface Props { searchQuery: string; @@ -22,16 +23,13 @@ export default class OrgActionBar extends PureComponent { return (
- + onSetLayoutMode(mode)} />
diff --git a/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap b/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap index 25de037930a..3a75f9192b2 100644 --- a/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap +++ b/public/app/core/components/OrgActionBar/__snapshots__/OrgActionBar.test.tsx.snap @@ -7,20 +7,13 @@ exports[`Render should render component 1`] = `
- + diff --git a/public/app/core/components/TagFilter/TagFilter.tsx b/public/app/core/components/TagFilter/TagFilter.tsx index 7e8bc9c6fd2..967792a9f7b 100644 --- a/public/app/core/components/TagFilter/TagFilter.tsx +++ b/public/app/core/components/TagFilter/TagFilter.tsx @@ -5,6 +5,7 @@ import AsyncSelect from '@torkelo/react-select/lib/Async'; import { TagOption } from './TagOption'; import { TagBadge } from './TagBadge'; import { components } from '@torkelo/react-select'; +import { escapeStringForRegex } from '../FilterInput/FilterInput'; export interface Props { tags: string[]; @@ -51,7 +52,7 @@ export class TagFilter extends React.Component { value: tags, styles: resetSelectStyles(), filterOption: (option, searchQuery) => { - const regex = RegExp(searchQuery, 'i'); + const regex = RegExp(escapeStringForRegex(searchQuery), 'i'); return regex.test(option.value); }, components: { diff --git a/public/app/features/alerting/AlertRuleList.test.tsx b/public/app/features/alerting/AlertRuleList.test.tsx index ce3b8d47142..e1db5c77839 100644 --- a/public/app/features/alerting/AlertRuleList.test.tsx +++ b/public/app/features/alerting/AlertRuleList.test.tsx @@ -18,7 +18,7 @@ const setup = (propOverrides?: object) => { togglePauseAlertRule: jest.fn(), stateFilter: '', search: '', - isLoading: false + isLoading: false, }; Object.assign(props, propOverrides); @@ -147,9 +147,8 @@ describe('Functions', () => { describe('Search query change', () => { it('should set search query', () => { const { instance } = setup(); - const mockEvent = { target: { value: 'dashboard' } } as React.ChangeEvent; - instance.onSearchQueryChange(mockEvent); + instance.onSearchQueryChange('dashboard'); expect(instance.props.setSearchQuery).toHaveBeenCalledWith('dashboard'); }); diff --git a/public/app/features/alerting/AlertRuleList.tsx b/public/app/features/alerting/AlertRuleList.tsx index 3a16703ef54..d20c977273c 100644 --- a/public/app/features/alerting/AlertRuleList.tsx +++ b/public/app/features/alerting/AlertRuleList.tsx @@ -9,6 +9,7 @@ import { getNavModel } from 'app/core/selectors/navModel'; import { NavModel, StoreState, AlertRule } from 'app/types'; import { getAlertRulesAsync, setSearchQuery, togglePauseAlertRule } from './state/actions'; import { getAlertRuleItems, getSearchQuery } from './state/selectors'; +import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; export interface Props { navModel: NavModel; @@ -69,8 +70,7 @@ export class AlertRuleList extends PureComponent { }); }; - onSearchQueryChange = (evt: React.ChangeEvent) => { - const { value } = evt.target; + onSearchQueryChange = (value: string) => { this.props.setSearchQuery(value); }; @@ -78,7 +78,7 @@ export class AlertRuleList extends PureComponent { this.props.togglePauseAlertRule(rule.id, { paused: rule.state !== 'paused' }); }; - alertStateFilterOption = ({ text, value }: { text: string; value: string; }) => { + alertStateFilterOption = ({ text, value }: { text: string; value: string }) => { return (