mirror of
https://github.com/prymitive/karma
synced 2026-08-23 11:56:20 +00:00
fix(ui): rewrite most configuration UI with hooks
This commit is contained in:
committed by
Łukasz Mierzwa
parent
d6e667a62b
commit
0fce699894
@@ -1,76 +1,54 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver } from "mobx-react";
|
||||
|
||||
import Select from "react-select";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
|
||||
const AlertGroupCollapseConfiguration = observer(
|
||||
class AlertGroupCollapseConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
static contextType = ThemeContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.validateConfig();
|
||||
}
|
||||
|
||||
valueToOption = (val) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
return {
|
||||
label: settingsStore.alertGroupConfig.options[val].label,
|
||||
value: val,
|
||||
};
|
||||
};
|
||||
|
||||
validateConfig = action(() => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
if (
|
||||
!Object.values(settingsStore.alertGroupConfig.options)
|
||||
.map((o) => o.value)
|
||||
.includes(settingsStore.alertGroupConfig.config.defaultCollapseState)
|
||||
) {
|
||||
settingsStore.alertGroupConfig.config.defaultCollapseState =
|
||||
settingsStore.alertGroupConfig.options.collapsedOnMobile.value;
|
||||
}
|
||||
});
|
||||
|
||||
onCollapseChange = action((newValue, actionMeta) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
settingsStore.alertGroupConfig.config.defaultCollapseState =
|
||||
newValue.value;
|
||||
});
|
||||
|
||||
render() {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
return (
|
||||
<div className="form-group mb-0">
|
||||
<Select
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-collapse"
|
||||
defaultValue={this.valueToOption(
|
||||
settingsStore.alertGroupConfig.config.defaultCollapseState
|
||||
)}
|
||||
options={Object.values(settingsStore.alertGroupConfig.options)}
|
||||
onChange={this.onCollapseChange}
|
||||
hideSelectedOptions
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const AlertGroupCollapseConfiguration = ({ settingsStore }) => {
|
||||
if (
|
||||
!Object.values(settingsStore.alertGroupConfig.options)
|
||||
.map((o) => o.value)
|
||||
.includes(settingsStore.alertGroupConfig.config.defaultCollapseState)
|
||||
) {
|
||||
settingsStore.alertGroupConfig.config.defaultCollapseState =
|
||||
settingsStore.alertGroupConfig.options.collapsedOnMobile.value;
|
||||
}
|
||||
);
|
||||
|
||||
const valueToOption = (val) => {
|
||||
return {
|
||||
label: settingsStore.alertGroupConfig.options[val].label,
|
||||
value: val,
|
||||
};
|
||||
};
|
||||
|
||||
const onCollapseChange = (newValue, actionMeta) => {
|
||||
settingsStore.alertGroupConfig.config.defaultCollapseState = newValue.value;
|
||||
};
|
||||
|
||||
const context = React.useContext(ThemeContext);
|
||||
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-0">
|
||||
<Select
|
||||
styles={context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-collapse"
|
||||
defaultValue={valueToOption(
|
||||
settingsStore.alertGroupConfig.config.defaultCollapseState
|
||||
)}
|
||||
options={Object.values(settingsStore.alertGroupConfig.options)}
|
||||
onChange={onCollapseChange}
|
||||
hideSelectedOptions
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
AlertGroupCollapseConfiguration.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { AlertGroupCollapseConfiguration };
|
||||
|
||||
@@ -4,28 +4,23 @@ import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { MockThemeContext } from "__mocks__/Theme";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import {
|
||||
ReactSelectColors,
|
||||
ReactSelectStyles,
|
||||
} from "Components/Theme/ReactSelect";
|
||||
import { AlertGroupCollapseConfiguration } from "./AlertGroupCollapseConfiguration";
|
||||
|
||||
let settingsStore;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(React, "useContext").mockImplementation(() => MockThemeContext);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
settingsStore = new Settings();
|
||||
});
|
||||
|
||||
const FakeConfiguration = () => {
|
||||
return mount(
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light),
|
||||
}}
|
||||
>
|
||||
<AlertGroupCollapseConfiguration settingsStore={settingsStore} />
|
||||
</ThemeContext.Provider>
|
||||
<AlertGroupCollapseConfiguration settingsStore={settingsStore} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,56 +1,41 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observable, action, toJS } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver, useLocalStore } from "mobx-react";
|
||||
|
||||
import InputRange from "react-input-range";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
const AlertGroupConfiguration = observer(
|
||||
class AlertGroupConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
const AlertGroupConfiguration = ({ settingsStore }) => {
|
||||
const config = useLocalStore(() => ({
|
||||
defaultRenderCount:
|
||||
settingsStore.alertGroupConfig.config.defaultRenderCount,
|
||||
setDefaultRenderCount(val) {
|
||||
this.defaultRenderCount = val;
|
||||
},
|
||||
}));
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const onChangeComplete = (value) => {
|
||||
settingsStore.alertGroupConfig.update({ defaultRenderCount: value });
|
||||
};
|
||||
|
||||
this.config = observable({
|
||||
defaultRenderCount: toJS(
|
||||
props.settingsStore.alertGroupConfig.config.defaultRenderCount
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
onChange = action((value) => {
|
||||
this.config.defaultRenderCount = value;
|
||||
});
|
||||
|
||||
onChangeComplete = action((value) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
settingsStore.alertGroupConfig.update({ defaultRenderCount: value });
|
||||
});
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="form-group mb-0 text-center">
|
||||
<InputRange
|
||||
minValue={1}
|
||||
maxValue={10}
|
||||
step={1}
|
||||
value={this.config.defaultRenderCount}
|
||||
id="formControlRange"
|
||||
formatLabel={this.formatLabel}
|
||||
onChange={this.onChange}
|
||||
onChangeComplete={this.onChangeComplete}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-0 text-center">
|
||||
<InputRange
|
||||
minValue={1}
|
||||
maxValue={10}
|
||||
step={1}
|
||||
value={config.defaultRenderCount}
|
||||
id="formControlRange"
|
||||
onChange={config.setDefaultRenderCount}
|
||||
onChangeComplete={onChangeComplete}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
AlertGroupConfiguration.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { AlertGroupConfiguration };
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Settings } from "Stores/Settings";
|
||||
import { AlertGroupConfiguration } from "./AlertGroupConfiguration";
|
||||
|
||||
let settingsStore;
|
||||
|
||||
beforeEach(() => {
|
||||
settingsStore = new Settings();
|
||||
});
|
||||
@@ -22,16 +23,22 @@ describe("<AlertGroupConfiguration />", () => {
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("call to onChange() updates internal state", () => {
|
||||
const tree = FakeConfiguration();
|
||||
tree.instance().onChange(9);
|
||||
expect(tree.instance().config.defaultRenderCount).toBe(9);
|
||||
});
|
||||
|
||||
it("settings are updated on completed change", () => {
|
||||
const tree = FakeConfiguration();
|
||||
tree.instance().onChangeComplete(8);
|
||||
expect(settingsStore.alertGroupConfig.config.defaultRenderCount).toBe(8);
|
||||
expect(settingsStore.alertGroupConfig.config.defaultRenderCount).toBe(5);
|
||||
|
||||
const slider = tree.find(`Slider [onKeyDown]`).first();
|
||||
|
||||
slider.simulate("keyDown", { keyCode: 37 });
|
||||
slider.simulate("keyUp", { keyCode: 37 });
|
||||
|
||||
expect(settingsStore.alertGroupConfig.config.defaultRenderCount).toBe(4);
|
||||
|
||||
slider.simulate("keyDown", { keyCode: 39 });
|
||||
slider.simulate("keyUp", { keyCode: 39 });
|
||||
slider.simulate("keyDown", { keyCode: 39 });
|
||||
slider.simulate("keyUp", { keyCode: 39 });
|
||||
expect(settingsStore.alertGroupConfig.config.defaultRenderCount).toBe(6);
|
||||
});
|
||||
|
||||
it("custom interval value is rendered correctly", () => {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver } from "mobx-react";
|
||||
|
||||
import Select from "react-select";
|
||||
|
||||
@@ -10,108 +9,84 @@ import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { SortLabelName } from "./SortLabelName";
|
||||
|
||||
const AlertGroupSortConfiguration = observer(
|
||||
class AlertGroupSortConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
static contextType = ThemeContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.validateConfig();
|
||||
}
|
||||
|
||||
onSortOrderChange = action((newValue, actionMeta) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
settingsStore.gridConfig.config.sortOrder = newValue.value;
|
||||
});
|
||||
|
||||
onSortReverseChange = action((event) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
settingsStore.gridConfig.config.reverseSort = event.target.checked;
|
||||
});
|
||||
|
||||
valueToOption = (val) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
return { label: settingsStore.gridConfig.options[val].label, value: val };
|
||||
};
|
||||
|
||||
validateConfig = action(() => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
if (
|
||||
!Object.values(settingsStore.gridConfig.options)
|
||||
.map((o) => o.value)
|
||||
.includes(settingsStore.gridConfig.config.sortOrder)
|
||||
) {
|
||||
settingsStore.gridConfig.config.sortOrder =
|
||||
settingsStore.gridConfig.options.default.value;
|
||||
}
|
||||
});
|
||||
|
||||
render() {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
const hideReverse =
|
||||
settingsStore.gridConfig.config.sortOrder ===
|
||||
settingsStore.gridConfig.options.default.value ||
|
||||
settingsStore.gridConfig.config.sortOrder ===
|
||||
settingsStore.gridConfig.options.disabled.value;
|
||||
|
||||
return (
|
||||
<div className="form-group mb-0">
|
||||
<div className="d-flex flex-fill flex-lg-row flex-column justify-content-between">
|
||||
<div className="flex-shrink-0 flex-grow-1 flex-basis-auto">
|
||||
<Select
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-sort-order"
|
||||
defaultValue={this.valueToOption(
|
||||
settingsStore.gridConfig.config.sortOrder
|
||||
)}
|
||||
options={Object.values(settingsStore.gridConfig.options)}
|
||||
onChange={this.onSortOrderChange}
|
||||
hideSelectedOptions
|
||||
/>
|
||||
</div>
|
||||
{settingsStore.gridConfig.config.sortOrder ===
|
||||
settingsStore.gridConfig.options.label.value ? (
|
||||
<div className="flex-shrink-0 flex-grow-1 flex-basis-auto mx-0 mx-lg-1 mt-1 mt-lg-0">
|
||||
<SortLabelName settingsStore={settingsStore} />
|
||||
</div>
|
||||
) : null}
|
||||
{hideReverse ? null : (
|
||||
<div className="flex-shrink-1 flex-grow-0 form-check form-check-inline flex-basis-auto mt-1 mt-lg-0 ml-0 ml-lg-1 mr-0">
|
||||
<span className="custom-control custom-switch">
|
||||
<input
|
||||
id="configuration-sort-reverse"
|
||||
className="custom-control-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
checked={
|
||||
settingsStore.gridConfig.config.reverseSort || false
|
||||
}
|
||||
onChange={this.onSortReverseChange}
|
||||
/>
|
||||
<label
|
||||
className="custom-control-label cursor-pointer mr-3"
|
||||
htmlFor="configuration-sort-reverse"
|
||||
>
|
||||
Reverse
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const AlertGroupSortConfiguration = ({ settingsStore }) => {
|
||||
if (
|
||||
!Object.values(settingsStore.gridConfig.options)
|
||||
.map((o) => o.value)
|
||||
.includes(settingsStore.gridConfig.config.sortOrder)
|
||||
) {
|
||||
settingsStore.gridConfig.config.sortOrder =
|
||||
settingsStore.gridConfig.options.default.value;
|
||||
}
|
||||
);
|
||||
|
||||
const onSortOrderChange = (newValue, actionMeta) => {
|
||||
settingsStore.gridConfig.config.sortOrder = newValue.value;
|
||||
};
|
||||
|
||||
const onSortReverseChange = (event) => {
|
||||
settingsStore.gridConfig.config.reverseSort = event.target.checked;
|
||||
};
|
||||
|
||||
const valueToOption = (val) => {
|
||||
return { label: settingsStore.gridConfig.options[val].label, value: val };
|
||||
};
|
||||
|
||||
const hideReverse =
|
||||
settingsStore.gridConfig.config.sortOrder ===
|
||||
settingsStore.gridConfig.options.default.value ||
|
||||
settingsStore.gridConfig.config.sortOrder ===
|
||||
settingsStore.gridConfig.options.disabled.value;
|
||||
|
||||
const context = React.useContext(ThemeContext);
|
||||
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-0">
|
||||
<div className="d-flex flex-fill flex-lg-row flex-column justify-content-between">
|
||||
<div className="flex-shrink-0 flex-grow-1 flex-basis-auto">
|
||||
<Select
|
||||
styles={context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-sort-order"
|
||||
defaultValue={valueToOption(
|
||||
settingsStore.gridConfig.config.sortOrder
|
||||
)}
|
||||
options={Object.values(settingsStore.gridConfig.options)}
|
||||
onChange={onSortOrderChange}
|
||||
hideSelectedOptions
|
||||
/>
|
||||
</div>
|
||||
{settingsStore.gridConfig.config.sortOrder ===
|
||||
settingsStore.gridConfig.options.label.value ? (
|
||||
<div className="flex-shrink-0 flex-grow-1 flex-basis-auto mx-0 mx-lg-1 mt-1 mt-lg-0">
|
||||
<SortLabelName settingsStore={settingsStore} />
|
||||
</div>
|
||||
) : null}
|
||||
{hideReverse ? null : (
|
||||
<div className="flex-shrink-1 flex-grow-0 form-check form-check-inline flex-basis-auto mt-1 mt-lg-0 ml-0 ml-lg-1 mr-0">
|
||||
<span className="custom-control custom-switch">
|
||||
<input
|
||||
id="configuration-sort-reverse"
|
||||
className="custom-control-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
checked={settingsStore.gridConfig.config.reverseSort || false}
|
||||
onChange={onSortReverseChange}
|
||||
/>
|
||||
<label
|
||||
className="custom-control-label cursor-pointer mr-3"
|
||||
htmlFor="configuration-sort-reverse"
|
||||
>
|
||||
Reverse
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
AlertGroupSortConfiguration.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { AlertGroupSortConfiguration };
|
||||
|
||||
@@ -75,10 +75,12 @@ describe("<AlertGroupSortConfiguration />", () => {
|
||||
settingsStore.gridConfig.options.label.value
|
||||
);
|
||||
const tree = FakeConfiguration();
|
||||
tree.instance().onSortOrderChange({
|
||||
label: settingsStore.gridConfig.options.startsAt.label,
|
||||
value: settingsStore.gridConfig.options.startsAt.value,
|
||||
});
|
||||
|
||||
tree
|
||||
.find("input#react-select-configuration-sort-order-input")
|
||||
.simulate("change", { target: { value: " " } });
|
||||
tree.find("div.react-select__option").at(2).simulate("click");
|
||||
|
||||
setTimeout(() => {
|
||||
expect(settingsStore.gridConfig.config.sortOrder).toBe(
|
||||
settingsStore.gridConfig.options.startsAt.value
|
||||
|
||||
@@ -1,52 +1,42 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver } from "mobx-react";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
const AlertGroupTitleBarColor = observer(
|
||||
class AlertGroupTitleBarColor extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
const AlertGroupTitleBarColor = ({ settingsStore }) => {
|
||||
const onChange = (event) => {
|
||||
settingsStore.alertGroupConfig.config.colorTitleBar = event.target.checked;
|
||||
};
|
||||
|
||||
onChange = action((event) => {
|
||||
const { settingsStore } = this.props;
|
||||
settingsStore.alertGroupConfig.config.colorTitleBar =
|
||||
event.target.checked;
|
||||
});
|
||||
|
||||
render() {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
return (
|
||||
<div className="form-group mb-0">
|
||||
<div className="form-check form-check-inline">
|
||||
<span className="custom-control custom-switch">
|
||||
<input
|
||||
id="configuration-colortitlebar"
|
||||
className="custom-control-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
checked={
|
||||
settingsStore.alertGroupConfig.config.colorTitleBar || false
|
||||
}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<label
|
||||
className="custom-control-label cursor-pointer mr-3"
|
||||
htmlFor="configuration-colortitlebar"
|
||||
>
|
||||
Color group titlebar
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-0">
|
||||
<div className="form-check form-check-inline">
|
||||
<span className="custom-control custom-switch">
|
||||
<input
|
||||
id="configuration-colortitlebar"
|
||||
className="custom-control-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
checked={
|
||||
settingsStore.alertGroupConfig.config.colorTitleBar || false
|
||||
}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<label
|
||||
className="custom-control-label cursor-pointer mr-3"
|
||||
htmlFor="configuration-colortitlebar"
|
||||
>
|
||||
Color group titlebar
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
AlertGroupTitleBarColor.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { AlertGroupTitleBarColor };
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observable, action, toJS } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver, useLocalStore } from "mobx-react";
|
||||
|
||||
import debounce from "lodash/debounce";
|
||||
|
||||
@@ -10,50 +9,34 @@ import InputRange from "react-input-range";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
const AlertGroupWidthConfiguration = observer(
|
||||
class AlertGroupWidthConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
const AlertGroupWidthConfiguration = ({ settingsStore }) => {
|
||||
const config = useLocalStore(() => ({
|
||||
groupWidth: settingsStore.gridConfig.config.groupWidth,
|
||||
setGroupWidth(val) {
|
||||
this.groupWidth = val;
|
||||
},
|
||||
}));
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const onChangeComplete = debounce((value) => {
|
||||
settingsStore.gridConfig.config.groupWidth = value;
|
||||
}, 200);
|
||||
|
||||
this.config = observable({
|
||||
groupWidth: toJS(props.settingsStore.gridConfig.config.groupWidth),
|
||||
});
|
||||
}
|
||||
|
||||
onChange = action((value) => {
|
||||
this.config.groupWidth = value;
|
||||
});
|
||||
|
||||
onChangeComplete = debounce(
|
||||
action((value) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
settingsStore.gridConfig.config.groupWidth = value;
|
||||
}),
|
||||
200
|
||||
);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="form-group mb-0 text-center">
|
||||
<InputRange
|
||||
minValue={300}
|
||||
maxValue={800}
|
||||
step={20}
|
||||
value={this.config.groupWidth}
|
||||
id="formControlRange"
|
||||
formatLabel={this.formatLabel}
|
||||
onChange={this.onChange}
|
||||
onChangeComplete={this.onChangeComplete}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-0 text-center">
|
||||
<InputRange
|
||||
minValue={300}
|
||||
maxValue={800}
|
||||
step={20}
|
||||
value={config.groupWidth}
|
||||
id="formControlRange"
|
||||
onChange={config.setGroupWidth}
|
||||
onChangeComplete={onChangeComplete}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
AlertGroupWidthConfiguration.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { AlertGroupWidthConfiguration };
|
||||
|
||||
@@ -22,16 +22,23 @@ describe("<AlertGroupWidthConfiguration />", () => {
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("call to onChange() updates internal state", () => {
|
||||
const tree = FakeConfiguration();
|
||||
tree.instance().onChange(500);
|
||||
expect(tree.instance().config.groupWidth).toBe(500);
|
||||
});
|
||||
|
||||
it("settings are updated on completed change", () => {
|
||||
const tree = FakeConfiguration();
|
||||
tree.instance().onChangeComplete(555);
|
||||
expect(settingsStore.gridConfig.config.groupWidth).toBe(555);
|
||||
expect(settingsStore.gridConfig.config.groupWidth).toBe(420);
|
||||
|
||||
const slider = tree.find(`Slider [onKeyDown]`).first();
|
||||
|
||||
slider.simulate("keyDown", { keyCode: 37 });
|
||||
slider.simulate("keyUp", { keyCode: 37 });
|
||||
|
||||
expect(settingsStore.gridConfig.config.groupWidth).toBe(400);
|
||||
|
||||
slider.simulate("keyDown", { keyCode: 39 });
|
||||
slider.simulate("keyUp", { keyCode: 39 });
|
||||
slider.simulate("keyDown", { keyCode: 39 });
|
||||
slider.simulate("keyUp", { keyCode: 39 });
|
||||
|
||||
expect(settingsStore.gridConfig.config.groupWidth).toBe(440);
|
||||
});
|
||||
|
||||
it("custom interval value is rendered correctly", () => {
|
||||
|
||||
@@ -1,58 +1,41 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observable, action, toJS } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver, useLocalStore } from "mobx-react";
|
||||
|
||||
import InputRange from "react-input-range";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
const FetchConfiguration = observer(
|
||||
class FetchConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
const FetchConfiguration = ({ settingsStore }) => {
|
||||
const config = useLocalStore(() => ({
|
||||
fetchInterval: settingsStore.fetchConfig.config.interval,
|
||||
setFetchInterval(val) {
|
||||
this.fetchInterval = val;
|
||||
},
|
||||
}));
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const onChangeComplete = (value) => {
|
||||
settingsStore.fetchConfig.setInterval(value);
|
||||
};
|
||||
|
||||
this.config = observable({
|
||||
fetchInterval: toJS(props.settingsStore.fetchConfig.config.interval),
|
||||
});
|
||||
}
|
||||
|
||||
onChange = action((value) => {
|
||||
this.config.fetchInterval = value;
|
||||
});
|
||||
|
||||
onChangeComplete = action((value) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
settingsStore.fetchConfig.setInterval(value);
|
||||
});
|
||||
|
||||
formatLabel(value) {
|
||||
return `${value}s`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="form-group mb-0 text-center">
|
||||
<InputRange
|
||||
minValue={10}
|
||||
maxValue={120}
|
||||
step={10}
|
||||
value={this.config.fetchInterval}
|
||||
id="formControlRange"
|
||||
formatLabel={this.formatLabel}
|
||||
onChange={this.onChange}
|
||||
onChangeComplete={this.onChangeComplete}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-0 text-center">
|
||||
<InputRange
|
||||
minValue={10}
|
||||
maxValue={120}
|
||||
step={10}
|
||||
value={config.fetchInterval}
|
||||
id="formControlRange"
|
||||
formatLabel={(value) => `${value}s`}
|
||||
onChange={config.setFetchInterval}
|
||||
onChangeComplete={onChangeComplete}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
FetchConfiguration.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { FetchConfiguration };
|
||||
|
||||
@@ -22,16 +22,23 @@ describe("<FetchConfiguration />", () => {
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("call to onChange() updates internal state", () => {
|
||||
const tree = FakeConfiguration();
|
||||
tree.instance().onChange(55);
|
||||
expect(tree.instance().config.fetchInterval).toBe(55);
|
||||
});
|
||||
|
||||
it("settings are updated on completed change", () => {
|
||||
const tree = FakeConfiguration();
|
||||
tree.instance().onChangeComplete(123);
|
||||
expect(settingsStore.fetchConfig.config.interval).toBe(123);
|
||||
expect(settingsStore.fetchConfig.config.interval).toBe(30);
|
||||
|
||||
const slider = tree.find(`Slider [onKeyDown]`).first();
|
||||
|
||||
slider.simulate("keyDown", { keyCode: 37 });
|
||||
slider.simulate("keyUp", { keyCode: 37 });
|
||||
|
||||
expect(settingsStore.fetchConfig.config.interval).toBe(20);
|
||||
|
||||
slider.simulate("keyDown", { keyCode: 39 });
|
||||
slider.simulate("keyUp", { keyCode: 39 });
|
||||
slider.simulate("keyDown", { keyCode: 39 });
|
||||
slider.simulate("keyUp", { keyCode: 39 });
|
||||
|
||||
expect(settingsStore.fetchConfig.config.interval).toBe(40);
|
||||
});
|
||||
|
||||
it("custom interval value is rendered correctly", () => {
|
||||
|
||||
@@ -1,49 +1,39 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver } from "mobx-react";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
const FilterBarConfiguration = observer(
|
||||
class FilterBarConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
onAutohideChange = action((event) => {
|
||||
const { settingsStore } = this.props;
|
||||
settingsStore.filterBarConfig.config.autohide = event.target.checked;
|
||||
});
|
||||
|
||||
render() {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
return (
|
||||
<div className="form-group mb-0">
|
||||
<div className="form-check form-check-inline">
|
||||
<span className="custom-control custom-switch">
|
||||
<input
|
||||
id="configuration-autohide"
|
||||
className="custom-control-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
checked={settingsStore.filterBarConfig.config.autohide || false}
|
||||
onChange={this.onAutohideChange}
|
||||
/>
|
||||
<label
|
||||
className="custom-control-label cursor-pointer mr-3"
|
||||
htmlFor="configuration-autohide"
|
||||
>
|
||||
Hide filter bar when idle
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
const FilterBarConfiguration = ({ settingsStore }) => {
|
||||
const onAutohideChange = (event) => {
|
||||
settingsStore.filterBarConfig.config.autohide = event.target.checked;
|
||||
};
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-0">
|
||||
<div className="form-check form-check-inline">
|
||||
<span className="custom-control custom-switch">
|
||||
<input
|
||||
id="configuration-autohide"
|
||||
className="custom-control-input"
|
||||
type="checkbox"
|
||||
value=""
|
||||
checked={settingsStore.filterBarConfig.config.autohide || false}
|
||||
onChange={onAutohideChange}
|
||||
/>
|
||||
<label
|
||||
className="custom-control-label cursor-pointer mr-3"
|
||||
htmlFor="configuration-autohide"
|
||||
>
|
||||
Hide filter bar when idle
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
FilterBarConfiguration.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { FilterBarConfiguration };
|
||||
|
||||
@@ -1,75 +1,52 @@
|
||||
import React, { Component } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { useObserver } from "mobx-react";
|
||||
|
||||
import Select from "react-select";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
|
||||
const ThemeConfiguration = observer(
|
||||
class ThemeConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
static contextType = ThemeContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.validateConfig();
|
||||
}
|
||||
|
||||
valueToOption = (val) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
return {
|
||||
label: settingsStore.themeConfig.options[val].label,
|
||||
value: val,
|
||||
};
|
||||
};
|
||||
|
||||
validateConfig = action(() => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
if (
|
||||
!Object.values(settingsStore.themeConfig.options)
|
||||
.map((o) => o.value)
|
||||
.includes(settingsStore.themeConfig.config.theme)
|
||||
) {
|
||||
settingsStore.themeConfig.config.theme =
|
||||
settingsStore.themeConfig.options.auto.value;
|
||||
}
|
||||
});
|
||||
|
||||
onCollapseChange = action((newValue, actionMeta) => {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
settingsStore.themeConfig.config.theme = newValue.value;
|
||||
});
|
||||
|
||||
render() {
|
||||
const { settingsStore } = this.props;
|
||||
|
||||
return (
|
||||
<div className="form-group mb-2">
|
||||
<Select
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-theme"
|
||||
defaultValue={this.valueToOption(
|
||||
settingsStore.themeConfig.config.theme
|
||||
)}
|
||||
options={Object.values(settingsStore.themeConfig.options)}
|
||||
onChange={this.onCollapseChange}
|
||||
hideSelectedOptions
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const ThemeConfiguration = ({ settingsStore }) => {
|
||||
if (
|
||||
!Object.values(settingsStore.themeConfig.options)
|
||||
.map((o) => o.value)
|
||||
.includes(settingsStore.themeConfig.config.theme)
|
||||
) {
|
||||
settingsStore.themeConfig.config.theme =
|
||||
settingsStore.themeConfig.options.auto.value;
|
||||
}
|
||||
);
|
||||
|
||||
const valueToOption = (val) => {
|
||||
return {
|
||||
label: settingsStore.themeConfig.options[val].label,
|
||||
value: val,
|
||||
};
|
||||
};
|
||||
|
||||
const onCollapseChange = (newValue, actionMeta) => {
|
||||
settingsStore.themeConfig.config.theme = newValue.value;
|
||||
};
|
||||
|
||||
const context = React.useContext(ThemeContext);
|
||||
|
||||
return useObserver(() => (
|
||||
<div className="form-group mb-2">
|
||||
<Select
|
||||
styles={context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-theme"
|
||||
defaultValue={valueToOption(settingsStore.themeConfig.config.theme)}
|
||||
options={Object.values(settingsStore.themeConfig.options)}
|
||||
onChange={onCollapseChange}
|
||||
hideSelectedOptions
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
};
|
||||
ThemeConfiguration.propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
};
|
||||
|
||||
export { ThemeConfiguration };
|
||||
|
||||
@@ -4,29 +4,22 @@ import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { MockThemeContext } from "__mocks__/Theme";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import {
|
||||
ReactSelectColors,
|
||||
ReactSelectStyles,
|
||||
} from "Components/Theme/ReactSelect";
|
||||
import { ThemeConfiguration } from "./ThemeConfiguration";
|
||||
|
||||
let settingsStore;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(React, "useContext").mockImplementation(() => MockThemeContext);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
settingsStore = new Settings();
|
||||
});
|
||||
|
||||
const FakeConfiguration = () => {
|
||||
return mount(
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light),
|
||||
}}
|
||||
>
|
||||
<ThemeConfiguration settingsStore={settingsStore} />
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
return mount(<ThemeConfiguration settingsStore={settingsStore} />);
|
||||
};
|
||||
|
||||
describe("<ThemeConfiguration />", () => {
|
||||
|
||||
+5
-5
@@ -4,12 +4,12 @@ exports[`<AlertGroupCollapseConfiguration /> matches snapshot with default value
|
||||
"
|
||||
<div class=\\"form-group mb-0\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-1ne8613-ValueContainer\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
<div class=\\"react-select__control css-yk16xz-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-g1d714-ValueContainer\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
Collapse on mobile
|
||||
</div>
|
||||
<div class=\\"css-ps6ina-Input\\">
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
@@ -29,7 +29,7 @@ exports[`<AlertGroupCollapseConfiguration /> matches snapshot with default value
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"react-select__indicators css-vcwr3k-IndicatorsContainer\\">
|
||||
<div class=\\"react-select__indicators css-1hb7zxy-IndicatorsContainer\\">
|
||||
<span class=\\"react-select__indicator-separator css-1okebmr-indicatorSeparator\\">
|
||||
</span>
|
||||
<div aria-hidden=\\"true\\"
|
||||
|
||||
+5
-5
@@ -4,12 +4,12 @@ exports[`<ThemeConfiguration /> matches snapshot with default values 1`] = `
|
||||
"
|
||||
<div class=\\"form-group mb-2\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-1ne8613-ValueContainer\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
<div class=\\"react-select__control css-yk16xz-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-g1d714-ValueContainer\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
Automatic theme, follow browser preference
|
||||
</div>
|
||||
<div class=\\"css-ps6ina-Input\\">
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
@@ -29,7 +29,7 @@ exports[`<ThemeConfiguration /> matches snapshot with default values 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"react-select__indicators css-vcwr3k-IndicatorsContainer\\">
|
||||
<div class=\\"react-select__indicators css-1hb7zxy-IndicatorsContainer\\">
|
||||
<span class=\\"react-select__indicator-separator css-1okebmr-indicatorSeparator\\">
|
||||
</span>
|
||||
<div aria-hidden=\\"true\\"
|
||||
|
||||
Reference in New Issue
Block a user