diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b97da0e81c..faa2f49faff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # 6.0.0-beta3 (unreleased) +### Minor +* **CLI**: Grafana CLI should preserve permissions for backend binaries for Linux and Darwin [#15500](https://github.com/grafana/grafana/issues/15500) +* **Alerting**: Allow image rendering 90 percent of alertTimeout [#15395](https://github.com/grafana/grafana/pull/15395) + +### Bug fixes +* **Influxdb**: Add support for alerting on InfluxDB queries that use the non_negative_difference function [#15415](https://github.com/grafana/grafana/issues/15415), thx [@kiran3394](https://github.com/kiran3394) +* **Alerting**: Fix percent_diff calculation when points are nulls [#15443](https://github.com/grafana/grafana/issues/15443), thx [@max-neverov](https://github.com/max-neverov) +* **Alerting**: Fixed handling of alert urls with true flags [#15454](https://github.com/grafana/grafana/issues/15454) + # 6.0.0-beta2 (2019-02-11) ### New Features @@ -20,20 +29,19 @@ * **Login**: Anonymous usage stats for token auth [#15288](https://github.com/grafana/grafana/issues/15288) * **AzureMonitor**: improve autocomplete for Log Analytics and App Insights editor [#15131](https://github.com/grafana/grafana/issues/15131) * **LDAP**: Fix IPA/FreeIPA v4.6.4 does not allow LDAP searches with empty attributes [#14432](https://github.com/grafana/grafana/issues/14432) +* **Provisioning**: Allow testing data sources that were added by config [#12164](https://github.com/grafana/grafana/issues/12164) ### Breaking changes * **Internal Metrics** Edition has been added to the build_info metric. This will break any Graphite queries using this metric. Edition will be a new label for the Prometheus metric. [#15363](https://github.com/grafana/grafana/pull/15363) -### 6.0.0-beta1 fixes +### Bug fixes -* **Postgres**: Fix default port not added when port not configured [#15189](https://github.com/grafana/grafana/issues/15189) -* **Alerting**: Fixes crash bug when alert notifier folders are missing [#15295](https://github.com/grafana/grafana/issues/15295) -* **Dashboard**: Fix save provisioned dashboard modal [#15219](https://github.com/grafana/grafana/pull/15219) -* **Dashboard**: Fix having a long query in prometheus dashboard query editor blocks 30% of the query field when on OSX and having native scrollbars [#15122](https://github.com/grafana/grafana/issues/15122) -* **Explore**: Fix issue with wrapping on long queries [#15222](https://github.com/grafana/grafana/issues/15222) -* **Explore**: Fix cut & paste adds newline before and after selection [#15223](https://github.com/grafana/grafana/issues/15223) -* **Dataproxy**: Fix global datasource proxy timeout not added to correct http client [#15258](https://github.com/grafana/grafana/issues/15258) [#5699](https://github.com/grafana/grafana/issues/5699) +* **Gauge**: Fix issue with gauge requests being cancelled [#15366](https://github.com/grafana/grafana/issues/15366) +* **Gauge**: Accept decimal inputs for thresholds [#15372](https://github.com/grafana/grafana/issues/15372) +* **UI**: Fix error caused by named colors that are not part of named colors palette [#15373](https://github.com/grafana/grafana/issues/15373) +* **Search**: Bug pressing special regexp chars in input fields [#12972](https://github.com/grafana/grafana/issues/12972) +* **Permissions**: No need to have edit permissions to be able to "Save as" [#13066](https://github.com/grafana/grafana/issues/13066) # 6.0.0-beta1 (2019-01-30) @@ -82,6 +90,13 @@ * **Prometheus**: Query for annotation always uses 60s step regardless of dashboard range, fixes [#14795](https://github.com/grafana/grafana/issues/14795) * **Annotations**: Fix creating annotation when graph panel has no data points position the popup outside viewport [#13765](https://github.com/grafana/grafana/issues/13765), thx [@banjeremy](https://github.com/banjeremy) * **Piechart/Flot**: Fixes multiple piechart instances with donut bug [#15062](https://github.com/grafana/grafana/pull/15062) +* **Postgres**: Fix default port not added when port not configured [#15189](https://github.com/grafana/grafana/issues/15189) +* **Alerting**: Fixes crash bug when alert notifier folders are missing [#15295](https://github.com/grafana/grafana/issues/15295) +* **Dashboard**: Fix save provisioned dashboard modal [#15219](https://github.com/grafana/grafana/pull/15219) +* **Dashboard**: Fix having a long query in prometheus dashboard query editor blocks 30% of the query field when on OSX and having native scrollbars [#15122](https://github.com/grafana/grafana/issues/15122) +* **Explore**: Fix issue with wrapping on long queries [#15222](https://github.com/grafana/grafana/issues/15222) +* **Explore**: Fix cut & paste adds newline before and after selection [#15223](https://github.com/grafana/grafana/issues/15223) +* **Dataproxy**: Fix global datasource proxy timeout not added to correct http client [#15258](https://github.com/grafana/grafana/issues/15258) [#5699](https://github.com/grafana/grafana/issues/5699) ### Breaking changes * **Text Panel**: The text panel does no longer by default allow unsantizied HTML. [#4117](https://github.com/grafana/grafana/issues/4117). This means that if you have text panels with scripts tags they will no longer work as before. To enable unsafe javascript execution in text panels enable the settings `disable_sanitize_html` under the section `[panels]` in your Grafana ini file, or set env variable `GF_PANELS_DISABLE_SANITIZE_HTML=true`. diff --git a/docs/sources/features/datasources/index.md b/docs/sources/features/datasources/index.md index a892f38a448..42ce3f84819 100644 --- a/docs/sources/features/datasources/index.md +++ b/docs/sources/features/datasources/index.md @@ -13,7 +13,6 @@ weight = 5 Grafana supports many different storage backends for your time series data (Data Source). Each Data Source has a specific Query Editor that is customized for the features and capabilities that the particular Data Source exposes. - ## Querying The query language and capabilities of each Data Source are obviously very different. You can combine data from multiple Data Sources onto a single Dashboard, but each Panel is tied to a specific Data Source that belongs to a particular Organization. @@ -28,6 +27,7 @@ The following datasources are officially supported: * [InfluxDB]({{< relref "influxdb.md" >}}) * [OpenTSDB]({{< relref "opentsdb.md" >}}) * [Prometheus]({{< relref "prometheus.md" >}}) +* [Loki]({{< relref "loki.md" >}}) * [MySQL]({{< relref "mysql.md" >}}) * [Postgres]({{< relref "postgres.md" >}}) * [Microsoft SQL Server (MSSQL)]({{< relref "mssql.md" >}}) diff --git a/docs/sources/features/datasources/loki.md b/docs/sources/features/datasources/loki.md new file mode 100644 index 00000000000..d43ef982b4b --- /dev/null +++ b/docs/sources/features/datasources/loki.md @@ -0,0 +1,119 @@ ++++ +title = "Using Loki in Grafana" +description = "Guide for using Loki in Grafana" +keywords = ["grafana", "loki", "logging", "guide"] +type = "docs" +aliases = ["/datasources/loki"] +[menu.docs] +name = "Loki" +parent = "datasources" +weight = 11 ++++ + +# Using Loki in Grafana + +> BETA: Querying Loki data requires Grafana's Explore section. +> Grafana v6.x comes with Explore enabled by default. +> In Grafana v5.3.x and v5.4.x. you need to enable Explore manually. +> Viewing Loki data in dashboard panels is not supported yet, but is being worked on. + +Grafana ships with built-in support for Loki, Grafana's log aggregation system. +Just add it as a datasource and you are ready to query your log data in [Explore](/features/explore). + +## Adding the data source to Grafana + +1. Open Grafana and make sure you are logged in. +2. In the side menu under the `Configuration` link you should find a link named `Data Sources`. +3. Click the `Add data source` button at the top. +4. Select `Loki` from the list of data sources. + +> NOTE: If you're not seeing the `Data Sources` link in your side menu it means that your current user does not have the `Admin` role for the current organization. + +| Name | Description | +| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| _Name_ | The datasource name. This is how you refer to the datasource in panels, queries, and Explore. | +| _Default_ | Default datasource means that it will be pre-selected for new panels. | +| _URL_ | The URL of the Loki instance, e.g., `http://localhost:3100` | +| _Maximum lines_ | Upper limit for number of log lines returned by Loki (default is 1000). Decrease if your browser is sluggish when displaying logs in Explore. | + +## Querying Logs + +Querying and displaying log data from Loki is available via [Explore](/features/explore). +Select the Loki data source, and then enter a log query to display your logs. + +> Viewing Loki data in dashboard panels is not supported yet, but is being worked on. + +### Log Queries + +A log query consists of two parts: **log stream selector**, and a **search expression**. For performance reasons you need to start by choosing a log stream by selecting a log label. + +The Logs Explorer (the `Log labels` button) next to the query field shows a list of labels of available log streams. An alternative way to write a query is to use the query field's autocomplete - you start by typing a left curly brace `{` and the autocomplete menu will suggest a list of labels. Press the `enter` key to execute the query. + +Once the result is returned, the log panel shows a list of log rows and a bar chart where the x-axis shows the time and the y-axis shows the frequency/count. + +
+ +
+ +
+ +### Log Stream Selector + +For the label part of the query expression, wrap it in curly braces `{}` and then use the key value syntax for selecting labels. Multiple label expressions are separated by a comma: + +`{app="mysql",name="mysql-backup"}` + +The following label matching operators are currently supported: + +* `=` exactly equal. +* `!=` not equal. +* `=~` regex-match. +* `!~` do not regex-match. + +Examples: + +* `{name=~"mysql.+"}` +* `{name!~"mysql.+"}` + +The [same rules that apply for Prometheus Label Selectors](https://prometheus.io/docs/prometheus/latest/querying/basics/#instant-vector-selectors) apply for Loki Log Stream Selectors. + +Another way to add a label selector, is in the table section, clicking on the **Filter** button beside a label will add the label to the query expression. This even works for multiple queries and will the label selector to each query. + +### Search Expression + +After writing the Log Stream Selector, you can filter the results further by writing a search expression. The search expression can be just text or a regex expression. + +Example queries: + +* `{job="mysql"} error` +* `{name="kafka"} tsdb-ops.*io:2003` +* `{instance=~"kafka-[23]",name="kafka"} kafka.server:type=ReplicaManager` + +## Templating + +Template variables are not yet supported by Loki. + +## Annotations + +Annotations are not yet supported by Loki. + +## Configure the Datasource with Provisioning + +You can set up the datasource via config files with Grafana's provisioning system. +You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + +Here is an example: + +```yaml +apiVersion: 1 + +datasources: + - name: Loki + type: loki + url: http://localhost:3100 + jsonData: + maxLines: 1000 +``` diff --git a/docs/sources/features/explore/index.md b/docs/sources/features/explore/index.md index 25af18c2a3d..bf5811baea6 100644 --- a/docs/sources/features/explore/index.md +++ b/docs/sources/features/explore/index.md @@ -67,9 +67,9 @@ The autocomplete menu can be trigger by pressing Ctrl + Space. The Autocomplete Suggestions can appear under the query field - click on them to update your query with the suggested change. -- For counters (monotonously increasing metrics), a rate function will be suggested. -- For buckets, a histogram function will be suggested. -- For recording rules, possible to expand the rules. +* For counters (monotonously increasing metrics), a rate function will be suggested. +* For buckets, a histogram function will be suggested. +* For recording rules, possible to expand the rules. ### Table Filters @@ -79,6 +79,8 @@ Click on the filter button - - - -
- -#### Log Stream Selector - -For the label part of the query expression, wrap it in curly braces `{}` and then use the key value syntax for selecting labels. Multiple label expressions are separated by a comma: - -`{app="mysql",name="mysql-backup"}` - -The following label matching operators are currently supported: - -- `=` exactly equal. -- `!=` not equal. -- `=~` regex-match. -- `!~` do not regex-match. - -Examples: - -- `{name=~"mysql.+"}` -- `{name!~"mysql.+"}` - -The [same rules that apply for Prometheus Label Selectors](https://prometheus.io/docs/prometheus/latest/querying/basics/#instant-vector-selectors) apply for Loki Log Stream Selectors. - -Another way to add a label selector, is in the table section, clicking on the **Filter** button beside a label will add the label to the query expression. This even works for multiple queries and will the label selector to each query. - -#### Search Expression - -After writing the Log Stream Selector, you can filter the results further by writing a search expression. The search expression can be just text or a regex expression. - -Example queries: - -- `{job="mysql"} error` -- `{name="kafka"} tsdb-ops.*io:2003` -- `{instance=~"kafka-[23]",name="kafka"} kafka.server:type=ReplicaManager` - ### Deduping Log data can be very repetitive and Explore can help by hiding duplicate log lines. There are a few different deduplication algorithms that you can use: -- `exact` Exact matches are done on the whole line, except for date fields. -- `numbers` Matches on the line after stripping out numbers (durations, IP addresses etc.). -- `signature` The most aggressive deduping - strips all letters and numbers, and matches on the remaining whitespace and punctuation. +* `exact` Exact matches are done on the whole line, except for date fields. +* `numbers` Matches on the line after stripping out numbers (durations, IP addresses etc.). +* `signature` The most aggressive deduping - strips all letters and numbers, and matches on the remaining whitespace and punctuation. ### Timestamp, Local time and Labels There are some other check boxes under the logging graph apart from the Deduping options. -- Timestamp: shows/hides the Timestamp column -- Local time: shows/hides the Local time column -- Labels: shows/hides the label filters column +* Timestamp: shows/hides the Timestamp column +* Local time: shows/hides the Local time column +* Labels: shows/hides the label filters column diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx index 5e5f477a733..70e29abc221 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.test.tsx @@ -84,9 +84,9 @@ describe('Get thresholds formatted', () => { it('should get the correct formatted values when thresholds are added', () => { const { instance } = setup({ thresholds: [ - { index: 2, value: 75, color: '#6ED0E0' }, - { index: 1, value: 50, color: '#EAB839' }, { index: 0, value: -Infinity, color: '#7EB26D' }, + { index: 1, value: 50, color: '#EAB839' }, + { index: 2, value: 75, color: '#6ED0E0' }, ], }); diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index 2f81d51e6cf..d46ddff8be3 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -87,16 +87,15 @@ export class Gauge extends PureComponent { getFormattedThresholds() { const { maxValue, minValue, thresholds, theme } = this.props; - const thresholdsSortedByIndex = [...thresholds].sort((t1, t2) => t1.index - t2.index); - const lastThreshold = thresholdsSortedByIndex[thresholdsSortedByIndex.length - 1]; + const lastThreshold = thresholds[thresholds.length - 1]; return [ - ...thresholdsSortedByIndex.map(threshold => { + ...thresholds.map(threshold => { if (threshold.index === 0) { return { value: minValue, color: getColorFromHexRgbOrName(threshold.color, theme.type) }; } - const previousThreshold = thresholdsSortedByIndex[threshold.index - 1]; + const previousThreshold = thresholds[threshold.index - 1]; return { value: threshold.value, color: getColorFromHexRgbOrName(previousThreshold.color, theme.type) }; }), { value: maxValue, color: getColorFromHexRgbOrName(lastThreshold.color, theme.type) }, diff --git a/packages/grafana-ui/src/components/Switch/Switch.tsx b/packages/grafana-ui/src/components/Switch/Switch.tsx index feee58386b8..f79de95fd2d 100644 --- a/packages/grafana-ui/src/components/Switch/Switch.tsx +++ b/packages/grafana-ui/src/components/Switch/Switch.tsx @@ -17,7 +17,7 @@ export interface State { export class Switch extends PureComponent { state = { - id: _.uniqueId(), + id: _.uniqueId('check-'), }; internalOnChange = (event: React.FormEvent) => { @@ -29,18 +29,20 @@ export class Switch extends PureComponent { render() { const { labelClass = '', switchClass = '', label, checked, transparent, className } = this.props; - const labelId = `check-${this.state.id}`; + const labelId = this.state.id; const labelClassName = `gf-form-label ${labelClass} ${transparent ? 'gf-form-label--transparent' : ''} pointer`; const switchClassName = `gf-form-switch ${switchClass} ${transparent ? 'gf-form-switch--transparent' : ''}`; return ( - +
+ +
); } } diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx index 2b6af67df22..ea94537c429 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.test.tsx @@ -1,9 +1,8 @@ import React, { ChangeEvent } from 'react'; -import { shallow } from 'enzyme'; - +import { mount } from 'enzyme'; import { ThresholdsEditor, Props } from './ThresholdsEditor'; -const setup = (propOverrides?: object) => { +const setup = (propOverrides?: Partial) => { const props: Props = { onChange: jest.fn(), thresholds: [], @@ -11,12 +10,26 @@ const setup = (propOverrides?: object) => { Object.assign(props, propOverrides); - return shallow().instance() as ThresholdsEditor; + const wrapper = mount(); + const instance = wrapper.instance() as ThresholdsEditor; + + return { + instance, + wrapper, + }; }; +describe('Render', () => { + it('should render with base threshold', () => { + const { wrapper } = setup(); + + expect(wrapper).toMatchSnapshot(); + }); +}); + describe('Initialization', () => { it('should add a base threshold if missing', () => { - const instance = setup(); + const { instance } = setup(); expect(instance.state.thresholds).toEqual([{ index: 0, value: -Infinity, color: '#7EB26D' }]); }); @@ -24,7 +37,7 @@ describe('Initialization', () => { describe('Add threshold', () => { it('should not add threshold at index 0', () => { - const instance = setup(); + const { instance } = setup(); instance.onAddThreshold(0); @@ -32,32 +45,32 @@ describe('Add threshold', () => { }); it('should add threshold', () => { - const instance = setup(); + const { instance } = setup(); instance.onAddThreshold(1); expect(instance.state.thresholds).toEqual([ - { index: 1, value: 50, color: '#EAB839' }, { index: 0, value: -Infinity, color: '#7EB26D' }, + { index: 1, value: 50, color: '#EAB839' }, ]); }); it('should add another threshold above a first', () => { - const instance = setup({ + const { instance } = setup({ thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }, { index: 1, value: 50, color: '#EAB839' }], }); instance.onAddThreshold(2); expect(instance.state.thresholds).toEqual([ - { index: 2, value: 75, color: '#6ED0E0' }, - { index: 1, value: 50, color: '#EAB839' }, { index: 0, value: -Infinity, color: '#7EB26D' }, + { index: 1, value: 50, color: '#EAB839' }, + { index: 2, value: 75, color: '#6ED0E0' }, ]); }); it('should add another threshold between first and second index', () => { - const instance = setup({ + const { instance } = setup({ thresholds: [ { index: 0, value: -Infinity, color: '#7EB26D' }, { index: 1, value: 50, color: '#EAB839' }, @@ -68,10 +81,10 @@ describe('Add threshold', () => { instance.onAddThreshold(2); expect(instance.state.thresholds).toEqual([ - { index: 3, value: 75, color: '#6ED0E0' }, - { index: 2, value: 62.5, color: '#EF843C' }, - { index: 1, value: 50, color: '#EAB839' }, { index: 0, value: -Infinity, color: '#7EB26D' }, + { index: 1, value: 50, color: '#EAB839' }, + { index: 2, value: 62.5, color: '#EF843C' }, + { index: 3, value: 75, color: '#6ED0E0' }, ]); }); }); @@ -83,7 +96,7 @@ describe('Remove threshold', () => { { index: 1, value: 50, color: '#EAB839' }, { index: 2, value: 75, color: '#6ED0E0' }, ]; - const instance = setup({ thresholds }); + const { instance } = setup({ thresholds }); instance.onRemoveThreshold(thresholds[0]); @@ -96,9 +109,7 @@ describe('Remove threshold', () => { { index: 1, value: 50, color: '#EAB839' }, { index: 2, value: 75, color: '#6ED0E0' }, ]; - const instance = setup({ - thresholds, - }); + const { instance } = setup({ thresholds }); instance.onRemoveThreshold(thresholds[1]); @@ -116,7 +127,7 @@ describe('change threshold value', () => { { index: 1, value: 50, color: '#EAB839' }, { index: 2, value: 75, color: '#6ED0E0' }, ]; - const instance = setup({ thresholds }); + const { instance } = setup({ thresholds }); const mockEvent = ({ target: { value: '12' } } as any) as ChangeEvent; @@ -126,7 +137,7 @@ describe('change threshold value', () => { }); it('should update value', () => { - const instance = setup(); + const { instance } = setup(); const thresholds = [ { index: 0, value: -Infinity, color: '#7EB26D' }, { index: 1, value: 50, color: '#EAB839' }, @@ -150,24 +161,24 @@ describe('change threshold value', () => { }); describe('on blur threshold value', () => { - it('should resort rows and update indexes', () => { - const instance = setup(); + it.only('should resort rows and update indexes', () => { + const { instance } = setup(); const thresholds = [ { index: 0, value: -Infinity, color: '#7EB26D' }, { index: 1, value: 78, color: '#EAB839' }, { index: 2, value: 75, color: '#6ED0E0' }, ]; - instance.state = { + instance.setState({ thresholds, - }; + }); instance.onBlur(); expect(instance.state.thresholds).toEqual([ - { index: 2, value: 78, color: '#EAB839' }, - { index: 1, value: 75, color: '#6ED0E0' }, { index: 0, value: -Infinity, color: '#7EB26D' }, + { index: 1, value: 75, color: '#6ED0E0' }, + { index: 2, value: 78, color: '#EAB839' }, ]); }); }); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index f4db23d6656..d613a911b03 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -1,7 +1,7 @@ import React, { PureComponent, ChangeEvent } from 'react'; import { Threshold } from '../../types'; -import { ColorPicker } from '../ColorPicker/ColorPicker'; -import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; +import { ColorPicker } from '..'; +import { PanelOptionsGroup } from '..'; import { colors } from '../../utils'; import { getColorFromHexRgbOrName, ThemeContext } from '@grafana/ui'; @@ -54,16 +54,16 @@ export class ThresholdsEditor extends PureComponent { const value = afterThresholdValue - (afterThresholdValue - beforeThresholdValue) / 2; // Set a color - const color = colors.filter(c => newThresholds.some(t => t.color === c) === false)[0]; + const color = colors.filter(c => !newThresholds.some(t => t.color === c))[0]; this.setState( { thresholds: this.sortThresholds([ ...newThresholds, { + color, index, value: value as number, - color, }, ]), }, @@ -137,10 +137,11 @@ export class ThresholdsEditor extends PureComponent { onBlur = () => { this.setState(prevState => { const sortThresholds = this.sortThresholds([...prevState.thresholds]); - let index = sortThresholds.length - 1; + let index = 0; sortThresholds.forEach(t => { - t.index = index--; + t.index = index++; }); + return { thresholds: sortThresholds }; }); @@ -153,12 +154,11 @@ export class ThresholdsEditor extends PureComponent { sortThresholds = (thresholds: Threshold[]) => { return thresholds.sort((t1, t2) => { - return t2.value - t1.value; + return t1.value - t2.value; }); }; renderInput = (threshold: Threshold) => { - const value = threshold.index === 0 ? 'Base' : threshold.value; return (
@@ -169,51 +169,60 @@ export class ThresholdsEditor extends PureComponent {
)} -
- this.onChangeThresholdValue(event, threshold)} - value={value} - onBlur={this.onBlur} - readOnly={threshold.index === 0} - /> -
- {threshold.index > 0 && ( -
this.onRemoveThreshold(threshold)}> - + {threshold.index === 0 && ( +
+
)} + {threshold.index > 0 && ( + <> +
+ this.onChangeThresholdValue(event, threshold)} + value={threshold.value} + onBlur={this.onBlur} + readOnly={threshold.index === 0} + /> +
+
this.onRemoveThreshold(threshold)}> + +
+ + )}
); }; render() { const { thresholds } = this.state; - return ( {theme => { return (
- {thresholds.map((threshold, index) => { - return ( -
-
this.onAddThreshold(threshold.index + 1)} - > - + {thresholds + .slice(0) + .reverse() + .map((threshold, index) => { + return ( +
+
this.onAddThreshold(threshold.index + 1)} + > + +
+
+
{this.renderInput(threshold)}
-
-
{this.renderInput(threshold)}
-
- ); - })} + ); + })}
); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss index 8ef59bf08af..af70fd86f7a 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss +++ b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss @@ -43,7 +43,7 @@ } .thresholds-row-input { - margin-top: 49px; + margin-top: 44px; margin-left: 2px; } diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap b/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap new file mode 100644 index 00000000000..b0dc025090b --- /dev/null +++ b/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render with base threshold 1`] = ` + + + +`; diff --git a/packages/grafana-ui/src/utils/valueFormats/categories.ts b/packages/grafana-ui/src/utils/valueFormats/categories.ts index efba2cc5b79..e127285b473 100644 --- a/packages/grafana-ui/src/utils/valueFormats/categories.ts +++ b/packages/grafana-ui/src/utils/valueFormats/categories.ts @@ -191,6 +191,7 @@ export const getCategories = (): ValueFormatCategory[] => [ { name: 'Litre/hour', id: 'litreh', fn: toFixedUnit('l/h') }, { name: 'Litre/min (l/min)', id: 'flowlpm', fn: toFixedUnit('l/min') }, { name: 'milliLitre/min (mL/min)', id: 'flowmlpm', fn: toFixedUnit('mL/min') }, + { name: 'Lux (lx)', id: 'lux', fn: toFixedUnit('lux') }, ], }, { diff --git a/pkg/api/index.go b/pkg/api/index.go index e90db84016d..248ebf63f0f 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -145,7 +145,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er Text: "Explore", Id: "explore", SubTitle: "Explore your data", - Icon: "fa fa-rocket", + Icon: "gicon gicon-explore", Url: setting.AppSubUrl + "/explore", }) } diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index f88bb9bbfff..d758633fea5 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -57,6 +57,8 @@ func installCommand(c CommandLine) error { return InstallPlugin(pluginToInstall, version, c) } +// InstallPlugin downloads the plugin code as a zip file from the Grafana.com API +// and then extracts the zip into the plugins directory. func InstallPlugin(pluginName, version string, c CommandLine) error { pluginFolder := c.PluginDirectory() downloadURL := c.PluginURL() @@ -152,6 +154,10 @@ func downloadFile(pluginName, filePath, url string) (err error) { return err } + return extractFiles(body, pluginName, filePath) +} + +func extractFiles(body []byte, pluginName string, filePath string) error { r, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) if err != nil { return err @@ -161,12 +167,18 @@ func downloadFile(pluginName, filePath, url string) (err error) { if zf.FileInfo().IsDir() { err := os.Mkdir(newFile, 0777) - if PermissionsError(err) { + if permissionsError(err) { return fmt.Errorf(permissionsDeniedMessage, newFile) } } else { - dst, err := os.Create(newFile) - if PermissionsError(err) { + fileMode := zf.Mode() + + if strings.HasSuffix(newFile, "_linux_amd64") || strings.HasSuffix(newFile, "_darwin_amd64") { + fileMode = os.FileMode(0755) + } + + dst, err := os.OpenFile(newFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileMode) + if permissionsError(err) { return fmt.Errorf(permissionsDeniedMessage, newFile) } @@ -184,6 +196,6 @@ func downloadFile(pluginName, filePath, url string) (err error) { return nil } -func PermissionsError(err error) bool { +func permissionsError(err error) bool { return err != nil && strings.Contains(err.Error(), "permission denied") } diff --git a/pkg/cmd/grafana-cli/commands/install_command_test.go b/pkg/cmd/grafana-cli/commands/install_command_test.go index 52b329adf7f..3554dda82a9 100644 --- a/pkg/cmd/grafana-cli/commands/install_command_test.go +++ b/pkg/cmd/grafana-cli/commands/install_command_test.go @@ -1,6 +1,8 @@ package commands import ( + "io/ioutil" + "os" "testing" . "github.com/smartystreets/goconvey/convey" @@ -37,3 +39,42 @@ func TestFoldernameReplacement(t *testing.T) { }) }) } + +func TestExtractFiles(t *testing.T) { + Convey("Should preserve file permissions for plugin backend binaries for linux and darwin", t, func() { + err := os.RemoveAll("testdata/fake-plugins-dir") + So(err, ShouldBeNil) + + err = os.MkdirAll("testdata/fake-plugins-dir", 0774) + So(err, ShouldBeNil) + + body, err := ioutil.ReadFile("testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip") + So(err, ShouldBeNil) + + err = extractFiles(body, "grafana-simple-json-datasource", "testdata/fake-plugins-dir") + So(err, ShouldBeNil) + + //File in zip has permissions 777 + fileInfo, err := os.Stat("testdata/fake-plugins-dir/grafana-simple-json-datasource/simple-plugin_darwin_amd64") + So(err, ShouldBeNil) + So(fileInfo.Mode().String(), ShouldEqual, "-rwxr-xr-x") + + //File in zip has permission 664 + fileInfo, err = os.Stat("testdata/fake-plugins-dir/grafana-simple-json-datasource/simple-plugin_linux_amd64") + So(err, ShouldBeNil) + So(fileInfo.Mode().String(), ShouldEqual, "-rwxr-xr-x") + + //File in zip has permission 644 + fileInfo, err = os.Stat("testdata/fake-plugins-dir/grafana-simple-json-datasource/simple-plugin_windows_amd64.exe") + So(err, ShouldBeNil) + So(fileInfo.Mode().String(), ShouldEqual, "-rw-r--r--") + + //File in zip has permission 755 + fileInfo, err = os.Stat("testdata/fake-plugins-dir/grafana-simple-json-datasource/non-plugin-binary") + So(err, ShouldBeNil) + So(fileInfo.Mode().String(), ShouldEqual, "-rwxr-xr-x") + + err = os.RemoveAll("testdata/fake-plugins-dir") + So(err, ShouldBeNil) + }) +} diff --git a/pkg/cmd/grafana-cli/commands/testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip b/pkg/cmd/grafana-cli/commands/testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip new file mode 100644 index 00000000000..f9263ab3e78 Binary files /dev/null and b/pkg/cmd/grafana-cli/commands/testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip differ diff --git a/pkg/services/auth/token_cleanup.go b/pkg/services/auth/token_cleanup.go index aa5bc4856ab..1fe0996aa4c 100644 --- a/pkg/services/auth/token_cleanup.go +++ b/pkg/services/auth/token_cleanup.go @@ -52,6 +52,6 @@ func (srv *UserAuthTokenService) deleteExpiredTokens(maxInactiveLifetime, maxLif return 0, nil } - srv.log.Info("cleanup of expired auth tokens done", "count", affected) + srv.log.Debug("cleanup of expired auth tokens done", "count", affected) return affected, err } diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 8806e35c6b6..99ce3f904cd 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -65,7 +65,7 @@ export class DashNav extends PureComponent { }); } else { this.props.updateLocation({ - query: { panelId: null, edit: null, fullscreen: null }, + query: { panelId: null, edit: null, fullscreen: null, tab: null }, partial: true, }); } diff --git a/public/app/features/dashboard/services/DashboardSrv.ts b/public/app/features/dashboard/services/DashboardSrv.ts index e20c0ed5a31..c82aed13e44 100644 --- a/public/app/features/dashboard/services/DashboardSrv.ts +++ b/public/app/features/dashboard/services/DashboardSrv.ts @@ -43,6 +43,7 @@ export class DashboardSrv { delete urlParams.fullscreen; delete urlParams.edit; delete urlParams.panelId; + delete urlParams.tab; this.$location.search(urlParams); return; } @@ -58,6 +59,7 @@ export class DashboardSrv { urlParams.edit = true; } else { delete urlParams.edit; + delete urlParams.tab; } if (options.panelId || options.panelId === 0) { diff --git a/public/app/features/dashboard/state/DashboardMigrator.ts b/public/app/features/dashboard/state/DashboardMigrator.ts index 1aa310308d5..c43f4c491c0 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.ts @@ -399,6 +399,12 @@ export class DashboardMigrator { prefix: panel.options.prefix, suffix: panel.options.suffix, }; + + // correct order + if (panel.options.thresholds) { + panel.options.thresholds.reverse(); + } + // this options prop was due to a bug delete panel.options.options; delete panel.options.unit; diff --git a/public/app/features/dashboard/state/PanelModel.test.ts b/public/app/features/dashboard/state/PanelModel.test.ts index d96838dc640..079946b1521 100644 --- a/public/app/features/dashboard/state/PanelModel.test.ts +++ b/public/app/features/dashboard/state/PanelModel.test.ts @@ -10,6 +10,20 @@ describe('PanelModel', () => { type: 'table', showColumns: true, targets: [{ refId: 'A' }, { noRefId: true }], + options: { + thresholds: [ + { + color: '#F2495C', + index: 1, + value: 50, + }, + { + color: '#73BF69', + index: 0, + value: null, + }, + ], + }, }); }); @@ -35,6 +49,21 @@ describe('PanelModel', () => { expect(saveModel.events).toBe(undefined); }); + it('should restore -Infinity value for base threshold', () => { + expect(model.options.thresholds).toEqual([ + { + color: '#F2495C', + index: 1, + value: 50, + }, + { + color: '#73BF69', + index: 0, + value: -Infinity, + }, + ]); + }); + describe('when changing panel type', () => { beforeEach(() => { model.changeType('graph', true); diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index fda586d2776..ac3722d61c2 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; // Types import { Emitter } from 'app/core/utils/emitter'; -import { DataQuery, TimeSeries } from '@grafana/ui'; +import { DataQuery, TimeSeries, Threshold } from '@grafana/ui'; import { TableData } from '@grafana/ui/src'; export interface GridPos { @@ -46,8 +46,6 @@ const mustKeepProps: { [str: string]: boolean } = { timeFrom: true, timeShift: true, hideTimeOverride: true, - maxDataPoints: true, - interval: true, description: true, links: true, fullscreen: true, @@ -91,7 +89,9 @@ export class PanelModel { timeFrom?: any; timeShift?: any; hideTimeOverride?: any; - options: object; + options: { + [key: string]: any; + }; maxDataPoints?: number; interval?: string; @@ -119,6 +119,8 @@ export class PanelModel { _.defaultsDeep(this, _.cloneDeep(defaults)); // queries must have refId this.ensureQueryIds(); + + this.restoreInfintyForThresholds(); } ensureQueryIds() { @@ -131,6 +133,19 @@ export class PanelModel { } } + restoreInfintyForThresholds() { + if (this.options && this.options.thresholds) { + this.options.thresholds = this.options.thresholds.map((threshold: Threshold) => { + // JSON serialization of -Infinity is 'null' so lets convert it back to -Infinity + if (threshold.index === 0 && threshold.value === null) { + return { ...threshold, value: -Infinity }; + } + + return threshold; + }); + } + } + getOptions(panelDefaults) { return _.defaultsDeep(this.options || {}, panelDefaults); } diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 786998c96c1..adcd0c8afa8 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -103,18 +103,16 @@ export class UnConnectedExploreToolbar extends PureComponent {
{exploreId === 'left' && ( - + Explore )}
-
- {exploreId === 'right' && ( - - - - )} -
+ {exploreId === 'right' && ( + + + + )}
@@ -156,7 +154,7 @@ export class UnConnectedExploreToolbar extends PureComponent { splitted, title: 'Run Query', onClick: this.onRunQuery, - buttonClassName: 'navbar-button--primary', + buttonClassName: 'navbar-button--secondary', iconClassName: loading ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-level-down fa-fw run-icon', iconSide: IconSide.right, })} diff --git a/public/app/plugins/datasource/grafana/datasource.ts b/public/app/plugins/datasource/grafana/datasource.ts index 3bf772d160c..0e779b234eb 100644 --- a/public/app/plugins/datasource/grafana/datasource.ts +++ b/public/app/plugins/datasource/grafana/datasource.ts @@ -57,10 +57,17 @@ class GrafanaDatasource { if (!_.isArray(options.annotation.tags) || options.annotation.tags.length === 0) { return this.$q.when([]); } + const delimiter = '__delimiter__'; const tags = []; for (const t of params.tags) { - const renderedValues = this.templateSrv.replace(t, {}, 'pipe'); - for (const tt of renderedValues.split('|')) { + const renderedValues = this.templateSrv.replace(t, {}, value => { + if (typeof value === 'string') { + return value; + } + + return value.join(delimiter); + }); + for (const tt of renderedValues.split(delimiter)) { tags.push(tt); } } diff --git a/public/app/plugins/datasource/grafana/specs/datasource.test.ts b/public/app/plugins/datasource/grafana/specs/datasource.test.ts index 3a52e278690..3c0205640e2 100644 --- a/public/app/plugins/datasource/grafana/specs/datasource.test.ts +++ b/public/app/plugins/datasource/grafana/specs/datasource.test.ts @@ -14,7 +14,7 @@ describe('grafana data source', () => { const templateSrvStub = { replace: val => { - return val.replace('$var2', 'replaced|replaced2').replace('$var', 'replaced'); + return val.replace('$var2', 'replaced__delimiter__replaced2').replace('$var', 'replaced'); }, }; diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index a3a3fadf6bf..6dd6d4a77c4 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -1,4 +1,8 @@ -import { GraphPanel } from './GraphPanel'; -import { GraphPanelEditor } from './GraphPanelEditor'; +import { ReactPanelPlugin } from '@grafana/ui'; -export { GraphPanel as Panel, GraphPanelEditor as PanelOptions }; +import { GraphPanelEditor } from './GraphPanelEditor'; +import { GraphPanel } from './GraphPanel'; +import { Options } from './types'; + +export const reactPanel = new ReactPanelPlugin(GraphPanel); +reactPanel.setEditor(GraphPanelEditor); diff --git a/public/img/icons_dark_theme/icon_explore.svg b/public/img/icons_dark_theme/icon_explore.svg new file mode 100644 index 00000000000..8d7e1b93fce --- /dev/null +++ b/public/img/icons_dark_theme/icon_explore.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/public/img/icons_light_theme/icon_explore.svg b/public/img/icons_light_theme/icon_explore.svg new file mode 100644 index 00000000000..8732b2d9ae8 --- /dev/null +++ b/public/img/icons_light_theme/icon_explore.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 739ccb6c412..3170de5ba1a 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -60,7 +60,6 @@ @import 'components/panel_text'; @import 'components/panel_heatmap'; @import 'components/panel_logs'; -@import 'components/settings_permissions'; @import 'components/tagsinput'; @import 'components/tables_lists'; @import 'components/search'; @@ -110,7 +109,6 @@ @import 'pages/admin'; @import 'pages/alerting'; @import 'pages/history'; -@import 'pages/plugins'; @import 'pages/signup'; @import 'pages/styleguide'; @import 'pages/errorpage'; diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index 481500be2c1..46b6c923f0a 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -12,28 +12,31 @@ $sapphire-shade: #1f60c4; $lobster-base: #e02f44; $lobster-shade: #c4162a; $forest-light: #96d98d; -$forest-base: #37872d; -$forest-shade: #19730e; $green-base: #299c46; $green-shade: #23843b; // Grays // ------------------------- + $black: #000; $dark-1: #141414; -$dark-2: #1f1f20; -$dark-3: #262628; -$dark-4: #333333; -$dark-5: #444444; +$dark-2: #161719; +$dark-3: #1f1f20; +$dark-4: #212124; +$dark-5: #222426; +$dark-6: #262628; +$dark-7: #292a2d; +$dark-8: #2f2f32; +$dark-9: #343436; +$dark-10: #424345; $gray-1: #555555; $gray-2: #8e8e8e; $gray-3: #b3b3b3; $gray-4: #d8d9da; $gray-5: #ececec; -$gray-7: #fbfbfb; -$gray-blue: #212327; $input-black: #09090b; +$gray-blue: #212327; $white: #fff; @@ -66,14 +69,14 @@ $critical: $lobster-base; // Scaffolding // ------------------------- -$body-bg: rgb(23, 24, 25); -$page-bg: rgb(22, 23, 25); +$body-bg: $dark-2; +$page-bg: $dark-2; $body-color: $gray-4; $text-color: $gray-4; $text-color-strong: $white; $text-color-weak: $gray-2; -$text-color-faint: $dark-5; +$text-color-faint: $dark-10; $text-color-emphasis: $gray-5; $text-shadow-faint: 1px 1px 4px rgb(45, 45, 45); @@ -87,8 +90,8 @@ $brand-gradient: linear-gradient( rgba(255, 68, 0, 0.7) 100% ); -$page-gradient: linear-gradient(180deg, #222426 10px, rgb(22, 23, 25) 100px); -$edit-gradient: linear-gradient(180deg, rgb(22, 23, 25) 50%, #090909); +$page-gradient: linear-gradient(180deg, $dark-5 10px, dark-2 100px); +$edit-gradient: linear-gradient(180deg, $dark-2 50%, $input-black); // Links // ------------------------- @@ -100,60 +103,58 @@ $external-link-color: $sapphire-light; // Typography // ------------------------- $headings-color: darken($white, 11%); -$abbr-border-color: $gray-3 !default; +$abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; -$hr-border-color: $dark-4; +$hr-border-color: $dark-9; // Panel // ------------------------- -$panel-bg: #212124; +$panel-bg: $dark-4; $panel-border: solid 1px $dark-1; -$panel-header-hover-bg: $dark-4; +$panel-header-hover-bg: $dark-9; $panel-corner: $panel-bg; // page header -$page-header-bg: linear-gradient(90deg, #292a2d, black); -$page-header-shadow: inset 0px -4px 14px $dark-2; -$page-header-border-color: $dark-4; +$page-header-bg: linear-gradient(90deg, $dark-7, $black); +$page-header-shadow: inset 0px -4px 14px $dark-3; +$page-header-border-color: $dark-9; $divider-border-color: $gray-1; // Graphite Target Editor -$tight-form-bg: $dark-3; -$tight-form-func-bg: $dark-4; -$tight-form-func-highlight-bg: $dark-5; +$tight-form-func-bg: $dark-9; +$tight-form-func-highlight-bg: $dark-10; $modal-backdrop-bg: #353c42; $code-tag-bg: $dark-1; -$code-tag-border: $dark-4; +$code-tag-border: $dark-9; // cards -$card-background: linear-gradient(135deg, #2f2f32, #262628); -$card-background-hover: linear-gradient(135deg, #343436, #262628); +$card-background: linear-gradient(135deg, $dark-8, $dark-6); +$card-background-hover: linear-gradient(135deg, $dark-9, $dark-6); $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3); // Lists $list-item-bg: $card-background; -$list-item-hover-bg: lighten($gray-blue, 2%); +$list-item-hover-bg: $card-background-hover; $list-item-link-color: $text-color; $list-item-shadow: $card-shadow; $empty-list-cta-bg: $gray-blue; // Scrollbars -$scrollbarBackground: #404357; -$scrollbarBackground2: #3a3a3a; -$scrollbarBorder: black; +$scrollbarBackground: $dark-9; +$scrollbarBackground2: $dark-9; +$scrollbarBorder: $dark-10; // Tables // ------------------------- -$table-bg: transparent; // overall background-color -$table-bg-accent: $dark-3; // for striping -$table-border: $dark-3; // table and cell border +$table-bg-accent: $dark-6; // for striping +$table-border: $dark-6; // table and cell border -$table-bg-odd: $dark-2; -$table-bg-hover: $dark-3; +$table-bg-odd: $dark-3; +$table-bg-hover: $dark-6; // Buttons // ------------------------- @@ -170,8 +171,8 @@ $btn-success-bg-hl: $green-shade; $btn-danger-bg: $lobster-base; $btn-danger-bg-hl: $lobster-shade; -$btn-inverse-bg: $dark-3; -$btn-inverse-bg-hl: lighten($dark-3, 4%); +$btn-inverse-bg: $dark-6; +$btn-inverse-bg-hl: lighten($dark-6, 4%); $btn-inverse-text-color: $link-color; $btn-inverse-text-shadow: 0px 1px 0 rgba(0, 0, 0, 0.1); @@ -179,24 +180,24 @@ $btn-link-color: $gray-3; $iconContainerBackground: $black; -$btn-divider-left: $dark-4; -$btn-divider-right: $dark-2; +$btn-divider-left: $dark-9; +$btn-divider-right: $dark-3; $btn-drag-image: '../img/grab_dark.svg'; // Forms // ------------------------- $input-bg: $input-black; -$input-bg-disabled: $dark-3; +$input-bg-disabled: $dark-6; $input-color: $gray-4; -$input-border-color: $dark-3; +$input-border-color: $dark-6; $input-box-shadow: inset 1px 0px 0.3rem 0px rgba(150, 150, 150, 0.1); -$input-border-focus: $input-border-color; -$input-box-shadow-focus: rgba(102, 175, 233, 0.6); +$input-border-focus: $dark-6 !default; +$input-box-shadow-focus: $sapphire-light !default; $input-color-placeholder: $gray-1 !default; $input-label-bg: $gray-blue; -$input-label-border-color: $dark-3; +$input-label-border-color: $dark-6; $input-color-select-arrow: $white; // Input placeholder text color @@ -208,12 +209,12 @@ $search-filter-box-bg: $gray-blue; // Typeahead $typeahead-shadow: 0 5px 10px 0 $black; -$typeahead-selected-bg: $dark-4; +$typeahead-selected-bg: $dark-9; $typeahead-selected-color: $yellow; // Dropdowns // ------------------------- -$dropdownBackground: $dark-3; +$dropdownBackground: $dark-6; $dropdownBorder: rgba(0, 0, 0, 0.2); $dropdownDividerTop: transparent; $dropdownDividerBottom: #444; @@ -222,7 +223,7 @@ $dropdownLinkColor: $text-color; $dropdownLinkColorHover: $white; $dropdownLinkColorActive: $white; -$dropdownLinkBackgroundHover: $dark-4; +$dropdownLinkBackgroundHover: $dark-9; // Horizontal forms & lists // ------------------------- @@ -233,10 +234,7 @@ $horizontalComponentOffset: 180px; $navbarHeight: 55px; $navbarBackground: $panel-bg; -$navbarBorder: 1px solid $dark-3; -$navbarShadow: 0 0 20px black; - -$navbarLinkColor: $gray-4; +$navbarBorder: 1px solid $dark-6; $navbarButtonBackground: $navbarBackground; $navbarButtonBackgroundHighlight: $body-bg; @@ -247,19 +245,19 @@ $navbar-button-border: #2f2f32; // ------------------------- $side-menu-bg: $black; $side-menu-bg-mobile: $side-menu-bg; -$side-menu-item-hover-bg: $dark-2; +$side-menu-item-hover-bg: $dark-3; $side-menu-shadow: 0 0 20px black; $side-menu-link-color: $link-color; // Menu dropdowns // ------------------------- $menu-dropdown-bg: $body-bg; -$menu-dropdown-hover-bg: $dark-2; +$menu-dropdown-hover-bg: $dark-3; $menu-dropdown-shadow: 5px 5px 20px -5px $black; // Tabs // ------------------------- -$tab-border-color: $dark-4; +$tab-border-color: $dark-9; // Toolbar $toolbar-bg: $input-black; @@ -276,9 +274,9 @@ $alert-warning-bg: linear-gradient(90deg, $lobster-base, $lobster-shade); $alert-info-bg: linear-gradient(100deg, $sapphire-base, $sapphire-shade); // popover -$popover-bg: $page-bg; +$popover-bg: $dark-2; $popover-color: $text-color; -$popover-border-color: $dark-4; +$popover-border-color: $dark-9; $popover-shadow: 0 0 20px black; $popover-help-bg: $btn-secondary-bg; @@ -324,13 +322,13 @@ $json-explorer-url-color: #027bff; // Changelog and diff // ------------------------- -$diff-label-bg: $dark-2; +$diff-label-bg: $dark-3; $diff-label-fg: $white; -$diff-group-bg: $dark-4; +$diff-group-bg: $dark-9; $diff-arrow-color: $white; -$diff-json-bg: $dark-4; +$diff-json-bg: $dark-9; $diff-json-fg: $gray-5; $diff-json-added: $sapphire-shade; @@ -342,7 +340,7 @@ $diff-json-new: #457740; $diff-json-changed-fg: $gray-5; $diff-json-changed-num: $text-color; -$diff-json-icon: $gray-7; +$diff-json-icon: $gray-5; //Submenu $variable-option-bg: $dropdownLinkBackgroundHover; @@ -350,7 +348,7 @@ $variable-option-bg: $dropdownLinkBackgroundHover; //Switch Slider // ------------------------- $switch-bg: $input-bg; -$switch-slider-color: $dark-2; +$switch-slider-color: $dark-3; $switch-slider-off-bg: $gray-1; $switch-slider-on-bg: linear-gradient(90deg, #eb7b18, #d44a3a); $switch-slider-shadow: 0 0 3px black; @@ -366,8 +364,8 @@ $checkbox-color: $dark-1; // ------------------------- $panel-editor-shadow: 0 0 20px black; $panel-editor-side-menu-shadow: drop-shadow(0 0 10px $black); -$panel-editor-viz-item-shadow: 0 0 8px $dark-5; -$panel-editor-viz-item-border: 1px solid $dark-5; +$panel-editor-viz-item-shadow: 0 0 8px $dark-10; +$panel-editor-viz-item-border: 1px solid $dark-10; $panel-editor-viz-item-shadow-hover: 0 0 4px $sapphire-light; $panel-editor-viz-item-border-hover: 1px solid $sapphire-light; $panel-editor-viz-item-bg: $input-black; @@ -387,8 +385,8 @@ $logs-color-unkown: $gray-2; // toggle-group $button-toggle-group-btn-active-bg: linear-gradient(90deg, #eb7b18, #d44a3a); $button-toggle-group-btn-active-shadow: inset 0 0 4px $black; -$button-toggle-group-btn-seperator-border: 1px solid $page-bg; +$button-toggle-group-btn-seperator-border: 1px solid $dark-2; -$vertical-resize-handle-bg: $dark-5; +$vertical-resize-handle-bg: $dark-10; $vertical-resize-handle-dots: $gray-1; $vertical-resize-handle-dots-hover: $gray-2; diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index 57ab0d9e12a..94c2134313f 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -6,13 +6,11 @@ $theme-name: light; // New Colors // ------------------------- $sapphire-faint: #f5f9ff; -$sapphire-light: #a8caff; +$sapphire-light: #5794f2; $sapphire-base: #3274d9; $sapphire-shade: #1f60c4; $lobster-base: #e02f44; $lobster-shade: #c4162a; -$green-base: #37872d; -$green-shade: #19730e; $green-base: #3eb15b; $green-shade: #369b4f; $purple-shade: #8f3bb8; @@ -22,8 +20,6 @@ $yellow-base: #f2cc0c; // ------------------------- $black: #000; $dark-2: #1e2028; -$dark-3: #303133; -$dark-4: #35373f; $dark-5: #41444b; $gray-1: #52545c; $gray-2: #767980; @@ -38,7 +34,6 @@ $white: #fff; // Accent colors // ------------------------- $blue: #0083b3; -$blue-light: #00a8e6; $green: #3aa655; $red: $lobster-base; $yellow: #ff851b; @@ -96,7 +91,7 @@ $headings-color: $text-color; $abbr-border-color: $gray-2 !default; $text-muted: $text-color-weak; -$hr-border-color: $dark-3 !default; +$hr-border-color: $gray-4 !default; // Panel // ------------------------- @@ -113,13 +108,12 @@ $page-header-border-color: $gray-4; $divider-border-color: $gray-2; // Graphite Target Editor -$tight-form-bg: #eaebee; $tight-form-func-bg: $gray-5; $tight-form-func-highlight-bg: $gray-6; $modal-backdrop-bg: $body-bg; $code-tag-bg: $gray-6; -$code-tag-border: darken($code-tag-bg, 3%); +$code-tag-border: $gray-4; // cards $card-background: linear-gradient(135deg, $gray-6, $gray-5); @@ -135,13 +129,12 @@ $list-item-shadow: $card-shadow; $empty-list-cta-bg: $gray-6; // Scrollbars -$scrollbarBackground: $gray-5; -$scrollbarBackground2: $gray-5; -$scrollbarBorder: $gray-4; +$scrollbarBackground: $gray-4; +$scrollbarBackground2: $gray-4; +$scrollbarBorder: $gray-3; // Tables // ------------------------- -$table-bg: transparent; // overall background-color $table-bg-accent: $gray-5; // for striping $table-border: $gray-3; // table and cell border @@ -162,8 +155,9 @@ $btn-success-bg-hl: $green-shade; $btn-danger-bg: $lobster-base; $btn-danger-bg-hl: $lobster-shade; -$btn-inverse-bg: $gray-6; -$btn-inverse-bg-hl: darken($gray-6, 5%); +$btn-inverse-bg: $gray-5; +$btn-inverse-bg-hl: darken($gray-5, 5%); +$btn-inverse-bg-hl: $gray-4; $btn-inverse-text-color: $gray-1; $btn-inverse-text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4); @@ -181,10 +175,10 @@ $btn-drag-image: '../img/grab_light.svg'; $input-bg: $white; $input-bg-disabled: $gray-5; -$input-color: $dark-3; +$input-color: $dark-5; $input-border-color: $gray-5; $input-box-shadow: none; -$input-border-focus: $sapphire-light !default; +$input-border-focus: $gray-5 !default; $input-box-shadow-focus: $sapphire-light !default; $input-color-placeholder: $gray-4 !default; $input-label-bg: $gray-5; @@ -210,7 +204,7 @@ $dropdownBorder: $gray-4; $dropdownDividerTop: $gray-6; $dropdownDividerBottom: $white; -$dropdownLinkColor: $dark-3; +$dropdownLinkColor: $dark-5; $dropdownLinkColorHover: $link-color; $dropdownLinkColorActive: $link-color; @@ -225,10 +219,7 @@ $horizontalComponentOffset: 180px; $navbarHeight: 52px; $navbarBackground: $white; -$navbarBorder: 1px solid $gray-4; -$navbarShadow: 0 0 3px #c1c1c1; - -$navbarLinkColor: #444; +$navbarBorder: 1px solid $gray-5; $navbarButtonBackground: lighten($navbarBackground, 3%); $navbarButtonBackgroundHighlight: lighten($navbarBackground, 5%); @@ -265,7 +256,7 @@ $success-text-color: lighten($green, 10%); $alert-error-bg: linear-gradient(90deg, $lobster-base, $lobster-shade); $alert-success-bg: linear-gradient(90deg, $green-base, $green-shade); $alert-warning-bg: linear-gradient(90deg, $lobster-base, $lobster-shade); -$alert-info-bg: $sapphire-base; +$alert-info-bg: linear-gradient(100deg, $sapphire-base, $sapphire-shade); // popover $popover-bg: $page-bg; @@ -273,7 +264,7 @@ $popover-color: $text-color; $popover-border-color: $gray-5; $popover-shadow: 0 0 20px $white; -$popover-help-bg: $sapphire-base; +$popover-help-bg: $btn-secondary-bg; $popover-help-color: $gray-6; $popover-error-bg: $btn-danger-bg; @@ -316,13 +307,13 @@ $json-explorer-url-color: $sapphire-base; // Changelog and diff // ------------------------- -$diff-label-bg: $gray-5; +$diff-label-bg: $gray-7; $diff-label-fg: $gray-2; -$diff-arrow-color: $dark-3; -$diff-group-bg: $gray-7; +$diff-arrow-color: $dark-5; +$diff-group-bg: $gray-6; -$diff-json-bg: $gray-5; +$diff-json-bg: $gray-6; $diff-json-fg: $gray-1; $diff-json-added: $sapphire-shade; @@ -360,11 +351,13 @@ $panel-editor-shadow: 0px 0px 8px $gray-3; $panel-editor-side-menu-shadow: drop-shadow(0 0 2px $gray-3); $panel-editor-viz-item-shadow: 0 0 4px $gray-3; $panel-editor-viz-item-border: 1px solid $gray-3; -$panel-editor-viz-item-shadow-hover: 0 0 4px $blue-light; -$panel-editor-viz-item-border-hover: 1px solid $blue-light; +$panel-editor-viz-item-shadow-hover: 0 0 4px $sapphire-light; +$panel-editor-viz-item-border-hover: 1px solid $sapphire-light; $panel-editor-viz-item-bg: $white; $panel-editor-tabs-line-color: $dark-5; -$panel-editor-viz-item-bg-hover: lighten($blue, 62%); + +$panel-editor-viz-item-bg-hover: lighten($sapphire-base, 45%); + $panel-options-group-border: none; $panel-options-group-header-bg: $gray-5; diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss index 2d0cb394da1..8d171673f6b 100644 --- a/public/sass/base/_icons.scss +++ b/public/sass/base/_icons.scss @@ -192,6 +192,10 @@ background-image: url('../img/icons_#{$theme-name}_theme/icon_zoom_out.svg'); } +.gicon-explore { + background-image: url('../img/icons_#{$theme-name}_theme/icon_explore.svg'); +} + .sidemenu { .gicon-dashboard { background-image: url('../img/icons_dark_theme/icon_dashboard.svg'); @@ -205,6 +209,9 @@ .gicon-question { background-image: url('../img/icons_dark_theme/icon_question.svg'); } + .gicon-explore { + background-image: url('../img/icons_dark_theme/icon_explore.svg'); + } } .fa--permissions-list { diff --git a/public/sass/base/_reboot.scss b/public/sass/base/_reboot.scss index 8aac8bc815e..65cfafc107d 100644 --- a/public/sass/base/_reboot.scss +++ b/public/sass/base/_reboot.scss @@ -246,7 +246,7 @@ textarea { table { // Reset for nesting within parents with `background-color`. - background-color: $table-bg; + background-color: transparent; } caption { diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss index 9919a370a87..82aae6351af 100644 --- a/public/sass/base/_type.scss +++ b/public/sass/base/_type.scss @@ -266,7 +266,7 @@ dd { // Abbreviations and acronyms abbr[title] { cursor: help; - border-bottom: 1px dotted $gray-2; + border-bottom: 1px dotted $abbr-border-color; } abbr.initialism { diff --git a/public/sass/components/_navbar.scss b/public/sass/components/_navbar.scss index 86494f9e47a..a86b8c450a0 100644 --- a/public/sass/components/_navbar.scss +++ b/public/sass/components/_navbar.scss @@ -1,9 +1,8 @@ .navbar { position: relative; - padding-left: 20px; z-index: $zindex-navbar-fixed; height: $navbarHeight; - padding-right: 20px; + padding: 0 20px 0 50px; display: flex; flex-grow: 1; border-bottom: 1px solid transparent; @@ -57,15 +56,14 @@ white-space: nowrap; display: block; margin: 0; - color: darken($link-color, 5%); + color: $headings-color; font-size: $font-size-lg; - padding-left: 1rem; min-height: $navbarHeight; line-height: $navbarHeight; .fa-caret-down { font-size: 60%; - padding-left: 0.2rem; + padding-left: 6px; } &--search { @@ -73,12 +71,12 @@ } .gicon { + top: -2px; position: relative; - top: -1px; - font-size: 19px; + font-size: 17px; line-height: 8px; opacity: 0.75; - margin-right: 13px; + margin-right: 10px; display: none; } @@ -123,7 +121,7 @@ height: 30px; color: $text-muted; border: 1px solid $navbar-button-border; - margin-right: 3px; + margin-left: 3px; white-space: nowrap; .gicon { @@ -152,19 +150,19 @@ } } - &--primary { - @include buttonBackground($btn-primary-bg, $btn-primary-bg-hl); + &--secondary { + @include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl); } } @include media-breakpoint-up(sm) { .navbar { - padding-left: 50px; + padding-left: 60px; } .sidemenu-open { .navbar { - padding-left: 15px; + padding-left: 25px; margin-left: 0; } } @@ -180,7 +178,7 @@ display: flex; height: $navbarHeight; align-items: center; - padding-left: 7px; + padding-right: 13px; } .navbar-edit__back-btn { diff --git a/public/sass/components/_settings_permissions.scss b/public/sass/components/_settings_permissions.scss deleted file mode 100644 index 5864c58679e..00000000000 --- a/public/sass/components/_settings_permissions.scss +++ /dev/null @@ -1,32 +0,0 @@ -.permissionlist { - .permissionlist__section { - margin-bottom: $spacer*2; - } - - .permissionlist__section-header { - margin-bottom: $spacer; - display: flex; - } - - .permissionlist__section-header h6 { - margin: auto 5px; - color: $text-color-weak; - } - - .permissionlist__section-header__add-button { - margin-left: auto; - width: 105px; - } - - .permissionlist__item { - background-color: $tight-form-bg; - - &:hover { - background-color: $tight-form-func-bg; - } - } - - .permissionlist__item-buttons { - margin-left: auto; - } -} diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index 44941540598..f30bdb5c79e 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -253,7 +253,7 @@ li.sidemenu-org-switcher { } .sidemenu__logo_small_breakpoint { - padding: 16px 10px 26px; + padding: 14px 10px 26px 13px; display: flex; flex-direction: row; justify-content: space-between; diff --git a/public/sass/components/_switch.scss b/public/sass/components/_switch.scss index 92756f81a76..f7980215659 100644 --- a/public/sass/components/_switch.scss +++ b/public/sass/components/_switch.scss @@ -13,6 +13,10 @@ gf-form-switch[disabled] { } } +.gf-form-switch-container-react { + display: flex; +} + .gf-form-switch-container { display: flex; cursor: pointer; diff --git a/public/sass/pages/_explore.scss b/public/sass/pages/_explore.scss index 151130a69e2..9b8f18e8681 100644 --- a/public/sass/pages/_explore.scss +++ b/public/sass/pages/_explore.scss @@ -30,12 +30,6 @@ padding: 0; margin-left: 0; } - - .explore-toolbar-header-title { - .navbar-page-btn { - padding-left: 0; - } - } } .explore-toolbar { @@ -44,7 +38,7 @@ flex-flow: row wrap; justify-content: flex-start; height: auto; - padding: 0px $dashboard-padding; + padding: 0px $dashboard-padding 0 25px; border-bottom: 1px solid #0000; transition-duration: 0.35s; transition-timing-function: ease-in-out; @@ -87,22 +81,9 @@ align-items: center; } -.explore-toolbar-header-title { - color: darken($link-color, 5%); - - .navbar-page-btn { - padding-left: $dashboard-padding; - } - - .fa { - font-size: 100%; - opacity: 0.75; - margin-right: 0.5em; - } -} - .explore-toolbar-header-close { margin-left: auto; + color: $text-color-weak; } .explore-toolbar-content { @@ -156,7 +137,6 @@ .sidemenu-open { .explore-toolbar-header-title { .navbar-page-btn { - padding-left: 0; margin-left: 0; } } @@ -164,7 +144,6 @@ .explore-toolbar-header-title { .navbar-page-btn { - padding-left: 0; margin-left: $dashboard-padding; } } @@ -185,7 +164,6 @@ .sidemenu-open { .explore-toolbar-header-title { .navbar-page-btn { - padding-left: 0; margin-left: $dashboard-padding; } } @@ -193,7 +171,6 @@ .explore-toolbar-header-title { .navbar-page-btn { - padding-left: 0; margin-left: $dashboard-padding; } } diff --git a/public/sass/pages/_login.scss b/public/sass/pages/_login.scss index 091391da7ff..c42fd47ba1c 100644 --- a/public/sass/pages/_login.scss +++ b/public/sass/pages/_login.scss @@ -176,12 +176,6 @@ select:-webkit-autofill:focus { } } -.login-tab-header { - background: $tight-form-bg; - text-align: center; - margin-bottom: 3rem; -} - .login-change-password-info { padding-bottom: 1.5rem; @@ -195,26 +189,6 @@ select:-webkit-autofill:focus { background-color: $btn-semi-transparent; } -.btn-login-tab { - background: transparent; - border: none; - font-size: 15px; - padding: 10px 10px; - font-weight: bold; - display: inline-block; - width: 170px; - color: $text-color; - - &.active { - background: darken($tight-form-bg, 5%); - color: $white; - } - - &:focus { - outline: 0; - } -} - .password-strength { display: block; width: 15%; @@ -252,15 +226,6 @@ select:-webkit-autofill:focus { width: 100%; } -.password-recovery { - background: $tight-form-bg; - padding: 10px; - - a { - color: $gray-2; - } -} - .login-divider { float: left; width: 100%; diff --git a/public/sass/pages/_plugins.scss b/public/sass/pages/_plugins.scss deleted file mode 100644 index 2570eb6c791..00000000000 --- a/public/sass/pages/_plugins.scss +++ /dev/null @@ -1,31 +0,0 @@ -.get-more-plugins-link { - color: $gray-3; - font-size: $font-size-sm; - position: relative; - top: 1.2rem; - - &:hover { - color: $link-hover-color; - } - - img { - vertical-align: top; - } -} - -@include media-breakpoint-down(sm) { - .get-more-plugins-link { - display: none; - } -} - -.plugin-info-list-item { - white-space: nowrap; - max-width: $page-sidebar-width; - text-overflow: ellipsis; - overflow: hidden; - - img { - width: 16px; - } -}