fix(ui): revert filter background on click outside

This commit is contained in:
Łukasz Mierzwa
2019-11-29 23:23:00 +00:00
parent 091c165ca5
commit cf71aa4579
2 changed files with 187 additions and 170 deletions

View File

@@ -9,6 +9,8 @@ import debounce from "lodash/debounce";
import Autosuggest from "react-autosuggest";
import Highlight from "react-highlighter";
import onClickOutside from "react-onclickoutside";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSearch } from "@fortawesome/free-solid-svg-icons/faSearch";
@@ -20,191 +22,200 @@ import { FilterInputLabel } from "Components/Labels/FilterInputLabel";
import { AutosuggestTheme } from "./Constants";
import { History } from "./History";
const FilterInput = observer(
class FilterInput extends Component {
static propTypes = {
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
settingsStore: PropTypes.instanceOf(Settings).isRequired
};
const FilterInput = onClickOutside(
observer(
class FilterInput extends Component {
static propTypes = {
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
settingsStore: PropTypes.instanceOf(Settings).isRequired
};
inputStore = observable(
{
ref: null,
suggestions: [],
suggestionsFetch: null,
value: "",
focused: false,
storeInputReference(ref) {
this.ref = ref;
inputStore = observable(
{
ref: null,
suggestions: [],
suggestionsFetch: null,
value: "",
focused: false,
storeInputReference(ref) {
this.ref = ref;
}
},
{
storeInputReference: action.bound
},
{ name: "Filter input state" }
);
componentDidMount() {
if (this.inputStore.ref !== null && !IsMobile()) {
this.inputStore.ref.input.focus();
}
},
{
storeInputReference: action.bound
},
{ name: "Filter input state" }
);
componentDidMount() {
if (this.inputStore.ref !== null && !IsMobile()) {
this.inputStore.ref.input.focus();
}
}
onChange = action((event, { newValue, method }) => {
// onChange here handles change for the user input in the filter bar
// we need to update inputStore.value every time user types in something
event.preventDefault();
this.inputStore.value = newValue;
});
onChange = action((event, { newValue, method }) => {
// onChange here handles change for the user input in the filter bar
// we need to update inputStore.value every time user types in something
event.preventDefault();
this.inputStore.value = newValue;
});
onSubmit = action(event => {
event.preventDefault();
if (this.inputStore.value !== "") {
this.props.alertStore.filters.addFilter(this.inputStore.value);
this.inputStore.value = "";
}
});
onSubmit = action(event => {
event.preventDefault();
if (this.inputStore.value !== "") {
this.props.alertStore.filters.addFilter(this.inputStore.value);
this.inputStore.value = "";
}
});
onSuggestionsClearRequested = action(() => {
this.inputStore.suggestions = [];
});
onSuggestionsClearRequested = action(() => {
this.inputStore.suggestions = [];
});
onSuggestionsFetchRequested = debounce(
action(({ value }) => {
if (value !== "") {
this.inputStore.suggestionsFetch = FetchGet(
FormatBackendURI(`autocomplete.json?term=${value}`),
{}
)
.then(
result => result.json(),
err => {
return [];
}
onSuggestionsFetchRequested = debounce(
action(({ value }) => {
if (value !== "") {
this.inputStore.suggestionsFetch = FetchGet(
FormatBackendURI(`autocomplete.json?term=${value}`),
{}
)
.then(result => result.slice(0, 20))
.then(result => {
this.inputStore.suggestions = result;
})
.catch(err => console.error(err.message));
}
}),
300
);
onSuggestionSelected = action((event, { suggestion }) => {
this.inputStore.value = "";
this.props.alertStore.filters.addFilter(suggestion);
});
onInputClick = (inputReference, event) => {
if (
typeof event.target.className === "string" &&
event.target.className.split(" ").includes("form-control")
) {
inputReference.input.focus();
}
};
onFocus = action(() => {
this.inputStore.focused = true;
});
onBlur = action(() => {
this.inputStore.focused = false;
});
renderSuggestion = (suggestion, { query, isHighlighted }) => {
return (
<Highlight
matchElement="span"
matchClass="font-weight-bold"
search={query}
>
{suggestion}
</Highlight>
);
};
renderInputComponent = inputProps => {
const { value } = inputProps;
return (
<input
className="components-filterinput-wrapper text-white"
placeholder=""
size={value.length + 1}
{...inputProps}
/>
);
};
render() {
const { alertStore, settingsStore } = this.props;
return (
// data-filters is there to register filters for observation in mobx
// in order to re-render input component
<form
className="form-inline mw-100"
onSubmit={this.onSubmit}
data-filters={alertStore.filters.values.map(f => f.raw).join(" ")}
>
<div
className={`input-group w-100 mr-2 border-left-0 border-right-0 border-top-0 border border-light ${
this.inputStore.focused ? "bg-focused" : "bg-transparent"
}`}
>
<div className="input-group-prepend">
<span className="input-group-text px-2 border-0 rounded-0 bg-transparent text-white">
<FontAwesomeIcon icon={faSearch} />
</span>
</div>
<div
className="form-control components-filterinput border-0 rounded-0 bg-transparent"
onClick={event => {
this.onInputClick(this.inputStore.ref, event);
}}
onFocus={this.onFocus}
onBlur={this.onBlur}
>
{alertStore.filters.values.map(filter => (
<FilterInputLabel
key={filter.raw}
alertStore={alertStore}
filter={filter}
/>
))}
<Autosuggest
ref={this.inputStore.storeInputReference}
suggestions={this.inputStore.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
shouldRenderSuggestions={value =>
value && value.trim().length > 1
.then(
result => result.json(),
err => {
return [];
}
getSuggestionValue={suggestion => suggestion}
renderSuggestion={this.renderSuggestion}
renderInputComponent={this.renderInputComponent}
inputProps={{
value: this.inputStore.value,
onChange: this.onChange
}}
theme={AutosuggestTheme}
/>
</div>
)
.then(result => result.slice(0, 20))
.then(result => {
this.inputStore.suggestions = result;
})
.catch(err => console.error(err.message));
}
}),
300
);
onSuggestionSelected = action((event, { suggestion }) => {
this.inputStore.value = "";
this.props.alertStore.filters.addFilter(suggestion);
});
onInputClick = (inputReference, event) => {
if (
typeof event.target.className === "string" &&
event.target.className.split(" ").includes("form-control")
) {
inputReference.input.focus();
}
};
onFocus = action(() => {
this.inputStore.focused = true;
});
onBlur = action(() => {
this.inputStore.focused = false;
});
handleClickOutside = action(event => {
this.inputStore.focused = false;
});
renderSuggestion = (suggestion, { query, isHighlighted }) => {
return (
<Highlight
matchElement="span"
matchClass="font-weight-bold"
search={query}
>
{suggestion}
</Highlight>
);
};
renderInputComponent = inputProps => {
const { value } = inputProps;
return (
<input
className="components-filterinput-wrapper text-white"
placeholder=""
size={value.length + 1}
{...inputProps}
/>
);
};
render() {
const { alertStore, settingsStore } = this.props;
return (
// data-filters is there to register filters for observation in mobx
// in order to re-render input component
<form
className="form-inline mw-100"
onSubmit={this.onSubmit}
data-filters={alertStore.filters.values.map(f => f.raw).join(" ")}
>
<div
className={`input-group-append ${
className={`input-group w-100 mr-2 border-left-0 border-right-0 border-top-0 border border-light ${
this.inputStore.focused ? "bg-focused" : "bg-transparent"
}`}
>
<History alertStore={alertStore} settingsStore={settingsStore} />
<div className="input-group-prepend">
<span className="input-group-text px-2 border-0 rounded-0 bg-transparent text-white">
<FontAwesomeIcon icon={faSearch} />
</span>
</div>
<div
className="form-control components-filterinput border-0 rounded-0 bg-transparent"
onClick={event => {
this.onInputClick(this.inputStore.ref, event);
}}
onFocus={this.onFocus}
onBlur={this.onBlur}
>
{alertStore.filters.values.map(filter => (
<FilterInputLabel
key={filter.raw}
alertStore={alertStore}
filter={filter}
/>
))}
<Autosuggest
ref={this.inputStore.storeInputReference}
suggestions={this.inputStore.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
shouldRenderSuggestions={value =>
value && value.trim().length > 1
}
getSuggestionValue={suggestion => suggestion}
renderSuggestion={this.renderSuggestion}
renderInputComponent={this.renderInputComponent}
inputProps={{
value: this.inputStore.value,
onChange: this.onChange
}}
theme={AutosuggestTheme}
/>
</div>
<div
className={`input-group-append ${
this.inputStore.focused ? "bg-focused" : "bg-transparent"
}`}
>
<History
alertStore={alertStore}
settingsStore={settingsStore}
/>
</div>
</div>
</div>
</form>
);
</form>
);
}
}
}
)
);
export { FilterInput };

View File

@@ -135,6 +135,12 @@ describe("<FilterInput />", () => {
expect(toDiffableHtml(tree.html())).not.toMatch(/bg-focused/);
});
it("calling handleClickOutside() changes background color", () => {
const tree = MountedInput();
tree.instance().handleClickOutside();
expect(toDiffableHtml(tree.html())).not.toMatch(/bg-focused/);
});
it("componentDidMount executes even when inputStore.ref=null", () => {
const tree = MountedInput();
const instance = tree.instance();