Skip to content

Commit

Permalink
Merge pull request #1098 from prymitive/dark-mode
Browse files Browse the repository at this point in the history
feat(ui): experimental dark theme
  • Loading branch information
prymitive authored Oct 28, 2019
2 parents 4beec89 + 1de6f8f commit 7a7102d
Show file tree
Hide file tree
Showing 20 changed files with 393 additions and 62 deletions.
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ ARG VERSION
RUN CGO_ENABLED=0 make -C /src VERSION="${VERSION:-dev}" karma

FROM gcr.io/distroless/base
COPY ./docs/dark.css /themes/dark.css
COPY --from=go-builder /src/karma /karma
EXPOSE 8080
ENTRYPOINT ["/karma"]
12 changes: 4 additions & 8 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ ui:
refresh: duration
hideFiltersWhenIdle: bool
colorTitlebar: bool
darkTheme: bool
minimalGroupWidth: integer
alertsPerGroup: integer
collapseGroups: string
Expand All @@ -781,6 +782,8 @@ ui:
user inactivity
- `colorTitlebar` - if enabled alert group title bar color will be set to follow
alerts in that group
- `darkTheme` - if enabled dark mode will be enabled.
Note: dark mode is *experimental* and might be buggy.
- `minimalGroupWidth` - minimal width (in pixels) for each alert group rendered
on the grid. This value is used to calculate the number of columns rendered on
the grid.
Expand All @@ -799,6 +802,7 @@ ui:
refresh: 30s
hideFiltersWhenIdle: true
colorTitlebar: false
darkTheme: false
minimalGroupWidth: 420
alertsPerGroup: 5
collapseGroups: collapsedOnMobile
Expand Down Expand Up @@ -832,14 +836,6 @@ custom:
Use at your own risk and be aware that used CSS class names might change without
warning. This feature is provided as is without any guarantees.

There is an example `dark.css` file providing a dark theme. It's included in the
docker image as `/themes/dark.css` and can be enabled by passing environment
variable via docker:

```shell
-e CUSTOM_CSS=/themes/dark.css
```

## Command line flags

Config file options are mapped to command line flags, so `alertmanager:interval`
Expand Down
43 changes: 0 additions & 43 deletions docs/dark.css

This file was deleted.

2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func init() {
pflag.Duration("ui.refresh", time.Second*30, "UI refresh interval")
pflag.Bool("ui.hideFiltersWhenIdle", true, "Hide the filters bar when idle")
pflag.Bool("ui.colorTitlebar", false, "Color alert group titlebar based on alert state")
pflag.Bool("ui.darkTheme", false, "Enable dark theme")
pflag.Int("ui.minimalGroupWidth", 420, "Minimal width for each alert group on the grid")
pflag.Int("ui.alertsPerGroup", 5, "Default number of alerts to show for each alert group")
pflag.String("ui.collapseGroups", "collapsedOnMobile", "Default state for alert groups")
Expand Down Expand Up @@ -186,6 +187,7 @@ func (config *configSchema) Read() {
config.UI.Refresh = v.GetDuration("ui.refresh")
config.UI.HideFiltersWhenIdle = v.GetBool("ui.hideFiltersWhenIdle")
config.UI.ColorTitlebar = v.GetBool("ui.colorTitlebar")
config.UI.DarkTheme = v.GetBool("ui.darkTheme")
config.UI.MinimalGroupWidth = v.GetInt("ui.minimalGroupWidth")
config.UI.AlertsPerGroup = v.GetInt("ui.alertsPerGroup")
config.UI.CollapseGroups = v.GetString("ui.collapseGroups")
Expand Down
1 change: 1 addition & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ ui:
refresh: 30s
hideFiltersWhenIdle: true
colorTitlebar: false
darkTheme: false
minimalGroupWidth: 420
alertsPerGroup: 5
collapseGroups: collapsedOnMobile
Expand Down
1 change: 1 addition & 0 deletions internal/config/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ type configSchema struct {
Refresh time.Duration
HideFiltersWhenIdle bool `yaml:"hideFiltersWhenIdle" mapstructure:"hideFiltersWhenIdle"`
ColorTitlebar bool `yaml:"colorTitlebar" mapstructure:"colorTitlebar"`
DarkTheme bool `yaml:"darkTheme" mapstructure:"darkTheme"`
MinimalGroupWidth int `yaml:"minimalGroupWidth" mapstructure:"minimalGroupWidth"`
AlertsPerGroup int `yaml:"alertsPerGroup" mapstructure:"alertsPerGroup"`
CollapseGroups string `yaml:"collapseGroups" mapstructure:"collapseGroups"`
Expand Down
12 changes: 12 additions & 0 deletions ui/src/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,16 @@ describe("<App />", () => {
let event = new PopStateEvent("popstate");
window.onpopstate(event);
});

it("appends 'dark-theme' class to #root if dark mode is enabled", () => {
const tree = shallow(
<App
defaultFilters={["foo=bar"]}
uiDefaults={Object.assign({}, uiDefaults, { DarkMode: true })}
/>
);
tree.instance().componentWillUnmount();

expect(document.body.className.split(" ")).toContain("dark-theme");
});
});
7 changes: 7 additions & 0 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { FaviconBadge } from "Components/FaviconBadge";
import { ErrorBoundary } from "./ErrorBoundary";

import "./App.scss";
import "./DarkTheme.scss";

interface UIDefaults {
Refresh: number;
HideFiltersWhenIdle: boolean;
ColorTitlebar: boolean;
DarkTheme: boolean;
MinimalGroupWidth: number;
AlertsPerGroup: number;
CollapseGroups: "expanded" | "collapsed" | "collapsedOnMobile";
Expand Down Expand Up @@ -70,6 +72,11 @@ class App extends Component<AppProps, {}> {

componentDidMount() {
window.onpopstate = this.onPopState;

document.body.classList.toggle(
"dark-theme",
this.settingsStore.themeConfig.config.darkTheme
);
}

componentWillUnmount() {
Expand Down
2 changes: 1 addition & 1 deletion ui/src/Components/Grid/AlertGrid/AlertGroup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ const AlertGroup = observer(
setIsMenuOpen={this.renderConfig.setIsMenuOpen}
/>
{this.collapse.value ? null : (
<div className="card-body bg-white px-2 py-1">
<div className="card-body px-2 py-1 bg-white components-grid-alertgrid-card">
<ul className="list-group">
{group.alerts
.slice(0, this.renderConfig.alertsToRender)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const AlertGroupTitleBarColor = observer(
const { settingsStore } = this.props;

return (
<div className="form-group mb-0">
<div className="form-group mb-2">
<div className="form-check form-check-inline">
<span className="custom-control custom-switch">
<input
Expand Down
57 changes: 57 additions & 0 deletions ui/src/Components/MainModal/Configuration/ThemeConfiguration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { Component } from "react";
import PropTypes from "prop-types";

import { action } from "mobx";
import { observer } from "mobx-react";

import { Settings } from "Stores/Settings";

const ThemeConfiguration = observer(
class ThemeConfiguration extends Component {
static propTypes = {
settingsStore: PropTypes.instanceOf(Settings).isRequired
};

onChange = action(event => {
const { settingsStore } = this.props;
settingsStore.themeConfig.config.darkTheme = event.target.checked;

document.body.classList.toggle(
"dark-theme",
settingsStore.themeConfig.config.darkTheme
);
});

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-theme"
className="custom-control-input"
type="checkbox"
value=""
checked={settingsStore.themeConfig.config.darkTheme || false}
onChange={this.onChange}
/>
<label
className="custom-control-label cursor-pointer mr-3"
htmlFor="configuration-theme"
>
Enable dark mode
</label>
<span className="ml-5 badge badge-danger align-text-bottom">
Experimental
</span>
</span>
</div>
</div>
);
}
}
);

export { ThemeConfiguration };
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react";

import { mount } from "enzyme";

import toDiffableHtml from "diffable-html";

import { Settings } from "Stores/Settings";
import { ThemeConfiguration } from "./ThemeConfiguration";

let settingsStore;
beforeEach(() => {
settingsStore = new Settings();
});

const FakeConfiguration = () => {
return mount(<ThemeConfiguration settingsStore={settingsStore} />);
};

describe("<ThemeConfiguration />", () => {
it("matches snapshot with default values", () => {
const tree = FakeConfiguration();
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});

it("darkTheme is 'false' by default", () => {
expect(settingsStore.themeConfig.config.darkTheme).toBe(false);
});

it("unchecking the checkbox sets stored darkTheme value to 'false'", done => {
const tree = FakeConfiguration();
const checkbox = tree.find("#configuration-theme");

settingsStore.themeConfig.config.darkTheme = true;
expect(settingsStore.themeConfig.config.darkTheme).toBe(true);
checkbox.simulate("change", { target: { checked: false } });
setTimeout(() => {
expect(settingsStore.themeConfig.config.darkTheme).toBe(false);
done();
}, 200);
});

it("checking the checkbox sets stored darkTheme value to 'true'", done => {
const tree = FakeConfiguration();
const checkbox = tree.find("#configuration-theme");

settingsStore.themeConfig.config.darkTheme = false;
expect(settingsStore.themeConfig.config.darkTheme).toBe(false);
checkbox.simulate("change", { target: { checked: true } });
setTimeout(() => {
expect(settingsStore.themeConfig.config.darkTheme).toBe(true);
done();
}, 200);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`<AlertGroupTitleBarColor /> matches snapshot with default values 1`] = `
"
<div class=\\"form-group mb-0\\">
<div class=\\"form-group mb-2\\">
<div class=\\"form-check form-check-inline\\">
<span class=\\"custom-control custom-switch\\">
<input id=\\"configuration-colortitlebar\\"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<ThemeConfiguration /> matches snapshot with default values 1`] = `
"
<div class=\\"form-group mb-0\\">
<div class=\\"form-check form-check-inline\\">
<span class=\\"custom-control custom-switch\\">
<input id=\\"configuration-theme\\"
class=\\"custom-control-input\\"
type=\\"checkbox\\"
value
>
<label class=\\"custom-control-label cursor-pointer mr-3\\"
for=\\"configuration-theme\\"
>
Enable dark mode
</label>
<span class=\\"ml-5 badge badge-danger align-text-bottom\\">
Experimental
</span>
</span>
</div>
</div>
"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
<div class=\\"Collapsible__trigger is-closed card-header cursor-pointer border-bottom-0\\">
<div class=\\"d-flex flex-row justify-content-between\\">
<div>
Alert group titlebar configuration
Theme
</div>
<div>
<svg aria-hidden=\\"true\\"
Expand All @@ -150,7 +150,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
style=\\"height:0;-webkit-transition:height 50ms linear;-ms-transition:height 50ms linear;transition:height 50ms linear;overflow:hidden\\"
>
<div class=\\"Collapsible__contentInner card-body my-2\\">
<div class=\\"form-group mb-0\\">
<div class=\\"form-group mb-2\\">
<div class=\\"form-check form-check-inline\\">
<span class=\\"custom-control custom-switch\\">
<input type=\\"checkbox\\"
Expand All @@ -166,6 +166,25 @@ exports[`<Configuration /> matches snapshot 1`] = `
</span>
</div>
</div>
<div class=\\"form-group mb-0\\">
<div class=\\"form-check form-check-inline\\">
<span class=\\"custom-control custom-switch\\">
<input type=\\"checkbox\\"
id=\\"configuration-theme\\"
class=\\"custom-control-input\\"
value
>
<label class=\\"custom-control-label cursor-pointer mr-3\\"
for=\\"configuration-theme\\"
>
Enable dark mode
</label>
<span class=\\"ml-5 badge badge-danger align-text-bottom\\">
Experimental
</span>
</span>
</div>
</div>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 7a7102d

Please sign in to comment.