Commit 960fa560 by Torkel Ödegaard Committed by GitHub

Merge pull request #15382 from grafana/hugoh/bug-pressing-brackets-key-in-input-fields

Bug pressing special regexp chars in input fields
parents ef723643 90d162e6
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<HTMLInputElement, Props>((props, ref) => (
<label className={props.labelClassName}>
<input
ref={ref}
type="text"
className={props.inputClassName}
value={unEscapeStringFromRegex(props.value)}
onChange={event => props.onChange(escapeStringForRegex(event.target.value))}
placeholder={props.placeholder ? props.placeholder : null}
/>
<i className="gf-form-input-icon fa fa-search" />
</label>
));
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import LayoutSelector, { LayoutMode } from '../LayoutSelector/LayoutSelector'; import LayoutSelector, { LayoutMode } from '../LayoutSelector/LayoutSelector';
import { FilterInput } from '../FilterInput/FilterInput';
export interface Props { export interface Props {
searchQuery: string; searchQuery: string;
...@@ -22,16 +23,13 @@ export default class OrgActionBar extends PureComponent<Props> { ...@@ -22,16 +23,13 @@ export default class OrgActionBar extends PureComponent<Props> {
return ( return (
<div className="page-action-bar"> <div className="page-action-bar">
<div className="gf-form gf-form--grow"> <div className="gf-form gf-form--grow">
<label className="gf-form--has-input-icon"> <FilterInput
<input labelClassName="gf-form--has-input-icon"
type="text" inputClassName="gf-form-input width-20"
className="gf-form-input width-20"
value={searchQuery} value={searchQuery}
onChange={event => setSearchQuery(event.target.value)} onChange={setSearchQuery}
placeholder="Filter by name or type" placeholder={'Filter by name or type'}
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
<LayoutSelector mode={layoutMode} onLayoutModeChanged={(mode: LayoutMode) => onSetLayoutMode(mode)} /> <LayoutSelector mode={layoutMode} onLayoutModeChanged={(mode: LayoutMode) => onSetLayoutMode(mode)} />
</div> </div>
<div className="page-action-bar__spacer" /> <div className="page-action-bar__spacer" />
......
...@@ -7,20 +7,13 @@ exports[`Render should render component 1`] = ` ...@@ -7,20 +7,13 @@ exports[`Render should render component 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon" inputClassName="gf-form-input width-20"
> labelClassName="gf-form--has-input-icon"
<input onChange={[MockFunction]}
className="gf-form-input width-20"
onChange={[Function]}
placeholder="Filter by name or type" placeholder="Filter by name or type"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
<LayoutSelector <LayoutSelector
onLayoutModeChanged={[Function]} onLayoutModeChanged={[Function]}
/> />
......
...@@ -5,6 +5,7 @@ import AsyncSelect from '@torkelo/react-select/lib/Async'; ...@@ -5,6 +5,7 @@ import AsyncSelect from '@torkelo/react-select/lib/Async';
import { TagOption } from './TagOption'; import { TagOption } from './TagOption';
import { TagBadge } from './TagBadge'; import { TagBadge } from './TagBadge';
import { components } from '@torkelo/react-select'; import { components } from '@torkelo/react-select';
import { escapeStringForRegex } from '../FilterInput/FilterInput';
export interface Props { export interface Props {
tags: string[]; tags: string[];
...@@ -51,7 +52,7 @@ export class TagFilter extends React.Component<Props, any> { ...@@ -51,7 +52,7 @@ export class TagFilter extends React.Component<Props, any> {
value: tags, value: tags,
styles: resetSelectStyles(), styles: resetSelectStyles(),
filterOption: (option, searchQuery) => { filterOption: (option, searchQuery) => {
const regex = RegExp(searchQuery, 'i'); const regex = RegExp(escapeStringForRegex(searchQuery), 'i');
return regex.test(option.value); return regex.test(option.value);
}, },
components: { components: {
......
...@@ -18,7 +18,7 @@ const setup = (propOverrides?: object) => { ...@@ -18,7 +18,7 @@ const setup = (propOverrides?: object) => {
togglePauseAlertRule: jest.fn(), togglePauseAlertRule: jest.fn(),
stateFilter: '', stateFilter: '',
search: '', search: '',
isLoading: false isLoading: false,
}; };
Object.assign(props, propOverrides); Object.assign(props, propOverrides);
...@@ -147,9 +147,8 @@ describe('Functions', () => { ...@@ -147,9 +147,8 @@ describe('Functions', () => {
describe('Search query change', () => { describe('Search query change', () => {
it('should set search query', () => { it('should set search query', () => {
const { instance } = setup(); const { instance } = setup();
const mockEvent = { target: { value: 'dashboard' } } as React.ChangeEvent<HTMLInputElement>;
instance.onSearchQueryChange(mockEvent); instance.onSearchQueryChange('dashboard');
expect(instance.props.setSearchQuery).toHaveBeenCalledWith('dashboard'); expect(instance.props.setSearchQuery).toHaveBeenCalledWith('dashboard');
}); });
......
...@@ -9,6 +9,7 @@ import { getNavModel } from 'app/core/selectors/navModel'; ...@@ -9,6 +9,7 @@ import { getNavModel } from 'app/core/selectors/navModel';
import { NavModel, StoreState, AlertRule } from 'app/types'; import { NavModel, StoreState, AlertRule } from 'app/types';
import { getAlertRulesAsync, setSearchQuery, togglePauseAlertRule } from './state/actions'; import { getAlertRulesAsync, setSearchQuery, togglePauseAlertRule } from './state/actions';
import { getAlertRuleItems, getSearchQuery } from './state/selectors'; import { getAlertRuleItems, getSearchQuery } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
...@@ -69,8 +70,7 @@ export class AlertRuleList extends PureComponent<Props, any> { ...@@ -69,8 +70,7 @@ export class AlertRuleList extends PureComponent<Props, any> {
}); });
}; };
onSearchQueryChange = (evt: React.ChangeEvent<HTMLInputElement>) => { onSearchQueryChange = (value: string) => {
const { value } = evt.target;
this.props.setSearchQuery(value); this.props.setSearchQuery(value);
}; };
...@@ -78,7 +78,7 @@ export class AlertRuleList extends PureComponent<Props, any> { ...@@ -78,7 +78,7 @@ export class AlertRuleList extends PureComponent<Props, any> {
this.props.togglePauseAlertRule(rule.id, { paused: rule.state !== 'paused' }); this.props.togglePauseAlertRule(rule.id, { paused: rule.state !== 'paused' });
}; };
alertStateFilterOption = ({ text, value }: { text: string; value: string; }) => { alertStateFilterOption = ({ text, value }: { text: string; value: string }) => {
return ( return (
<option key={value} value={value}> <option key={value} value={value}>
{text} {text}
...@@ -94,16 +94,13 @@ export class AlertRuleList extends PureComponent<Props, any> { ...@@ -94,16 +94,13 @@ export class AlertRuleList extends PureComponent<Props, any> {
<Page.Contents isLoading={isLoading}> <Page.Contents isLoading={isLoading}>
<div className="page-action-bar"> <div className="page-action-bar">
<div className="gf-form gf-form--grow"> <div className="gf-form gf-form--grow">
<label className="gf-form--has-input-icon gf-form--grow"> <FilterInput
<input labelClassName="gf-form--has-input-icon gf-form--grow"
type="text" inputClassName="gf-form-input"
className="gf-form-input"
placeholder="Search alerts" placeholder="Search alerts"
value={search} value={search}
onChange={this.onSearchQueryChange} onChange={this.onSearchQueryChange}
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
</div> </div>
<div className="gf-form"> <div className="gf-form">
<label className="gf-form-label">States</label> <label className="gf-form-label">States</label>
...@@ -142,7 +139,7 @@ const mapStateToProps = (state: StoreState) => ({ ...@@ -142,7 +139,7 @@ const mapStateToProps = (state: StoreState) => ({
alertRules: getAlertRuleItems(state.alertRules), alertRules: getAlertRuleItems(state.alertRules),
stateFilter: state.location.query.state, stateFilter: state.location.query.state,
search: getSearchQuery(state.alertRules), search: getSearchQuery(state.alertRules),
isLoading: state.alertRules.isLoading isLoading: state.alertRules.isLoading,
}); });
const mapDispatchToProps = { const mapDispatchToProps = {
......
...@@ -13,20 +13,13 @@ exports[`Render should render alert rules 1`] = ` ...@@ -13,20 +13,13 @@ exports[`Render should render alert rules 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon gf-form--grow" inputClassName="gf-form-input"
> labelClassName="gf-form--has-input-icon gf-form--grow"
<input
className="gf-form-input"
onChange={[Function]} onChange={[Function]}
placeholder="Search alerts" placeholder="Search alerts"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
</div> </div>
<div <div
className="gf-form" className="gf-form"
...@@ -167,20 +160,13 @@ exports[`Render should render component 1`] = ` ...@@ -167,20 +160,13 @@ exports[`Render should render component 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon gf-form--grow" inputClassName="gf-form-input"
> labelClassName="gf-form--has-input-icon gf-form--grow"
<input
className="gf-form-input"
onChange={[Function]} onChange={[Function]}
placeholder="Search alerts" placeholder="Search alerts"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
</div> </div>
<div <div
className="gf-form" className="gf-form"
......
...@@ -8,11 +8,11 @@ const setup = (propOverrides?: object) => { ...@@ -8,11 +8,11 @@ const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
navModel: { navModel: {
main: { main: {
text: 'Configuration' text: 'Configuration',
}, },
node: { node: {
text: 'Api Keys' text: 'Api Keys',
} },
} as NavModel, } as NavModel,
apiKeys: [] as ApiKey[], apiKeys: [] as ApiKey[],
searchQuery: '', searchQuery: '',
...@@ -78,9 +78,8 @@ describe('Functions', () => { ...@@ -78,9 +78,8 @@ describe('Functions', () => {
describe('on search query change', () => { describe('on search query change', () => {
it('should call setSearchQuery', () => { it('should call setSearchQuery', () => {
const { instance } = setup(); const { instance } = setup();
const mockEvent = { target: { value: 'test' } };
instance.onSearchQueryChange(mockEvent); instance.onSearchQueryChange('test');
expect(instance.props.setSearchQuery).toHaveBeenCalledWith('test'); expect(instance.props.setSearchQuery).toHaveBeenCalledWith('test');
}); });
......
...@@ -13,6 +13,7 @@ import config from 'app/core/config'; ...@@ -13,6 +13,7 @@ import config from 'app/core/config';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import { DeleteButton } from '@grafana/ui'; import { DeleteButton } from '@grafana/ui';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
...@@ -59,8 +60,8 @@ export class ApiKeysPage extends PureComponent<Props, any> { ...@@ -59,8 +60,8 @@ export class ApiKeysPage extends PureComponent<Props, any> {
this.props.deleteApiKey(key.id); this.props.deleteApiKey(key.id);
} }
onSearchQueryChange = evt => { onSearchQueryChange = (value: string) => {
this.props.setSearchQuery(evt.target.value); this.props.setSearchQuery(value);
}; };
onToggleAdding = () => { onToggleAdding = () => {
...@@ -186,16 +187,13 @@ export class ApiKeysPage extends PureComponent<Props, any> { ...@@ -186,16 +187,13 @@ export class ApiKeysPage extends PureComponent<Props, any> {
<> <>
<div className="page-action-bar"> <div className="page-action-bar">
<div className="gf-form gf-form--grow"> <div className="gf-form gf-form--grow">
<label className="gf-form--has-input-icon gf-form--grow"> <FilterInput
<input labelClassName="gf-form--has-input-icon gf-form--grow"
type="text" inputClassName="gf-form-input"
className="gf-form-input"
placeholder="Search keys" placeholder="Search keys"
value={searchQuery} value={searchQuery}
onChange={this.onSearchQueryChange} onChange={this.onSearchQueryChange}
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
</div> </div>
<div className="page-action-bar__spacer" /> <div className="page-action-bar__spacer" />
...@@ -241,13 +239,7 @@ export class ApiKeysPage extends PureComponent<Props, any> { ...@@ -241,13 +239,7 @@ export class ApiKeysPage extends PureComponent<Props, any> {
return ( return (
<Page navModel={navModel}> <Page navModel={navModel}>
<Page.Contents isLoading={!hasFetched}> <Page.Contents isLoading={!hasFetched}>
{hasFetched && ( {hasFetched && (apiKeysCount > 0 ? this.renderApiKeyList() : this.renderEmptyList())}
apiKeysCount > 0 ? (
this.renderApiKeyList()
) : (
this.renderEmptyList()
)
)}
</Page.Contents> </Page.Contents>
</Page> </Page>
); );
......
...@@ -17,6 +17,7 @@ import { FadeIn } from 'app/core/components/Animations/FadeIn'; ...@@ -17,6 +17,7 @@ import { FadeIn } from 'app/core/components/Animations/FadeIn';
import { PanelModel } from '../state/PanelModel'; import { PanelModel } from '../state/PanelModel';
import { DashboardModel } from '../state/DashboardModel'; import { DashboardModel } from '../state/DashboardModel';
import { PanelPlugin } from 'app/types/plugins'; import { PanelPlugin } from 'app/types/plugins';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
interface Props { interface Props {
panel: PanelModel; panel: PanelModel;
...@@ -170,8 +171,7 @@ export class VisualizationTab extends PureComponent<Props, State> { ...@@ -170,8 +171,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
this.setState({ isVizPickerOpen: false }); this.setState({ isVizPickerOpen: false });
}; };
onSearchQueryChange = evt => { onSearchQueryChange = (value: string) => {
const value = evt.target.value;
this.setState({ this.setState({
searchQuery: value, searchQuery: value,
}); });
...@@ -184,17 +184,14 @@ export class VisualizationTab extends PureComponent<Props, State> { ...@@ -184,17 +184,14 @@ export class VisualizationTab extends PureComponent<Props, State> {
if (this.state.isVizPickerOpen) { if (this.state.isVizPickerOpen) {
return ( return (
<> <>
<label className="gf-form--has-input-icon"> <FilterInput
<input labelClassName="gf-form--has-input-icon"
type="text" inputClassName="gf-form-input width-13"
className="gf-form-input width-13"
placeholder="" placeholder=""
onChange={this.onSearchQueryChange} onChange={this.onSearchQueryChange}
value={searchQuery} value={searchQuery}
ref={elem => elem && elem.focus()} ref={elem => elem && elem.focus()}
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
<button className="btn btn-link toolbar__close" onClick={this.onCloseVizPicker}> <button className="btn btn-link toolbar__close" onClick={this.onCloseVizPicker}>
<i className="fa fa-chevron-up" /> <i className="fa fa-chevron-up" />
</button> </button>
......
...@@ -6,6 +6,7 @@ import { NavModel, Plugin, StoreState } from 'app/types'; ...@@ -6,6 +6,7 @@ import { NavModel, Plugin, StoreState } from 'app/types';
import { addDataSource, loadDataSourceTypes, setDataSourceTypeSearchQuery } from './state/actions'; import { addDataSource, loadDataSourceTypes, setDataSourceTypeSearchQuery } from './state/actions';
import { getNavModel } from 'app/core/selectors/navModel'; import { getNavModel } from 'app/core/selectors/navModel';
import { getDataSourceTypes } from './state/selectors'; import { getDataSourceTypes } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
...@@ -26,8 +27,8 @@ class NewDataSourcePage extends PureComponent<Props> { ...@@ -26,8 +27,8 @@ class NewDataSourcePage extends PureComponent<Props> {
this.props.addDataSource(plugin); this.props.addDataSource(plugin);
}; };
onSearchQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => { onSearchQueryChange = (value: string) => {
this.props.setDataSourceTypeSearchQuery(event.target.value); this.props.setDataSourceTypeSearchQuery(value);
}; };
render() { render() {
...@@ -37,16 +38,13 @@ class NewDataSourcePage extends PureComponent<Props> { ...@@ -37,16 +38,13 @@ class NewDataSourcePage extends PureComponent<Props> {
<Page.Contents isLoading={isLoading}> <Page.Contents isLoading={isLoading}>
<h2 className="add-data-source-header">Choose data source type</h2> <h2 className="add-data-source-header">Choose data source type</h2>
<div className="add-data-source-search"> <div className="add-data-source-search">
<label className="gf-form--has-input-icon"> <FilterInput
<input labelClassName="gf-form--has-input-icon"
type="text" inputClassName="gf-form-input width-20"
className="gf-form-input width-20"
value={dataSourceTypeSearchQuery} value={dataSourceTypeSearchQuery}
onChange={this.onSearchQueryChange} onChange={this.onSearchQueryChange}
placeholder="Filter by name or type" placeholder="Filter by name or type"
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
</div> </div>
<div className="add-data-source-grid"> <div className="add-data-source-grid">
{dataSourceTypes.map((plugin, index) => { {dataSourceTypes.map((plugin, index) => {
......
...@@ -5,13 +5,7 @@ import * as rangeUtil from 'app/core/utils/rangeutil'; ...@@ -5,13 +5,7 @@ import * as rangeUtil from 'app/core/utils/rangeutil';
import { RawTimeRange, Switch } from '@grafana/ui'; import { RawTimeRange, Switch } from '@grafana/ui';
import TimeSeries from 'app/core/time_series2'; import TimeSeries from 'app/core/time_series2';
import { import { LogsDedupDescription, LogsDedupStrategy, LogsModel, LogLevel, LogsMetaKind } from 'app/core/logs_model';
LogsDedupDescription,
LogsDedupStrategy,
LogsModel,
LogLevel,
LogsMetaKind,
} from 'app/core/logs_model';
import ToggleButtonGroup, { ToggleButton } from 'app/core/components/ToggleButtonGroup/ToggleButtonGroup'; import ToggleButtonGroup, { ToggleButton } from 'app/core/components/ToggleButtonGroup/ToggleButtonGroup';
......
...@@ -193,7 +193,7 @@ export interface ToggleLogsPayload { ...@@ -193,7 +193,7 @@ export interface ToggleLogsPayload {
exploreId: ExploreId; exploreId: ExploreId;
} }
export interface UpdateUIStatePayload extends Partial<ExploreUIState>{ export interface UpdateUIStatePayload extends Partial<ExploreUIState> {
exploreId: ExploreId; exploreId: ExploreId;
} }
...@@ -403,9 +403,7 @@ export const updateDatasourceInstanceAction = actionCreatorFactory<UpdateDatasou ...@@ -403,9 +403,7 @@ export const updateDatasourceInstanceAction = actionCreatorFactory<UpdateDatasou
'explore/UPDATE_DATASOURCE_INSTANCE' 'explore/UPDATE_DATASOURCE_INSTANCE'
).create(); ).create();
export const toggleLogLevelAction = actionCreatorFactory<ToggleLogLevelPayload>( export const toggleLogLevelAction = actionCreatorFactory<ToggleLogLevelPayload>('explore/TOGGLE_LOG_LEVEL').create();
'explore/TOGGLE_LOG_LEVEL'
).create();
/** /**
* Resets state for explore. * Resets state for explore.
......
...@@ -474,7 +474,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta ...@@ -474,7 +474,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
const { hiddenLogLevels } = action.payload; const { hiddenLogLevels } = action.payload;
return { return {
...state, ...state,
hiddenLogLevels: Array.from(hiddenLogLevels) hiddenLogLevels: Array.from(hiddenLogLevels),
}; };
}, },
}) })
......
...@@ -8,11 +8,11 @@ const setup = (propOverrides?: object) => { ...@@ -8,11 +8,11 @@ const setup = (propOverrides?: object) => {
const props: Props = { const props: Props = {
navModel: { navModel: {
main: { main: {
text: 'Configuration' text: 'Configuration',
}, },
node: { node: {
text: 'Team List' text: 'Team List',
} },
} as NavModel, } as NavModel,
teams: [] as Team[], teams: [] as Team[],
loadTeams: jest.fn(), loadTeams: jest.fn(),
...@@ -74,9 +74,8 @@ describe('Functions', () => { ...@@ -74,9 +74,8 @@ describe('Functions', () => {
describe('on search query change', () => { describe('on search query change', () => {
it('should call setSearchQuery', () => { it('should call setSearchQuery', () => {
const { instance } = setup(); const { instance } = setup();
const mockEvent = { target: { value: 'test' } };
instance.onSearchQueryChange(mockEvent); instance.onSearchQueryChange('test');
expect(instance.props.setSearchQuery).toHaveBeenCalledWith('test'); expect(instance.props.setSearchQuery).toHaveBeenCalledWith('test');
}); });
......
...@@ -8,6 +8,7 @@ import { NavModel, Team } from 'app/types'; ...@@ -8,6 +8,7 @@ import { NavModel, Team } from 'app/types';
import { loadTeams, deleteTeam, setSearchQuery } from './state/actions'; import { loadTeams, deleteTeam, setSearchQuery } from './state/actions';
import { getSearchQuery, getTeams, getTeamsCount } from './state/selectors'; import { getSearchQuery, getTeams, getTeamsCount } from './state/selectors';
import { getNavModel } from 'app/core/selectors/navModel'; import { getNavModel } from 'app/core/selectors/navModel';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
...@@ -33,8 +34,8 @@ export class TeamList extends PureComponent<Props, any> { ...@@ -33,8 +34,8 @@ export class TeamList extends PureComponent<Props, any> {
this.props.deleteTeam(team.id); this.props.deleteTeam(team.id);
}; };
onSearchQueryChange = event => { onSearchQueryChange = (value: string) => {
this.props.setSearchQuery(event.target.value); this.props.setSearchQuery(value);
}; };
renderTeam(team: Team) { renderTeam(team: Team) {
...@@ -89,16 +90,13 @@ export class TeamList extends PureComponent<Props, any> { ...@@ -89,16 +90,13 @@ export class TeamList extends PureComponent<Props, any> {
<> <>
<div className="page-action-bar"> <div className="page-action-bar">
<div className="gf-form gf-form--grow"> <div className="gf-form gf-form--grow">
<label className="gf-form--has-input-icon gf-form--grow"> <FilterInput
<input labelClassName="gf-form--has-input-icon gf-form--grow"
type="text" inputClassName="gf-form-input"
className="gf-form-input"
placeholder="Search teams" placeholder="Search teams"
value={searchQuery} value={searchQuery}
onChange={this.onSearchQueryChange} onChange={this.onSearchQueryChange}
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
</div> </div>
<div className="page-action-bar__spacer" /> <div className="page-action-bar__spacer" />
...@@ -141,9 +139,7 @@ export class TeamList extends PureComponent<Props, any> { ...@@ -141,9 +139,7 @@ export class TeamList extends PureComponent<Props, any> {
return ( return (
<Page navModel={navModel}> <Page navModel={navModel}>
<Page.Contents isLoading={!hasFetched}> <Page.Contents isLoading={!hasFetched}>{hasFetched && this.renderList()}</Page.Contents>
{hasFetched && this.renderList()}
</Page.Contents>
</Page> </Page>
); );
} }
......
...@@ -55,9 +55,8 @@ describe('Functions', () => { ...@@ -55,9 +55,8 @@ describe('Functions', () => {
describe('on search member query change', () => { describe('on search member query change', () => {
it('it should call setSearchMemberQuery', () => { it('it should call setSearchMemberQuery', () => {
const { instance } = setup(); const { instance } = setup();
const mockEvent = { target: { value: 'member' } };
instance.onSearchQueryChange(mockEvent); instance.onSearchQueryChange('member');
expect(instance.props.setSearchMemberQuery).toHaveBeenCalledWith('member'); expect(instance.props.setSearchMemberQuery).toHaveBeenCalledWith('member');
}); });
......
...@@ -7,6 +7,7 @@ import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; ...@@ -7,6 +7,7 @@ import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
import { TeamMember, User } from 'app/types'; import { TeamMember, User } from 'app/types';
import { loadTeamMembers, addTeamMember, removeTeamMember, setSearchMemberQuery } from './state/actions'; import { loadTeamMembers, addTeamMember, removeTeamMember, setSearchMemberQuery } from './state/actions';
import { getSearchMemberQuery, getTeamMembers } from './state/selectors'; import { getSearchMemberQuery, getTeamMembers } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props { export interface Props {
members: TeamMember[]; members: TeamMember[];
...@@ -33,8 +34,8 @@ export class TeamMembers extends PureComponent<Props, State> { ...@@ -33,8 +34,8 @@ export class TeamMembers extends PureComponent<Props, State> {
this.props.loadTeamMembers(); this.props.loadTeamMembers();
} }
onSearchQueryChange = event => { onSearchQueryChange = (value: string) => {
this.props.setSearchMemberQuery(event.target.value); this.props.setSearchMemberQuery(value);
}; };
onRemoveMember(member: TeamMember) { onRemoveMember(member: TeamMember) {
...@@ -89,16 +90,13 @@ export class TeamMembers extends PureComponent<Props, State> { ...@@ -89,16 +90,13 @@ export class TeamMembers extends PureComponent<Props, State> {
<div> <div>
<div className="page-action-bar"> <div className="page-action-bar">
<div className="gf-form gf-form--grow"> <div className="gf-form gf-form--grow">
<label className="gf-form--has-input-icon gf-form--grow"> <FilterInput
<input labelClassName="gf-form--has-input-icon gf-form--grow"
type="text" inputClassName="gf-form-input"
className="gf-form-input"
placeholder="Search members" placeholder="Search members"
value={searchMemberQuery} value={searchMemberQuery}
onChange={this.onSearchQueryChange} onChange={this.onSearchQueryChange}
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
</div> </div>
<div className="page-action-bar__spacer" /> <div className="page-action-bar__spacer" />
......
...@@ -41,20 +41,13 @@ exports[`Render should render teams table 1`] = ` ...@@ -41,20 +41,13 @@ exports[`Render should render teams table 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon gf-form--grow" inputClassName="gf-form-input"
> labelClassName="gf-form--has-input-icon gf-form--grow"
<input
className="gf-form-input"
onChange={[Function]} onChange={[Function]}
placeholder="Search teams" placeholder="Search teams"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
</div> </div>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
......
...@@ -8,20 +8,13 @@ exports[`Render should render component 1`] = ` ...@@ -8,20 +8,13 @@ exports[`Render should render component 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon gf-form--grow" inputClassName="gf-form-input"
> labelClassName="gf-form--has-input-icon gf-form--grow"
<input
className="gf-form-input"
onChange={[Function]} onChange={[Function]}
placeholder="Search members" placeholder="Search members"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
</div> </div>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
...@@ -102,20 +95,13 @@ exports[`Render should render team members 1`] = ` ...@@ -102,20 +95,13 @@ exports[`Render should render team members 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon gf-form--grow" inputClassName="gf-form-input"
> labelClassName="gf-form--has-input-icon gf-form--grow"
<input
className="gf-form-input"
onChange={[Function]} onChange={[Function]}
placeholder="Search members" placeholder="Search members"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
</div> </div>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
...@@ -322,20 +308,13 @@ exports[`Render should render team members when sync enabled 1`] = ` ...@@ -322,20 +308,13 @@ exports[`Render should render team members when sync enabled 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon gf-form--grow" inputClassName="gf-form-input"
> labelClassName="gf-form--has-input-icon gf-form--grow"
<input
className="gf-form-input"
onChange={[Function]} onChange={[Function]}
placeholder="Search members" placeholder="Search members"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
</div> </div>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
......
...@@ -3,6 +3,7 @@ import { connect } from 'react-redux'; ...@@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import classNames from 'classnames'; import classNames from 'classnames';
import { setUsersSearchQuery } from './state/actions'; import { setUsersSearchQuery } from './state/actions';
import { getInviteesCount, getUsersSearchQuery } from './state/selectors'; import { getInviteesCount, getUsersSearchQuery } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props { export interface Props {
searchQuery: string; searchQuery: string;
...@@ -43,16 +44,13 @@ export class UsersActionBar extends PureComponent<Props> { ...@@ -43,16 +44,13 @@ export class UsersActionBar extends PureComponent<Props> {
return ( return (
<div className="page-action-bar"> <div className="page-action-bar">
<div className="gf-form gf-form--grow"> <div className="gf-form gf-form--grow">
<label className="gf-form--has-input-icon"> <FilterInput
<input labelClassName="gf-form--has-input-icon"
type="text" inputClassName="gf-form-input width-20"
className="gf-form-input width-20"
value={searchQuery} value={searchQuery}
onChange={event => setUsersSearchQuery(event.target.value)} onChange={setUsersSearchQuery}
placeholder="Filter by name or type" placeholder="Filter by name or type"
/> />
<i className="gf-form-input-icon fa fa-search" />
</label>
{pendingInvitesCount > 0 && ( {pendingInvitesCount > 0 && (
<div style={{ marginLeft: '1rem' }}> <div style={{ marginLeft: '1rem' }}>
<button className={usersButtonStyle} key="users" onClick={onShowInvites}> <button className={usersButtonStyle} key="users" onClick={onShowInvites}>
......
...@@ -7,20 +7,13 @@ exports[`Render should render component 1`] = ` ...@@ -7,20 +7,13 @@ exports[`Render should render component 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon" inputClassName="gf-form-input width-20"
> labelClassName="gf-form--has-input-icon"
<input onChange={[MockFunction]}
className="gf-form-input width-20"
onChange={[Function]}
placeholder="Filter by name or type" placeholder="Filter by name or type"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
/> />
...@@ -35,20 +28,13 @@ exports[`Render should render pending invites button 1`] = ` ...@@ -35,20 +28,13 @@ exports[`Render should render pending invites button 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon" inputClassName="gf-form-input width-20"
> labelClassName="gf-form--has-input-icon"
<input onChange={[MockFunction]}
className="gf-form-input width-20"
onChange={[Function]}
placeholder="Filter by name or type" placeholder="Filter by name or type"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
<div <div
style={ style={
Object { Object {
...@@ -87,20 +73,13 @@ exports[`Render should show external user management button 1`] = ` ...@@ -87,20 +73,13 @@ exports[`Render should show external user management button 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon" inputClassName="gf-form-input width-20"
> labelClassName="gf-form--has-input-icon"
<input onChange={[MockFunction]}
className="gf-form-input width-20"
onChange={[Function]}
placeholder="Filter by name or type" placeholder="Filter by name or type"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
/> />
...@@ -125,20 +104,13 @@ exports[`Render should show invite button 1`] = ` ...@@ -125,20 +104,13 @@ exports[`Render should show invite button 1`] = `
<div <div
className="gf-form gf-form--grow" className="gf-form gf-form--grow"
> >
<label <ForwardRef
className="gf-form--has-input-icon" inputClassName="gf-form-input width-20"
> labelClassName="gf-form--has-input-icon"
<input onChange={[MockFunction]}
className="gf-form-input width-20"
onChange={[Function]}
placeholder="Filter by name or type" placeholder="Filter by name or type"
type="text"
value="" value=""
/> />
<i
className="gf-form-input-icon fa fa-search"
/>
</label>
<div <div
className="page-action-bar__spacer" className="page-action-bar__spacer"
/> />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment