fix(ui): rewrite most configuration UI with hooks

This commit is contained in:
Łukasz Mierzwa
2020-05-01 17:57:42 +01:00
committed by Łukasz Mierzwa
parent d6e667a62b
commit 0fce699894
16 changed files with 394 additions and 522 deletions
@@ -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 />", () => {
@@ -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\\"
@@ -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\\"