mirror of
https://github.com/grafana/grafana.git
synced 2025-12-21 20:24:41 +08:00
Compare commits
102 Commits
docs/updat
...
v8.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62e720c06b | ||
|
|
c65d3ea147 | ||
|
|
3a05320d00 | ||
|
|
49dca774f3 | ||
|
|
889e1ab729 | ||
|
|
30071cee1a | ||
|
|
dd15b33f74 | ||
|
|
3a69f8e915 | ||
|
|
bbee1b26c5 | ||
|
|
de4bf9b9f3 | ||
|
|
f3df336b17 | ||
|
|
6513bbfaf2 | ||
|
|
857bd77fc2 | ||
|
|
a91f08222e | ||
|
|
8ad4703f6d | ||
|
|
5e26c6148d | ||
|
|
48e5f17392 | ||
|
|
db240265ff | ||
|
|
9a89e0c157 | ||
|
|
0e5a6a62be | ||
|
|
4ab256c132 | ||
|
|
dd42424a01 | ||
|
|
2549519180 | ||
|
|
5291563f6c | ||
|
|
6f40b883e1 | ||
|
|
e3a270a041 | ||
|
|
f8b8b4f578 | ||
|
|
7039d3ce27 | ||
|
|
784b924e9f | ||
|
|
b05581cc23 | ||
|
|
b6e20abe61 | ||
|
|
62a64d3233 | ||
|
|
43cced54a0 | ||
|
|
617cf0646d | ||
|
|
7442a7c66e | ||
|
|
dfa1a2cd41 | ||
|
|
ee81dc3b01 | ||
|
|
6cd4182ec9 | ||
|
|
716b254973 | ||
|
|
1d12f22190 | ||
|
|
fc5e1dbd15 | ||
|
|
22a357cc7d | ||
|
|
bd3e140852 | ||
|
|
7a823fe895 | ||
|
|
4eb9c6f06c | ||
|
|
9ccc02f5a3 | ||
|
|
64bd269f1e | ||
|
|
7b3275d4ed | ||
|
|
b312a60627 | ||
|
|
0075a3c014 | ||
|
|
8c70bebf90 | ||
|
|
411e19ed4b | ||
|
|
62d0c516cb | ||
|
|
78b225a5c2 | ||
|
|
a952824e35 | ||
|
|
b0dd5762f8 | ||
|
|
f778fdbbe8 | ||
|
|
d1cd7e7c64 | ||
|
|
0f5446d768 | ||
|
|
33ab22229f | ||
|
|
d74d4d6be9 | ||
|
|
e9bea6053c | ||
|
|
376cbf75b0 | ||
|
|
65d0e4227e | ||
|
|
41315b0935 | ||
|
|
0d29421a87 | ||
|
|
1bad6de8cc | ||
|
|
2fbfe987f0 | ||
|
|
c2d807a1c8 | ||
|
|
3c4754b19f | ||
|
|
c440fd4f5a | ||
|
|
28972eaf4b | ||
|
|
a7017f2729 | ||
|
|
31cc177e31 | ||
|
|
abf351f776 | ||
|
|
1a353a1eea | ||
|
|
2b97f6a507 | ||
|
|
4478259f70 | ||
|
|
38cb26bd5b | ||
|
|
1f0339179f | ||
|
|
f3f8972505 | ||
|
|
0a08cf10e5 | ||
|
|
a8f5445d47 | ||
|
|
631c12ec91 | ||
|
|
d388afece6 | ||
|
|
b0fe99911a | ||
|
|
8392ebdacb | ||
|
|
147704deb9 | ||
|
|
078d716be9 | ||
|
|
04cb471599 | ||
|
|
6564f22772 | ||
|
|
13cd3ea28b | ||
|
|
d5e0665081 | ||
|
|
909141592d | ||
|
|
fda235a862 | ||
|
|
154231a58d | ||
|
|
197e4344da | ||
|
|
64b008e28b | ||
|
|
bc9ac1199b | ||
|
|
2b15e1a962 | ||
|
|
4308a77e27 | ||
|
|
f18749927c |
38
.drone.yml
38
.drone.yml
@@ -17,7 +17,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
|
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
|
||||||
@@ -258,7 +258,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
|
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
|
||||||
@@ -589,7 +589,7 @@ steps:
|
|||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
commands:
|
commands:
|
||||||
- $$ProgressPreference = "SilentlyContinue"
|
- $$ProgressPreference = "SilentlyContinue"
|
||||||
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/windows/grabpl.exe -OutFile grabpl.exe
|
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/windows/grabpl.exe -OutFile grabpl.exe
|
||||||
|
|
||||||
- name: build-windows-installer
|
- name: build-windows-installer
|
||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
@@ -638,7 +638,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
environment:
|
environment:
|
||||||
@@ -723,7 +723,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
- ./bin/grabpl verify-version ${DRONE_TAG}
|
- ./bin/grabpl verify-version ${DRONE_TAG}
|
||||||
@@ -1029,7 +1029,7 @@ steps:
|
|||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
commands:
|
commands:
|
||||||
- $$ProgressPreference = "SilentlyContinue"
|
- $$ProgressPreference = "SilentlyContinue"
|
||||||
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/windows/grabpl.exe -OutFile grabpl.exe
|
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/windows/grabpl.exe -OutFile grabpl.exe
|
||||||
|
|
||||||
- name: build-windows-installer
|
- name: build-windows-installer
|
||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
@@ -1079,7 +1079,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
|
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
|
||||||
- cd grafana-enterprise
|
- cd grafana-enterprise
|
||||||
@@ -1504,7 +1504,7 @@ steps:
|
|||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
commands:
|
commands:
|
||||||
- $$ProgressPreference = "SilentlyContinue"
|
- $$ProgressPreference = "SilentlyContinue"
|
||||||
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/windows/grabpl.exe -OutFile grabpl.exe
|
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/windows/grabpl.exe -OutFile grabpl.exe
|
||||||
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
|
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
|
||||||
- cd grafana-enterprise
|
- cd grafana-enterprise
|
||||||
- git checkout ${DRONE_TAG}
|
- git checkout ${DRONE_TAG}
|
||||||
@@ -1572,7 +1572,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
- ./bin/grabpl verify-version ${DRONE_TAG}
|
- ./bin/grabpl verify-version ${DRONE_TAG}
|
||||||
@@ -1677,7 +1677,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
- ./bin/grabpl verify-version v7.3.0-test
|
- ./bin/grabpl verify-version v7.3.0-test
|
||||||
@@ -1972,7 +1972,7 @@ steps:
|
|||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
commands:
|
commands:
|
||||||
- $$ProgressPreference = "SilentlyContinue"
|
- $$ProgressPreference = "SilentlyContinue"
|
||||||
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/windows/grabpl.exe -OutFile grabpl.exe
|
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/windows/grabpl.exe -OutFile grabpl.exe
|
||||||
|
|
||||||
- name: build-windows-installer
|
- name: build-windows-installer
|
||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
@@ -2022,7 +2022,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
|
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
|
||||||
- cd grafana-enterprise
|
- cd grafana-enterprise
|
||||||
@@ -2441,7 +2441,7 @@ steps:
|
|||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
commands:
|
commands:
|
||||||
- $$ProgressPreference = "SilentlyContinue"
|
- $$ProgressPreference = "SilentlyContinue"
|
||||||
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/windows/grabpl.exe -OutFile grabpl.exe
|
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/windows/grabpl.exe -OutFile grabpl.exe
|
||||||
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
|
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
|
||||||
- cd grafana-enterprise
|
- cd grafana-enterprise
|
||||||
- git checkout main
|
- git checkout main
|
||||||
@@ -2509,7 +2509,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
- ./bin/grabpl verify-version v7.3.0-test
|
- ./bin/grabpl verify-version v7.3.0-test
|
||||||
@@ -2614,7 +2614,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- ./bin/grabpl verify-drone
|
- ./bin/grabpl verify-drone
|
||||||
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
|
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
|
||||||
@@ -2884,7 +2884,7 @@ steps:
|
|||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
commands:
|
commands:
|
||||||
- $$ProgressPreference = "SilentlyContinue"
|
- $$ProgressPreference = "SilentlyContinue"
|
||||||
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/windows/grabpl.exe -OutFile grabpl.exe
|
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/windows/grabpl.exe -OutFile grabpl.exe
|
||||||
|
|
||||||
- name: build-windows-installer
|
- name: build-windows-installer
|
||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
@@ -2930,7 +2930,7 @@ steps:
|
|||||||
image: grafana/build-container:1.4.1
|
image: grafana/build-container:1.4.1
|
||||||
commands:
|
commands:
|
||||||
- mkdir -p bin
|
- mkdir -p bin
|
||||||
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/grabpl
|
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/grabpl
|
||||||
- chmod +x bin/grabpl
|
- chmod +x bin/grabpl
|
||||||
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
|
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
|
||||||
- cd grafana-enterprise
|
- cd grafana-enterprise
|
||||||
@@ -3352,7 +3352,7 @@ steps:
|
|||||||
image: grafana/ci-wix:0.1.1
|
image: grafana/ci-wix:0.1.1
|
||||||
commands:
|
commands:
|
||||||
- $$ProgressPreference = "SilentlyContinue"
|
- $$ProgressPreference = "SilentlyContinue"
|
||||||
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.2.8/windows/grabpl.exe -OutFile grabpl.exe
|
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v2.3.2/windows/grabpl.exe -OutFile grabpl.exe
|
||||||
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
|
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
|
||||||
- cd grafana-enterprise
|
- cd grafana-enterprise
|
||||||
- git checkout $$env:DRONE_BRANCH
|
- git checkout $$env:DRONE_BRANCH
|
||||||
@@ -3496,6 +3496,6 @@ get:
|
|||||||
|
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: fa16b4de5ce285e6e9495b3ed797383627ffd4d43539eab58186fe8cc227d3e7
|
hmac: 68edb93a18f2e16f8a9b2bf3d21073e181e56d2420feec37ae80b121fc2faeeb
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
125
CHANGELOG.md
125
CHANGELOG.md
@@ -1,4 +1,129 @@
|
|||||||
|
|
||||||
|
<!-- 8.1.0-beta3 START -->
|
||||||
|
|
||||||
|
# 8.1.0-beta3 (2021-07-29)
|
||||||
|
|
||||||
|
### Features and enhancements
|
||||||
|
|
||||||
|
* **Alerting:** Support label matcher syntax in alert rule list filter. [#36408](https://github.com/grafana/grafana/pull/36408), [@nathanrodman](https://github.com/nathanrodman)
|
||||||
|
* **IconButton:** Put tooltip text as aria-label. [#36760](https://github.com/grafana/grafana/pull/36760), [@tskarhed](https://github.com/tskarhed)
|
||||||
|
* **Live:** Experimental HA with Redis. [#36787](https://github.com/grafana/grafana/pull/36787), [@FZambia](https://github.com/FZambia)
|
||||||
|
* **UI:** FileDropzone component. [#36646](https://github.com/grafana/grafana/pull/36646), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **[v8.1.x] CloudWatch:** Add AWS LookoutMetrics. [#37329](https://github.com/grafana/grafana/pull/37329), [@ilyastoli](https://github.com/ilyastoli)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* **Docker:** Fix builds by delaying go mod verify until all required files are copied over. [#37246](https://github.com/grafana/grafana/pull/37246), [@wbrowne](https://github.com/wbrowne)
|
||||||
|
* **Exemplars:** Fix disable exemplars only on the query that failed. [#37296](https://github.com/grafana/grafana/pull/37296), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **SQL:** Fix SQL dataframe resampling (fill mode + time intervals). [#36937](https://github.com/grafana/grafana/pull/36937), [@idafurjes](https://github.com/idafurjes)
|
||||||
|
|
||||||
|
<!-- 8.1.0-beta3 END -->
|
||||||
|
|
||||||
|
<!-- 8.1.0-beta2 START -->
|
||||||
|
|
||||||
|
# 8.1.0-beta2 (2021-07-23)
|
||||||
|
|
||||||
|
### Features and enhancements
|
||||||
|
|
||||||
|
* **Alerting:** Expand the value string in alert annotations and labels. [#37051](https://github.com/grafana/grafana/pull/37051), [@gerobinson](https://github.com/gerobinson)
|
||||||
|
* **Auth:** Add Azure HTTP authentication middleware. [#36932](https://github.com/grafana/grafana/pull/36932), [@kostrse](https://github.com/kostrse)
|
||||||
|
* **Auth:** Auth: Pass user role when using the authentication proxy. [#36729](https://github.com/grafana/grafana/pull/36729), [@yuwaMSFT2](https://github.com/yuwaMSFT2)
|
||||||
|
* **Gazetteer:** Update countries.json file to allow for linking to 3-letter country codes. [#37129](https://github.com/grafana/grafana/pull/37129), [@bryanuribe](https://github.com/bryanuribe)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* **Config:** Fix Docker builds by correcting formatting in sample.ini. [#37106](https://github.com/grafana/grafana/pull/37106), [@FZambia](https://github.com/FZambia)
|
||||||
|
* **Explore:** Fix encoding of internal URLs. [#36919](https://github.com/grafana/grafana/pull/36919), [@aocenas](https://github.com/aocenas)
|
||||||
|
|
||||||
|
<!-- 8.1.0-beta2 END -->
|
||||||
|
|
||||||
|
<!-- 8.1.0-beta1 START -->
|
||||||
|
|
||||||
|
# 8.1.0-beta1 (2021-07-22)
|
||||||
|
|
||||||
|
### Features and enhancements
|
||||||
|
|
||||||
|
* **Alerting:** Add Alertmanager notifications tab. [#35759](https://github.com/grafana/grafana/pull/35759), [@nathanrodman](https://github.com/nathanrodman)
|
||||||
|
* **Alerting:** Add button to deactivate current Alertmanager configuration. [#36951](https://github.com/grafana/grafana/pull/36951), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** Add toggle in Loki/Prometheus data source configuration to opt out of alerting UI. [#36552](https://github.com/grafana/grafana/pull/36552), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** Allow any "evaluate for" value >=0 in the alert rule form. [#35807](https://github.com/grafana/grafana/pull/35807), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** Load default configuration from status endpoint, if Cortex Alertmanager returns empty user configuration. [#35769](https://github.com/grafana/grafana/pull/35769), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** view to display alert rule and its underlying data. [#35546](https://github.com/grafana/grafana/pull/35546), [@mckn](https://github.com/mckn)
|
||||||
|
* **Annotation panel:** Release the annotation panel. [#36959](https://github.com/grafana/grafana/pull/36959), [@ryantxu](https://github.com/ryantxu)
|
||||||
|
* **Annotations:** Add typeahead support for tags in built-in annotations. [#36377](https://github.com/grafana/grafana/pull/36377), [@ashharrison90](https://github.com/ashharrison90)
|
||||||
|
* **AzureMonitor:** Add curated dashboards for Azure services. [#35356](https://github.com/grafana/grafana/pull/35356), [@avidhanju](https://github.com/avidhanju)
|
||||||
|
* **AzureMonitor:** Add support for deep links to Microsoft Azure portal for Metrics. [#32273](https://github.com/grafana/grafana/pull/32273), [@shuotli](https://github.com/shuotli)
|
||||||
|
* **AzureMonitor:** Remove support for different credentials for Azure Monitor Logs. [#35121](https://github.com/grafana/grafana/pull/35121), [@andresmgot](https://github.com/andresmgot)
|
||||||
|
* **AzureMonitor:** Support querying any Resource for Logs queries. [#33879](https://github.com/grafana/grafana/pull/33879), [@joshhunt](https://github.com/joshhunt)
|
||||||
|
* **Elasticsearch:** Add frozen indices search support. [#36018](https://github.com/grafana/grafana/pull/36018), [@Elfo404](https://github.com/Elfo404)
|
||||||
|
* **Elasticsearch:** Name fields after template variables values instead of their name. [#36035](https://github.com/grafana/grafana/pull/36035), [@Elfo404](https://github.com/Elfo404)
|
||||||
|
* **Elasticsearch:** add rate aggregation. [#33311](https://github.com/grafana/grafana/pull/33311), [@estermv](https://github.com/estermv)
|
||||||
|
* **Email:** Allow configuration of content types for email notifications. [#34530](https://github.com/grafana/grafana/pull/34530), [@djairhogeuens](https://github.com/djairhogeuens)
|
||||||
|
* **Explore:** Add more meta information when line limit is hit. [#33069](https://github.com/grafana/grafana/pull/33069), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Explore:** UI improvements to trace view. [#34276](https://github.com/grafana/grafana/pull/34276), [@aocenas](https://github.com/aocenas)
|
||||||
|
* **FieldOverrides:** Added support to change display name in an override field and have it be matched by a later rule. [#35893](https://github.com/grafana/grafana/pull/35893), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **HTTP Client:** Introduce `dataproxy_max_idle_connections` config variable. [#35864](https://github.com/grafana/grafana/pull/35864), [@dsotirakis](https://github.com/dsotirakis)
|
||||||
|
* **InfluxDB:** InfluxQL: adds tags to timeseries data. [#36702](https://github.com/grafana/grafana/pull/36702), [@gabor](https://github.com/gabor)
|
||||||
|
* **InfluxDB:** InfluxQL: make measurement search case insensitive. [#34563](https://github.com/grafana/grafana/pull/34563), [@gabor](https://github.com/gabor)
|
||||||
|
* **Legacy Alerting:** Replace simplejson with a struct in webhook notification channel. [#34952](https://github.com/grafana/grafana/pull/34952), [@KEVISONG](https://github.com/KEVISONG)
|
||||||
|
* **Legend:** Updates display name for Last (not null) to just Last*. [#35633](https://github.com/grafana/grafana/pull/35633), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **Logs panel:** Add option to show common labels. [#36166](https://github.com/grafana/grafana/pull/36166), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Loki:** Add $__range variable. [#36175](https://github.com/grafana/grafana/pull/36175), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Loki:** Add support for "label_values(log stream selector, label)" in templating. [#35488](https://github.com/grafana/grafana/pull/35488), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Loki:** Add support for ad-hoc filtering in dashboard. [#36393](https://github.com/grafana/grafana/pull/36393), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **MySQL Datasource:** Add timezone parameter. [#27535](https://github.com/grafana/grafana/pull/27535), [@andipabst](https://github.com/andipabst)
|
||||||
|
* **NodeGraph:** Show gradient fields in legend. [#34078](https://github.com/grafana/grafana/pull/34078), [@aocenas](https://github.com/aocenas)
|
||||||
|
* **PanelOptions:** Don't mutate panel options/field config object when updating. [#36441](https://github.com/grafana/grafana/pull/36441), [@dprokop](https://github.com/dprokop)
|
||||||
|
* **PieChart:** Make pie gradient more subtle to match other charts. [#36961](https://github.com/grafana/grafana/pull/36961), [@nikki-kiga](https://github.com/nikki-kiga)
|
||||||
|
* **Prometheus:** Update PromQL typeahead and highlighting. [#36730](https://github.com/grafana/grafana/pull/36730), [@ekpdt](https://github.com/ekpdt)
|
||||||
|
* **Prometheus:** interpolate variable for step field. [#36437](https://github.com/grafana/grafana/pull/36437), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **Provisioning:** Improve validation by validating across all dashboard providers. [#26742](https://github.com/grafana/grafana/pull/26742), [@nabokihms](https://github.com/nabokihms)
|
||||||
|
* **SQL Datasources:** Allow multiple string/labels columns with time series. [#36485](https://github.com/grafana/grafana/pull/36485), [@kylebrandt](https://github.com/kylebrandt)
|
||||||
|
* **Select:** Portal select menu to document.body. [#36398](https://github.com/grafana/grafana/pull/36398), [@ashharrison90](https://github.com/ashharrison90)
|
||||||
|
* **Team Sync:** Add group mapping to support team sync in the Generic OAuth provider. [#36307](https://github.com/grafana/grafana/pull/36307), [@wardbekker](https://github.com/wardbekker)
|
||||||
|
* **Tooltip:** Make active series more noticeable. [#36824](https://github.com/grafana/grafana/pull/36824), [@nikki-kiga](https://github.com/nikki-kiga)
|
||||||
|
* **Tracing:** Add support to configure trace to logs start and end time. [#34995](https://github.com/grafana/grafana/pull/34995), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **Transformations:** Skip merge when there is only a single data frame. [#36407](https://github.com/grafana/grafana/pull/36407), [@edgarpoce](https://github.com/edgarpoce)
|
||||||
|
* **ValueMapping:** Added support for mapping text to color, boolean values, NaN and Null. Improved UI for value mapping. [#33820](https://github.com/grafana/grafana/pull/33820), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **Visualizations:** Dynamically set any config (min, max, unit, color, thresholds) from query results. [#36548](https://github.com/grafana/grafana/pull/36548), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **live:** Add support to handle origin without a value for the port when matching with root_url. [#36834](https://github.com/grafana/grafana/pull/36834), [@FZambia](https://github.com/FZambia)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* **Alerting:** Handle marshaling Inf values. [#36947](https://github.com/grafana/grafana/pull/36947), [@kylebrandt](https://github.com/kylebrandt)
|
||||||
|
* **AzureMonitor:** Fix macro resolution for template variables. [#36944](https://github.com/grafana/grafana/pull/36944), [@andresmgot](https://github.com/andresmgot)
|
||||||
|
* **AzureMonitor:** Fix queries with Microsoft.NetApp/../../volumes resources. [#32661](https://github.com/grafana/grafana/pull/32661), [@pckls](https://github.com/pckls)
|
||||||
|
* **AzureMonitor:** Request and concat subsequent resource pages. [#36958](https://github.com/grafana/grafana/pull/36958), [@andresmgot](https://github.com/andresmgot)
|
||||||
|
* **Bug:** Fix parse duration for day. [#36942](https://github.com/grafana/grafana/pull/36942), [@idafurjes](https://github.com/idafurjes)
|
||||||
|
* **Datasources:** Improve error handling for error messages. [#35120](https://github.com/grafana/grafana/pull/35120), [@ifrost](https://github.com/ifrost)
|
||||||
|
* **Explore:** Correct the functionality of shift-enter shortcut across all uses. [#36600](https://github.com/grafana/grafana/pull/36600), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Explore:** Show all dataFrames in data tab in Inspector. [#32161](https://github.com/grafana/grafana/pull/32161), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **GraphNG:** Fix Tooltip mode 'All' for XYChart. [#31260](https://github.com/grafana/grafana/pull/31260), [@Posnet](https://github.com/Posnet)
|
||||||
|
* **Loki:** Fix highlight of logs when using filter expressions with backticks. [#36024](https://github.com/grafana/grafana/pull/36024), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Modal:** Force modal content to overflow with scroll. [#36754](https://github.com/grafana/grafana/pull/36754), [@ashharrison90](https://github.com/ashharrison90)
|
||||||
|
* **Plugins:** Ignore symlinked folders when verifying plugin signature. [#34434](https://github.com/grafana/grafana/pull/34434), [@wbrowne](https://github.com/wbrowne)
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
|
||||||
|
When parsing Elasticsearch query responses using template variables, each field gets named after the variable value instead of the name.
|
||||||
|
For example, executing a `terms` aggregation on a variable named `$groupBy` that has `@hostname` as a value, the resulting column in the table response will be `@hostname` instead of `$groupBy` Issue [#36035](https://github.com/grafana/grafana/issues/36035)
|
||||||
|
|
||||||
|
|
||||||
|
Azure Monitor data source no longer supports different credentials for Metrics and Logs in existing data sources. To use different credentials for Azure Monitor Logs, create another data source. Issue [#35121](https://github.com/grafana/grafana/issues/35121)
|
||||||
|
|
||||||
|
Existing Azure Metrics Logs queries for Log Analytics Workspaces should be backward compatible with this change and should not get impacted. Panels will be migrated to use the new resource-centric backend when you first edit and save them.
|
||||||
|
|
||||||
|
Application Insights and Insights Analytics queries are now read-only and cannot be modified. To update Application Insights queries, users can manually recreate them as Metrics queries, and Insights Analytics are recreated with Logs.
|
||||||
|
|
||||||
|
Issue [#33879](https://github.com/grafana/grafana/issues/33879)
|
||||||
|
|
||||||
|
### Plugin development fixes & changes
|
||||||
|
|
||||||
|
* **Toolkit:** Improve error messages when tasks fail. [#36381](https://github.com/grafana/grafana/pull/36381), [@joshhunt](https://github.com/joshhunt)
|
||||||
|
|
||||||
|
<!-- 8.1.0-beta1 END -->
|
||||||
|
|
||||||
<!-- 8.0.6 START -->
|
<!-- 8.0.6 START -->
|
||||||
|
|
||||||
# 8.0.6 (2021-07-14)
|
# 8.0.6 (2021-07-14)
|
||||||
|
|||||||
@@ -24,14 +24,12 @@ RUN apk add --no-cache gcc g++
|
|||||||
WORKDIR $GOPATH/src/github.com/grafana/grafana
|
WORKDIR $GOPATH/src/github.com/grafana/grafana
|
||||||
|
|
||||||
COPY go.mod go.sum embed.go ./
|
COPY go.mod go.sum embed.go ./
|
||||||
|
|
||||||
RUN go mod verify
|
|
||||||
|
|
||||||
COPY cue cue
|
COPY cue cue
|
||||||
COPY public/app/plugins public/app/plugins
|
COPY public/app/plugins public/app/plugins
|
||||||
COPY pkg pkg
|
COPY pkg pkg
|
||||||
COPY build.go package.json ./
|
COPY build.go package.json ./
|
||||||
|
|
||||||
|
RUN go mod verify
|
||||||
RUN go run build.go build
|
RUN go run build.go build
|
||||||
|
|
||||||
# Final stage
|
# Final stage
|
||||||
|
|||||||
@@ -22,14 +22,12 @@ FROM golang:1.16 AS go-builder
|
|||||||
WORKDIR /src/grafana
|
WORKDIR /src/grafana
|
||||||
|
|
||||||
COPY go.mod go.sum embed.go ./
|
COPY go.mod go.sum embed.go ./
|
||||||
|
|
||||||
RUN go mod verify
|
|
||||||
|
|
||||||
COPY build.go package.json ./
|
COPY build.go package.json ./
|
||||||
COPY pkg pkg/
|
COPY pkg pkg/
|
||||||
COPY cue cue/
|
COPY cue cue/
|
||||||
COPY public/app/plugins public/app/plugins/
|
COPY public/app/plugins public/app/plugins/
|
||||||
|
|
||||||
|
RUN go mod verify
|
||||||
RUN go run build.go build
|
RUN go run build.go build
|
||||||
|
|
||||||
FROM ubuntu:20.04
|
FROM ubuntu:20.04
|
||||||
|
|||||||
@@ -995,12 +995,12 @@
|
|||||||
[geomap]
|
[geomap]
|
||||||
# Set the JSON configuration for the default basemap
|
# Set the JSON configuration for the default basemap
|
||||||
;default_baselayer_config = `{
|
;default_baselayer_config = `{
|
||||||
"type": "xyz",
|
; "type": "xyz",
|
||||||
"config": {
|
; "config": {
|
||||||
"attribution": "Open street map",
|
; "attribution": "Open street map",
|
||||||
"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
|
; "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
}
|
; }
|
||||||
}`
|
;}`
|
||||||
|
|
||||||
# Enable or disable loading other base map layers
|
# Enable or disable loading other base map layers
|
||||||
;enable_custom_baselayers = true
|
;enable_custom_baselayers = true
|
||||||
|
|||||||
@@ -1529,6 +1529,30 @@ For example:
|
|||||||
allowed_origins = "https://*.example.com"
|
allowed_origins = "https://*.example.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ha_engine
|
||||||
|
|
||||||
|
> **Note**: Available in Grafana v8.1 and later versions.
|
||||||
|
|
||||||
|
**Experimental**
|
||||||
|
|
||||||
|
The high availability (HA) engine name for Grafana Live. By default, it's not set. The only possible value is "redis".
|
||||||
|
|
||||||
|
For more information, refer to [Configure Grafana Live HA setup]({{< relref "../live/live-ha-setup.md" >}}).
|
||||||
|
|
||||||
|
### ha_engine_address
|
||||||
|
|
||||||
|
> **Note**: Available in Grafana v8.1 and later versions.
|
||||||
|
|
||||||
|
**Experimental**
|
||||||
|
|
||||||
|
Address string of selected the high availability (HA) Live engine. For Redis, it's a `host:port` string. Example:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[live]
|
||||||
|
ha_engine = redis
|
||||||
|
ha_engine_address = 127.0.0.1:6379
|
||||||
|
```
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
## [plugin.grafana-image-renderer]
|
## [plugin.grafana-image-renderer]
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ weight = 300
|
|||||||
|
|
||||||
# View Grafana server settings
|
# View Grafana server settings
|
||||||
|
|
||||||
|
> Refer to [Fine-grained access control]({{< relref "../../enterprise/access-control/_index.md" >}}) in Grafana Enterprise to understand how you can control access with fine-grained permissions.
|
||||||
|
|
||||||
If you are a Grafana server administrator, use the Settings tab to view the settings that are applied to your Grafana server via the [Configuration]({{< relref "../configuration.md#config-file-locations" >}}) file and any environmental variables.
|
If you are a Grafana server administrator, use the Settings tab to view the settings that are applied to your Grafana server via the [Configuration]({{< relref "../configuration.md#config-file-locations" >}}) file and any environmental variables.
|
||||||
|
|
||||||
> **Note:** Only Grafana server administrators can access the **Server Admin** menu. For more information about about administrative permissions, refer to [Grafana server admin]({{< relref "../../permissions/_index.md" >}}).
|
> **Note:** Only Grafana server administrators can access the **Server Admin** menu. For more information about about administrative permissions, refer to [Grafana server admin]({{< relref "../../permissions/_index.md" >}}).
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ weight = 400
|
|||||||
|
|
||||||
# View Grafana server stats
|
# View Grafana server stats
|
||||||
|
|
||||||
|
> Refer to [Fine-grained access control]({{< relref "../../enterprise/access-control/_index.md" >}}) in Grafana Enterprise to understand how you can control access with fine-grained permissions.
|
||||||
|
|
||||||
If you are a Grafana server admin, then you can view useful statistics about your Grafana server in the Stats tab.
|
If you are a Grafana server admin, then you can view useful statistics about your Grafana server in the Stats tab.
|
||||||
|
|
||||||
> **Note:** Only Grafana server administrators can access the **Server Admin** menu. For more information about about administrative permissions, refer to [Grafana server admin]({{< relref "../../permissions/_index.md" >}}).
|
> **Note:** Only Grafana server administrators can access the **Server Admin** menu. For more information about about administrative permissions, refer to [Grafana server admin]({{< relref "../../permissions/_index.md" >}}).
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ You can perform the following tasks for alerts:
|
|||||||
- [Create a Grafana managed alert rule]({{< relref "alerting-rules/create-grafana-managed-rule.md" >}})
|
- [Create a Grafana managed alert rule]({{< relref "alerting-rules/create-grafana-managed-rule.md" >}})
|
||||||
- [Create a Cortex or Loki managed alert rule]({{< relref "alerting-rules/create-cortex-loki-managed-rule.md" >}})
|
- [Create a Cortex or Loki managed alert rule]({{< relref "alerting-rules/create-cortex-loki-managed-rule.md" >}})
|
||||||
- [View existing alert rules and their current state]({{< relref "alerting-rules/rule-list.md" >}})
|
- [View existing alert rules and their current state]({{< relref "alerting-rules/rule-list.md" >}})
|
||||||
- [Test alert rules and troubleshoot]({{< relref "./troubleshoot-alerts.md" >}})
|
- [View state and health of alerting rules]({{< relref "alerting-rules/state-and-health.md" >}})
|
||||||
- [Add or edit an alert contact point]({{< relref "./contact-points.md" >}})
|
- [Add or edit an alert contact point]({{< relref "./contact-points.md" >}})
|
||||||
- [Add or edit notification policies]({{< relref "./notification-policies.md" >}})
|
- [Add or edit notification policies]({{< relref "./notification-policies.md" >}})
|
||||||
- [Create and edit silences]({{< relref "./silences.md" >}})
|
- [Create and edit silences]({{< relref "./silences.md" >}})
|
||||||
|
|||||||
@@ -109,8 +109,9 @@ The following template variables are available when expanding annotations and la
|
|||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| ------- | --------------- |
|
| ------- | --------------- |
|
||||||
| $labels | Labels contains the labels from the query or condition. For example, `{{ $labels.instance }}` and `{{ $labels.job }}`. |
|
| $labels | The labels from the query or condition. For example, `{{ $labels.instance }}` and `{{ $labels.job }}`. |
|
||||||
| $values | Values contains the values of all reduce and math expressions that were evaluated for this alert rule. For example, `{{ $values.A }}`, `{{ $values.A.Labels }}` and `{{ $values.A.Value }}` where `A` is the `refID` of the expression. |
|
| $values | The values of all reduce and math expressions that were evaluated for this alert rule. For example, `{{ $values.A }}`, `{{ $values.A.Labels }}` and `{{ $values.A.Value }}` where `A` is the `refID` of the expression. |
|
||||||
|
| $value | The value string of the alert instance. For example, `[ var='A' labels={instance=foo} value=10 ]`. |
|
||||||
|
|
||||||
## Preview alerts
|
## Preview alerts
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ weight = 400
|
|||||||
# View alert rules
|
# View alert rules
|
||||||
|
|
||||||
To view alerts:
|
To view alerts:
|
||||||
|
|
||||||
1. In the Grafana menu hover your cursor over the Alerting (bell) icon.
|
1. In the Grafana menu hover your cursor over the Alerting (bell) icon.
|
||||||
1. Click **Alert Rules**. You can see all configured Grafana alert rules as well as any rules from Loki or Prometheus data sources.
|
1. Click **Alert Rules**. You can see all configured Grafana alert rules as well as any rules from Loki or Prometheus data sources.
|
||||||
By default, the group view is shown. You can toggle between group or state views by clicking the relevant **View as** buttons in the options area at the top of the page.
|
By default, the group view is shown. You can toggle between group or state views by clicking the relevant **View as** buttons in the options area at the top of the page.
|
||||||
|
|
||||||
### Group view
|
### Group view
|
||||||
|
|
||||||
@@ -25,9 +26,10 @@ State view shows alert rules grouped by state. Use this view to get an overview
|
|||||||

|

|
||||||
|
|
||||||
## Filter alert rules
|
## Filter alert rules
|
||||||
|
|
||||||
You can use the following filters to view only alert rules that match specific criteria:
|
You can use the following filters to view only alert rules that match specific criteria:
|
||||||
|
|
||||||
- **Filter alerts by name or label -** Type an alert name, label name or value in the **Search** input.
|
- **Filter alerts by label -** Search by alert labels using label selectors in the **Search** input. eg: `environment=production,region=~US|EU,severity!=warning`
|
||||||
- **Filter alerts by state -** In **States** Select which alert states you want to see. All others are hidden.
|
- **Filter alerts by state -** In **States** Select which alert states you want to see. All others are hidden.
|
||||||
- **Filter alerts by data source -** Click the **Select data source** and select an alerting data source. Only alert rules that query selected data source will be visible.
|
- **Filter alerts by data source -** Click the **Select data source** and select an alerting data source. Only alert rules that query selected data source will be visible.
|
||||||
|
|
||||||
@@ -39,13 +41,13 @@ A rule row shows the rule state, health, and summary annotation if the rule has
|
|||||||
|
|
||||||
### Edit or delete rule
|
### Edit or delete rule
|
||||||
|
|
||||||
Grafana rules can only be edited or deleted by users with Edit permissions for the folder which contains the rule. Prometheus or Loki rules can be edited or deleted by users with Editor or Admin roles.
|
Grafana rules can only be edited or deleted by users with Edit permissions for the folder which contains the rule. Prometheus or Loki rules can be edited or deleted by users with Editor or Admin roles.
|
||||||
|
|
||||||
To edit or delete a rule:
|
To edit or delete a rule:
|
||||||
|
|
||||||
1. Expand this rule to reveal rule controls.
|
1. Expand this rule to reveal rule controls.
|
||||||
1. Click **Edit** to go to the rule editing form. Make changes following [instructions listed here]({{< relref "./create-grafana-managed-rule.md" >}}).
|
1. Click **Edit** to go to the rule editing form. Make changes following [instructions listed here]({{< relref "./create-grafana-managed-rule.md" >}}).
|
||||||
1. Click **Delete"** to delete a rule.
|
1. Click **Delete"** to delete a rule.
|
||||||
|
|
||||||
## Opt-out a Loki or Prometheus data source
|
## Opt-out a Loki or Prometheus data source
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ description = "State and Health of alerting rules"
|
|||||||
keywords = ["grafana", "alerting", "guide", "state"]
|
keywords = ["grafana", "alerting", "guide", "state"]
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
# State and Health of alerting rule
|
||||||
|
|
||||||
The concepts of state and health for alerting rules help you understand, at a glance, several key status indicators about your alerts. Alert state, alerting rule state, and alerting rule health are related, but they each convey subtly different information.
|
The concepts of state and health for alerting rules help you understand, at a glance, several key status indicators about your alerts. Alert state, alerting rule state, and alerting rule health are related, but they each convey subtly different information.
|
||||||
|
|
||||||
## Alerting rule state
|
## Alerting rule state
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ sync_ttl = 60
|
|||||||
# Example `whitelist = 192.168.1.1, 192.168.1.0/24, 2001::23, 2001::0/120`
|
# Example `whitelist = 192.168.1.1, 192.168.1.0/24, 2001::23, 2001::0/120`
|
||||||
whitelist =
|
whitelist =
|
||||||
# Optionally define more headers to sync other user attributes
|
# Optionally define more headers to sync other user attributes
|
||||||
# Example `headers = Name:X-WEBAUTH-NAME Email:X-WEBAUTH-EMAIL Groups:X-WEBAUTH-GROUPS`
|
# Example `headers = Name:X-WEBAUTH-NAME Role:X-WEBAUTH-ROLE Email:X-WEBAUTH-EMAIL Groups:X-WEBAUTH-GROUPS`
|
||||||
headers =
|
headers =
|
||||||
# Check out docs on this for more details on the below setting
|
# Check out docs on this for more details on the below setting
|
||||||
enable_login_token = false
|
enable_login_token = false
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ provider (listed above). There is also options for allowing self sign up.
|
|||||||
Grafana are using short-lived tokens as a mechanism for verifying authenticated users.
|
Grafana are using short-lived tokens as a mechanism for verifying authenticated users.
|
||||||
These short-lived tokens are rotated each `token_rotation_interval_minutes` for an active authenticated user.
|
These short-lived tokens are rotated each `token_rotation_interval_minutes` for an active authenticated user.
|
||||||
|
|
||||||
An active authenticated user that gets it token rotated will extend the `login_maximum_inactive_lifetime_days` time from "now" that Grafana will remember the user.
|
An active authenticated user that gets it token rotated will extend the `login_maximum_inactive_lifetime_duration` time from "now" that Grafana will remember the user.
|
||||||
This means that a user can close its browser and come back before `now + login_maximum_inactive_lifetime_days` and still being authenticated.
|
This means that a user can close its browser and come back before `now + login_maximum_inactive_lifetime_duration` and still being authenticated.
|
||||||
This is true as long as the time since user login is less than `login_maximum_lifetime_days`.
|
This is true as long as the time since user login is less than `login_maximum_lifetime_duration`.
|
||||||
|
|
||||||
#### Remote logout
|
#### Remote logout
|
||||||
|
|
||||||
@@ -38,10 +38,10 @@ Example:
|
|||||||
login_cookie_name = grafana_session
|
login_cookie_name = grafana_session
|
||||||
|
|
||||||
# The lifetime (days) an authenticated user can be inactive before being required to login at next visit. Default is 7 days.
|
# The lifetime (days) an authenticated user can be inactive before being required to login at next visit. Default is 7 days.
|
||||||
login_maximum_inactive_lifetime_days = 7
|
login_maximum_inactive_lifetime_duration = 7d
|
||||||
|
|
||||||
# The maximum lifetime (days) an authenticated user can be logged in since login time before being required to login. Default is 30 days.
|
# The maximum lifetime (days) an authenticated user can be logged in since login time before being required to login. Default is 30 days.
|
||||||
login_maximum_lifetime_days = 30
|
login_maximum_lifetime_duration = 30d
|
||||||
|
|
||||||
# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes.
|
# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes.
|
||||||
token_rotation_interval_minutes = 10
|
token_rotation_interval_minutes = 10
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ group memberships and Grafana Organization user roles.
|
|||||||
|
|
||||||
> [Enhanced LDAP authentication]({{< relref "../enterprise/enhanced_ldap.md" >}}) is available in [Grafana Cloud Pro and Advanced](https://grafana.com/docs/grafana-cloud/) and in [Grafana Enterprise]({{< relref "../enterprise" >}}).
|
> [Enhanced LDAP authentication]({{< relref "../enterprise/enhanced_ldap.md" >}}) is available in [Grafana Cloud Pro and Advanced](https://grafana.com/docs/grafana-cloud/) and in [Grafana Enterprise]({{< relref "../enterprise" >}}).
|
||||||
|
|
||||||
|
> Refer to [Fine-grained access control]({{< relref "../enterprise/access-control/_index.md" >}}) in Grafana Enterprise to understand how you can control access with fine-grained permissions.
|
||||||
|
|
||||||
## Supported LDAP Servers
|
## Supported LDAP Servers
|
||||||
|
|
||||||
Grafana uses a [third-party LDAP library](https://github.com/go-ldap/ldap) under the hood that supports basic LDAP v3 functionality.
|
Grafana uses a [third-party LDAP library](https://github.com/go-ldap/ldap) under the hood that supports basic LDAP v3 functionality.
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ provider (listed above). There are also options for allowing self sign up.
|
|||||||
Grafana are using short-lived tokens as a mechanism for verifying authenticated users.
|
Grafana are using short-lived tokens as a mechanism for verifying authenticated users.
|
||||||
These short-lived tokens are rotated each `token_rotation_interval_minutes` for an active authenticated user.
|
These short-lived tokens are rotated each `token_rotation_interval_minutes` for an active authenticated user.
|
||||||
|
|
||||||
An active authenticated user that gets it token rotated will extend the `login_maximum_inactive_lifetime_days` time from "now" that Grafana will remember the user.
|
An active authenticated user that gets it token rotated will extend the `login_maximum_inactive_lifetime_duration` time from "now" that Grafana will remember the user.
|
||||||
This means that a user can close its browser and come back before `now + login_maximum_inactive_lifetime_days` and still being authenticated.
|
This means that a user can close its browser and come back before `now + login_maximum_inactive_lifetime_duration` and still being authenticated.
|
||||||
This is true as long as the time since user login is less than `login_maximum_lifetime_days`.
|
This is true as long as the time since user login is less than `login_maximum_lifetime_duration`.
|
||||||
|
|
||||||
#### Remote logout
|
#### Remote logout
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Here are some examples:
|
|||||||
| This month so far | `now/M` | `now` |
|
| This month so far | `now/M` | `now` |
|
||||||
| Previous Month | `now-1M/M` | `now-1M/M` |
|
| Previous Month | `now-1M/M` | `now-1M/M` |
|
||||||
| This year so far | `now/Y` | `now` |
|
| This year so far | `now/Y` | `now` |
|
||||||
| This Year | `now/Y` | `now/Y |
|
| This Year | `now/Y` | `now/Y` |
|
||||||
|
|
||||||
## Common time range controls
|
## Common time range controls
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ visualize logs or metrics stored in Elasticsearch. You can also annotate your gr
|
|||||||
1. Open the side menu by clicking the Grafana icon in the top header.
|
1. Open the side menu by clicking the Grafana icon in the top header.
|
||||||
1. In the side menu under the `Dashboards` link you should find a link named `Data Sources`.
|
1. In the side menu under the `Dashboards` link you should find a link named `Data Sources`.
|
||||||
1. Click the `+ Add data source` button in the top header.
|
1. Click the `+ Add data source` button in the top header.
|
||||||
1. Select *Elasticsearch* from the *Type* dropdown.
|
1. Select **Elasticsearch** from the **Type** dropdown.
|
||||||
|
|
||||||
> **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.
|
> **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.
|
||||||
|
|
||||||
@@ -104,6 +104,7 @@ Data links create a link from a specified field that can be accessed in logs vie
|
|||||||
Each data link configuration consists of:
|
Each data link configuration consists of:
|
||||||
- **Field -** Name of the field used by the data link.
|
- **Field -** Name of the field used by the data link.
|
||||||
- **URL/query -** If the link is external, then enter the full link URL. If the link is internal link, then this input serves as query for the target data source. In both cases, you can interpolate the value from the field with `${__value.raw }` macro.
|
- **URL/query -** If the link is external, then enter the full link URL. If the link is internal link, then this input serves as query for the target data source. In both cases, you can interpolate the value from the field with `${__value.raw }` macro.
|
||||||
|
- **URL Label -** (Optional) Set a custom display label for the link. The link label defaults to the full external URL or name of the linked internal data source and is overridden by this setting.
|
||||||
- **Internal link -** Select if the link is internal or external. In case of internal link, a data source selector allows you to select the target data source. Only tracing data sources are supported.
|
- **Internal link -** Select if the link is internal or external. In case of internal link, a data source selector allows you to select the target data source. Only tracing data sources are supported.
|
||||||
|
|
||||||
## Metric Query editor
|
## Metric Query editor
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ This is a configuration for the [trace to logs feature]({{< relref "../explore/t
|
|||||||
|
|
||||||
- **Data source -** Target data source.
|
- **Data source -** Target data source.
|
||||||
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
|
- **Tags -** The tags that will be used in the Loki query. Default is `'cluster', 'hostname', 'namespace', 'pod'`.
|
||||||
- **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
|
- **Span start time shift -** Shift in the start time for the Loki query based on the span start time. In order to extend to the past, you need to use a negative value. Use time interval units like 5s, 1m, 3h. The default is 0.
|
||||||
- **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
|
- **Span end time shift -** Shift in the end time for the Loki query based on the span end time. Time units can be used here, for example, 5s, 1m, 3h. The default is 0.
|
||||||
|
|
||||||

|

|
||||||
@@ -56,6 +56,62 @@ To perform a search, set the query type selector to Search, then use the followi
|
|||||||
- Max Duration - Filter all traces with a duration lower than the set value. Possible values are `1.2s, 100ms, 500us`.
|
- Max Duration - Filter all traces with a duration lower than the set value. Possible values are `1.2s, 100ms, 500us`.
|
||||||
- Limit - Limits the number of traces returned.
|
- Limit - Limits the number of traces returned.
|
||||||
|
|
||||||
|
## Upload JSON trace file
|
||||||
|
|
||||||
|
You can upload a JSON file that contains a single trace to visualize it. If the file has multiple traces then the first trace is used for visualization.
|
||||||
|
|
||||||
|
{{< figure src="/static/img/docs/explore/jaeger-upload-json.png" class="docs-image--no-shadow" caption="Screenshot of the Jaeger data source in explore with upload selected" >}}
|
||||||
|
|
||||||
|
Here is an example JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"traceID": "2ee9739529395e31",
|
||||||
|
"spans": [
|
||||||
|
{
|
||||||
|
"traceID": "2ee9739529395e31",
|
||||||
|
"spanID": "2ee9739529395e31",
|
||||||
|
"flags": 1,
|
||||||
|
"operationName": "CAS",
|
||||||
|
"references": [],
|
||||||
|
"startTime": 1616095319593196,
|
||||||
|
"duration": 1004,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"key": "sampler.type",
|
||||||
|
"type": "string",
|
||||||
|
"value": "const"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"logs": [],
|
||||||
|
"processID": "p1",
|
||||||
|
"warnings": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"processes": {
|
||||||
|
"p1": {
|
||||||
|
"serviceName": "loki-all",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"key": "jaeger.version",
|
||||||
|
"type": "string",
|
||||||
|
"value": "Go-2.25.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warnings": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 0,
|
||||||
|
"limit": 0,
|
||||||
|
"offset": 0,
|
||||||
|
"errors": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Linking Trace ID from logs
|
## Linking Trace ID from logs
|
||||||
|
|
||||||
You can link to Jaeger trace from logs in Loki by configuring a derived field with internal link. See the [Derived fields]({{< relref "loki.md#derived-fields" >}}) section in the [Loki data source]({{< relref "loki.md" >}}) documentation for details.
|
You can link to Jaeger trace from logs in Loki by configuring a derived field with internal link. See the [Derived fields]({{< relref "loki.md#derived-fields" >}}) section in the [Loki data source]({{< relref "loki.md" >}}) documentation for details.
|
||||||
|
|||||||
@@ -33,13 +33,14 @@ The Derived Fields configuration allows you to:
|
|||||||
- Add fields parsed from the log message.
|
- Add fields parsed from the log message.
|
||||||
- Add a link that uses the value of the field.
|
- Add a link that uses the value of the field.
|
||||||
|
|
||||||
You can use this functionality to link to your tracing backend directly from your logs, or link to a user profile page if a userId is present in the log line. These links appear in the [log details]({{< relref "../explore/logs-integration/#labels-and-detected-fields" >}}).
|
For example, you can use this functionality to link to your tracing backend directly from your logs, or link to a user profile page if a userId is present in the log line. These links appear in the [log details]({{< relref "../explore/logs-integration/#labels-and-detected-fields" >}}).
|
||||||
|
|
||||||
Each derived field consists of:
|
Each derived field consists of:
|
||||||
|
|
||||||
- **Name -** Shown in the log details as a label.
|
- **Name -** Shown in the log details as a label.
|
||||||
- **Regex -** A Regex pattern that runs on the log message and captures part of it as the value of the new field. Can only contain a single capture group.
|
- **Regex -** A Regex pattern that runs on the log message and captures part of it as the value of the new field. Can only contain a single capture group.
|
||||||
- **URL/query -** If the link is external, then enter the full link URL. If the link is internal link, then this input serves as query for the target data source. In both cases, you can interpolate the value from the field with `${__value.raw }` macro.
|
- **URL/query -** If the link is external, then enter the full link URL. If the link is internal link, then this input serves as query for the target data source. In both cases, you can interpolate the value from the field with `${__value.raw }` macro.
|
||||||
|
- **URL Label -** (Optional) Set a custom display label for the link. The link label defaults to the full external URL or name of the linked internal data source and is overridden by this setting.
|
||||||
- **Internal link -** Select if the link is internal or external. In case of internal link, a data source selector allows you to select the target data source. Only tracing data sources are supported.
|
- **Internal link -** Select if the link is internal or external. In case of internal link, a data source selector allows you to select the target data source. Only tracing data sources are supported.
|
||||||
|
|
||||||
You can use a debug section to see what your fields extract and how the URL is interpolated. Click **Show example log message** to show the text area where you can enter a log message.
|
You can use a debug section to see what your fields extract and how the URL is interpolated. Click **Show example log message** to show the text area where you can enter a log message.
|
||||||
|
|||||||
@@ -46,8 +46,10 @@ Open a graph in edit mode by clicking the title > Edit (or by pressing `e` key w
|
|||||||
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `Query expression` | Prometheus query expression, check out the [Prometheus documentation](http://prometheus.io/docs/querying/basics/). |
|
| `Query expression` | Prometheus query expression, check out the [Prometheus documentation](http://prometheus.io/docs/querying/basics/). |
|
||||||
| `Legend format` | Controls the name of the time series, using name or pattern. For example `{{hostname}}` is replaced with the label value for the label `hostname`. |
|
| `Legend format` | Controls the name of the time series, using name or pattern. For example `{{hostname}}` is replaced with the label value for the label `hostname`. |
|
||||||
| `Min step` | An additional lower limit for the [`step` parameter of Prometheus range queries](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) and for the `$__interval` and `$__rate_interval` variables. The limit is absolute, it cannot modified by the _Resolution_ setting. |
|
| `Step` | Use 'Minimum' or 'Maximum' step mode to set the lower or upper bounds respectively on the interval between data points. For example, set "minimum 1h" to hint that measurements were not taken more frequently. Use the 'Exact' step mode to set an exact interval between data points. `$__interval` and `$__rate_interval` are supported. |
|
||||||
| `Resolution` | `1/1` sets both the `$__interval` variable and the [`step` parameter of Prometheus range queries](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) such that each pixel corresponds to one data point. For better performance, lower resolutions can be picked. `1/2` only retrieves a data point for every other pixel, and `1/10` retrieves one data point per 10 pixels. Note that both _Min time interval_ and _Min step_ limit the final value of `$__interval` and `step`. |
|
|
||||||
|
| `Resolution` | `1/1` sets both the `$__interval` variable and the [`step` parameter of Prometheus range queries](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) such that each pixel corresponds to one data point. For better performance, lower resolutions can be picked. `1/2` only retrieves a data point for every other pixel, and `1/10` retrieves one data point per 10 pixels. Note that both _Min time interval_ and _Step_ limit the final value of `$__interval` and `step`. |
|
||||||
|
|
||||||
| `Metric lookup` | Search for metric names in this input field. |
|
| `Metric lookup` | Search for metric names in this input field. |
|
||||||
| `Format as` | Switch between `Table`, `Time series`, or `Heatmap`. `Table` will only work in the Table panel. `Heatmap` is suitable for displaying metrics of the Histogram type on a Heatmap panel. Under the hood, it converts cumulative histograms to regular ones and sorts series by the bucket bound. |
|
| `Format as` | Switch between `Table`, `Time series`, or `Heatmap`. `Table` will only work in the Table panel. `Heatmap` is suitable for displaying metrics of the Histogram type on a Heatmap panel. Under the hood, it converts cumulative histograms to regular ones and sorts series by the bucket bound. |
|
||||||
| `Instant` | Perform an "instant" query, to return only the latest value that Prometheus has scraped for the requested time series. Instant queries return results much faster than normal range queries. Use them to look up label sets. |
|
| `Instant` | Perform an "instant" query, to return only the latest value that Prometheus has scraped for the requested time series. Instant queries return results much faster than normal range queries. Use them to look up label sets. |
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ The enhanced LDAP integration adds additional functionality on top of the [LDAP
|
|||||||
|
|
||||||
> Enhanced LDAP integration is only available in Grafana Enterprise.
|
> Enhanced LDAP integration is only available in Grafana Enterprise.
|
||||||
|
|
||||||
|
> Refer to [Fine-grained access control]({{< relref "../enterprise/access-control/_index.md" >}}) in Grafana Enterprise to understand how you can control access with fine-grained permissions.
|
||||||
|
|
||||||
## LDAP group synchronization for teams
|
## LDAP group synchronization for teams
|
||||||
|
|
||||||
{{< figure src="/static/img/docs/enterprise/team_members_ldap.png" class="docs-image--no-shadow docs-image--right" max-width= "600px" >}}
|
{{< figure src="/static/img/docs/enterprise/team_members_ldap.png" class="docs-image--no-shadow docs-image--right" max-width= "600px" >}}
|
||||||
|
|||||||
@@ -361,6 +361,19 @@ This value limits the size of a single cache value. If a cache value (or query r
|
|||||||
|
|
||||||
The default is `1`.
|
The default is `1`.
|
||||||
|
|
||||||
|
## [caching.encryption]
|
||||||
|
### enabled
|
||||||
|
|
||||||
|
When 'enabled' is `true`, query values in the cache are encrypted.
|
||||||
|
|
||||||
|
The default is `false`.
|
||||||
|
|
||||||
|
### encryption_key
|
||||||
|
|
||||||
|
A string used to generate a key for encrypting the cache. For the encrypted cache data to persist between Grafana restarts, you must specify this key. If it is empty when encryption is enabled, then the key is automatically generated on startup, and the cache clears upon restarts.
|
||||||
|
|
||||||
|
The default is `""`.
|
||||||
|
|
||||||
## [caching.memory]
|
## [caching.memory]
|
||||||
|
|
||||||
### gc_interval
|
### gc_interval
|
||||||
@@ -371,7 +384,7 @@ The default is `1m`.
|
|||||||
|
|
||||||
### max_size_mb
|
### max_size_mb
|
||||||
|
|
||||||
The maximum size of the in-memory cache in megabytes. Once this size is reached, new cache items are rejected. For more flexible control over cache eviction policies and size, use the Redis or Memcached backend.
|
The maximum size of the in-memory cache in megabytes. Once this size is reached, new cache items are rejected. For more flexible control over cache eviction policies and size, use the Redis or Memcached backend.
|
||||||
|
|
||||||
To disable the maximum, set this value to `0`.
|
To disable the maximum, set this value to `0`.
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Reporting allows you to automatically generate PDFs from any of your dashboards
|
|||||||
> If you have [Fine-grained access Control]({{< relref "../enterprise/access-control/_index.md" >}}) enabled, for some actions you would need to have relevant permissions.
|
> If you have [Fine-grained access Control]({{< relref "../enterprise/access-control/_index.md" >}}) enabled, for some actions you would need to have relevant permissions.
|
||||||
Refer to specific guides to understand what permissions are required.
|
Refer to specific guides to understand what permissions are required.
|
||||||
|
|
||||||
{{< figure src="/static/img/docs/enterprise/reports_list.png" max-width="500px" class="docs-image--no-shadow" >}}
|
{{< figure src="/static/img/docs/enterprise/reports_list_8.1.png" max-width="500px" class="docs-image--no-shadow" >}}
|
||||||
|
|
||||||
Any changes you make to a dashboard used in a report are reflected the next time the report is sent. For example, if you change the time range in the dashboard, then the time range in the report changes as well.
|
Any changes you make to a dashboard used in a report are reflected the next time the report is sent. For example, if you change the time range in the dashboard, then the time range in the report changes as well.
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ Only organization admins can create reports by default. You can customize who ca
|
|||||||
1. **Save** the report.
|
1. **Save** the report.
|
||||||
1. **Send test email** to verify that the whole configuration is working as expected. You can choose to send this email to the recipients configured for the report, or to a different set of email addresses only used for testing.
|
1. **Send test email** to verify that the whole configuration is working as expected. You can choose to send this email to the recipients configured for the report, or to a different set of email addresses only used for testing.
|
||||||
|
|
||||||
{{< figure src="/static/img/docs/enterprise/reports-create-new-8.0.png" max-width="500px" class="docs-image--no-shadow" >}}
|
{{< figure src="/static/img/docs/enterprise/reports_create_new_8.1.png" max-width="500px" class="docs-image--no-shadow" >}}
|
||||||
|
|
||||||
### Choose template variables
|
### Choose template variables
|
||||||
|
|
||||||
@@ -106,48 +106,26 @@ When the CSV file is generated, it is temporarily written to the `csv` folder in
|
|||||||
A background job runs every 10 minutes and removes temporary CSV files. You can configure how long a CSV file should be stored before being removed by configuring the [temp-data-lifetime]({{< relref "../administration/configuration/#temp-data-lifetime" >}}) setting. This setting also affects how long a renderer PNG file should be stored.
|
A background job runs every 10 minutes and removes temporary CSV files. You can configure how long a CSV file should be stored before being removed by configuring the [temp-data-lifetime]({{< relref "../administration/configuration/#temp-data-lifetime" >}}) setting. This setting also affects how long a renderer PNG file should be stored.
|
||||||
|
|
||||||
### Scheduling
|
### Scheduling
|
||||||
|
> Note: Scheduler has been significantly changed in Grafana Enterprise v8.1.
|
||||||
|
|
||||||
Scheduled reports can be sent on a monthly, weekly, daily, or hourly basis. You may also disable scheduling for when you either want to send it via the API.
|
Scheduled reports can be sent once or repeatedly on an hourly, daily, weekly, or monthly basis, or at custom intervals. You can also disable scheduling by selecting **Never**: for example, if you want to send the report via the API.
|
||||||
|
|
||||||
All scheduling indicates when the reporting service will start rendering the dashboard. It can take a few minutes to render a dashboard with a lot of panels.
|
{{< figure src="/static/img/docs/enterprise/reports_scheduler_8.1.png" max-width="500px" class="docs-image--no-shadow" >}}
|
||||||
|
|
||||||
#### Hourly
|
|
||||||
|
|
||||||
Hourly reports are generated once per hour. All fields are required.
|
**Send now or schedule for later**
|
||||||
|
|
||||||
- **At minute -** The number of minutes after full hour when the report should be generated.
|
- **Send now** sends the report immediately after you save it. To stop sending the report at some point in the future, add an end date. If you leave the end date empty, the report is sent out indefinitely.
|
||||||
- **Time zone -** Time zone to determine the offset of the full hour. Does not currently change the time in the rendered report.
|
|
||||||
|
|
||||||
#### Daily
|
- **Send later** schedules a report for a later date. Thus, the start date and time are required fields. If you leave the end date empty, the report is sent out indefinitely.
|
||||||
|
|
||||||
Daily reports are generated once per day. All fields are required.
|
**Send only from Monday to Friday**
|
||||||
|
|
||||||
- **Time -** Time the report is sent, in 24-hour format.
|
For reports that have an hourly or daily frequency, you can choose to send them only from Monday to Friday.
|
||||||
- **Time zone -** Time zone for the **Time** field.
|
|
||||||
|
|
||||||
#### Weekly
|
**Send on the last day of the month**
|
||||||
|
|
||||||
Weekly reports are generated once per week. All fields are required.
|
When you schedule a report with a monthly frequency, and set the start date between the 29th and the 31st of the month, the report is only sent during the months that have those dates. If you want the report to be sent every month, select the **Send on the last day of the month** option instead. This way, the report is sent on the last day of every month regardless of how many days there are in any given month.
|
||||||
|
|
||||||
- **Day -** Weekday which the report should be sent on.
|
|
||||||
- **Time -** Time the report is sent, in 24-hour format.
|
|
||||||
- **Time zone -** Time zone for the **Time** field.
|
|
||||||
|
|
||||||
#### Monthly
|
|
||||||
|
|
||||||
> Only available in Grafana Enterprise v7.1+.
|
|
||||||
|
|
||||||
Monthly reports are generated once per month. All fields are required.
|
|
||||||
|
|
||||||
- **Day in month -** Day of the month when the report should be sent. You can select `last` for reports that should go out on the last day of the month.
|
|
||||||
- **Time -** Time the report is sent, in 24-hour format.
|
|
||||||
- **Time zone -** Time zone for the **Time** field.
|
|
||||||
|
|
||||||
#### Never
|
|
||||||
|
|
||||||
> Only available in Grafana Enterprise v7.0+.
|
|
||||||
|
|
||||||
Reports which are scheduled to never be sent have no parameter and will not be sent to the scheduler. They may be manually generated from the **Send test email** prompt or via the [Reporting API]({{< relref "../http_api/reporting.md" >}}).
|
|
||||||
|
|
||||||
### Send test email
|
### Send test email
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,12 @@ For example, if you provide the following `updates`:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"auth.saml": {
|
"updates": {
|
||||||
"enabled": "true",
|
"auth.saml": {
|
||||||
"single_logout": "false"
|
"enabled": "true",
|
||||||
|
"single_logout": "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
it would enable SAML and disable single logouts. And, if you provide the following `removals`:
|
it would enable SAML and disable single logouts. And, if you provide the following `removals`:
|
||||||
@@ -78,3 +81,8 @@ settings updates. If there are updates, it reloads the Grafana services affected
|
|||||||
|
|
||||||
The background job synchronizes settings between instances in high availability set-ups. So, after you perform some changes through the
|
The background job synchronizes settings between instances in high availability set-ups. So, after you perform some changes through the
|
||||||
HTTP API, then the other instances are synchronized through the database and the background job.
|
HTTP API, then the other instances are synchronized through the database and the background job.
|
||||||
|
|
||||||
|
## Control access with fine-grained access control
|
||||||
|
|
||||||
|
If you have [Fine-grained access Control]({{< relref "../enterprise/access-control/_index.md" >}}) enabled, you can control who can read or update settings.
|
||||||
|
Refer to the [Admin API]({{< relref "../http_api/admin.md#update-settings" >}}) for more information.
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ The configuration file in Grafana Enterprise contains the following options. Eac
|
|||||||
;apple_touch_icon =
|
;apple_touch_icon =
|
||||||
|
|
||||||
# Set to complete URL to override loading logo
|
# Set to complete URL to override loading logo
|
||||||
;loading_logo_url =
|
;loading_logo =
|
||||||
```
|
```
|
||||||
You can replace the default footer links (Documentation, Support, Community) and even add your own custom links.
|
You can replace the default footer links (Documentation, Support, Community) and even add your own custom links.
|
||||||
An example follows for replacing the default footer and help links with new custom links.
|
An example follows for replacing the default footer and help links with new custom links.
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ weight = 90
|
|||||||
|
|
||||||
Grafana's dashboard UI is all about building dashboards for visualization. Explore strips away the dashboard and panel options so that you can focus on the query. It helps you iterate until you have a working query and then think about building a dashboard.
|
Grafana's dashboard UI is all about building dashboards for visualization. Explore strips away the dashboard and panel options so that you can focus on the query. It helps you iterate until you have a working query and then think about building a dashboard.
|
||||||
|
|
||||||
|
> Refer to [Fine-grained access control]({{< relref "../enterprise/access-control/_index.md" >}}) in Grafana Enterprise to understand how you can control access with fine-grained permissions.
|
||||||
|
|
||||||
If you just want to explore your data and do not want to create a dashboard, then Explore makes this much easier. If your data source supports graph and table data, then Explore shows the results both as a graph and a table. This allows you to see trends in the data and more details at the same time. See also:
|
If you just want to explore your data and do not want to create a dashboard, then Explore makes this much easier. If your data source supports graph and table data, then Explore shows the results both as a graph and a table. This allows you to see trends in the data and more details at the same time. See also:
|
||||||
|
|
||||||
- [Query management in Explore]({{< relref "query-management.md" >}})
|
- [Query management in Explore]({{< relref "query-management.md" >}})
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ Shows or hides the unique labels column that includes only non-common labels. Al
|
|||||||
|
|
||||||
Set this to True if you want the display to use line wrapping. If set to False, it will result in horizontal scrolling.
|
Set this to True if you want the display to use line wrapping. If set to False, it will result in horizontal scrolling.
|
||||||
|
|
||||||
|
#### Prettify JSON
|
||||||
|
|
||||||
|
Set this to `true` to pretty print all JSON logs. This setting does not affect logs in any format other than JSON.
|
||||||
|
|
||||||
#### Deduping
|
#### 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:
|
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:
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ Refer to specific resources to understand what permissions are required.
|
|||||||
|
|
||||||
Only works with Basic Authentication (username and password). See [introduction](http://docs.grafana.org/http_api/admin/#admin-api) for an explanation.
|
Only works with Basic Authentication (username and password). See [introduction](http://docs.grafana.org/http_api/admin/#admin-api) for an explanation.
|
||||||
|
|
||||||
|
#### Required permissions
|
||||||
|
|
||||||
|
See note in the [introduction]({{< ref "#admin-api" >}}) for an explanation.
|
||||||
|
|
||||||
|
Action | Scope
|
||||||
|
--- | --- |
|
||||||
|
settings:read | settings:**<br>settings:auth.saml:*<br>settings:auth.saml:enabled (property level)
|
||||||
|
|
||||||
**Example Request**:
|
**Example Request**:
|
||||||
|
|
||||||
```http
|
```http
|
||||||
@@ -184,6 +192,14 @@ Updates / removes and reloads database settings. You must provide either `update
|
|||||||
HTTP/1.1 200
|
HTTP/1.1 200
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
`PUT /api/admin/users/:id/permissions`
|
||||||
|
|
||||||
|
Only works with Basic Authentication (username and password). See [introduction](http://docs.grafana.org/http_api/admin/#admin-api) for an explanation.
|
||||||
|
|
||||||
#### Required permissions
|
#### Required permissions
|
||||||
|
|
||||||
See note in the [introduction]({{< ref "#admin-api" >}}) for an explanation.
|
See note in the [introduction]({{< ref "#admin-api" >}}) for an explanation.
|
||||||
@@ -230,6 +246,14 @@ Status codes:
|
|||||||
Accept: application/json
|
Accept: application/json
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Example Response**:
|
||||||
|
|
||||||
|
```http
|
||||||
|
HTTP/1.1 200
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## Pause all alerts
|
## Pause all alerts
|
||||||
|
|
||||||
|
|||||||
@@ -237,7 +237,9 @@ Content-Length: 97
|
|||||||
|
|
||||||
`DELETE /api/folders/:uid`
|
`DELETE /api/folders/:uid`
|
||||||
|
|
||||||
Deletes an existing folder identified by uid together with all dashboards stored in the folder, if any. This operation cannot be reverted.
|
Deletes an existing folder identified by UID along with all dashboards (and their alerts) stored in the folder. This operation cannot be reverted.
|
||||||
|
|
||||||
|
If [Grafana 8 Alerts]({{< relref "../alerting/unified-alerting/_index.md" >}}) are enabled, you can set an optional query parameter `forceDeleteRules=false` so that requests will fail with 400 (Bad Request) error if the folder contains any Grafana 8 Alerts. However, if this parameter is set to `true` then it will delete any Grafana 8 Alerts under this folder.
|
||||||
|
|
||||||
**Example Request**:
|
**Example Request**:
|
||||||
|
|
||||||
@@ -265,6 +267,7 @@ Status Codes:
|
|||||||
|
|
||||||
- **200** – Deleted
|
- **200** – Deleted
|
||||||
- **401** – Unauthorized
|
- **401** – Unauthorized
|
||||||
|
- **400** – Bad Request
|
||||||
- **403** – Access Denied
|
- **403** – Access Denied
|
||||||
- **404** – Folder not found
|
- **404** – Folder not found
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,52 @@ weight = 130
|
|||||||
|
|
||||||
# Configure Grafana Live HA setup
|
# Configure Grafana Live HA setup
|
||||||
|
|
||||||
Live features in Grafana v8.0 are designed to work with a single Grafana server instance only. We will add the option for HA configuration in future Grafana releases to eliminate the current limitations.
|
By default, Grafana Live uses in-memory data structures and in-memory PUB/SUB hub for handling subscriptions.
|
||||||
|
|
||||||
Currently, if you have several Grafana server instances behind a load balancer, you may come across the following limitations:
|
In a high availability Grafana setup involving several Grafana server instances behind a load balancer, you can find the following limitations:
|
||||||
|
|
||||||
- Built-in features like dashboard change notifications will only be broadcasted to users connected to the same Grafana server process instance.
|
- Built-in features like dashboard change notifications will only be broadcasted to users connected to the same Grafana server process instance.
|
||||||
- Streaming from Telegraf will deliver data only to clients connected to the same instance which received Telegraf data, active stream cache is not shared between different Grafana instances.
|
- Streaming from Telegraf will deliver data only to clients connected to the same instance which received Telegraf data, active stream cache is not shared between different Grafana instances.
|
||||||
- A separate unidirectional stream between Grafana and backend data source may be opened on different Grafana servers for the same channel.
|
- A separate unidirectional stream between Grafana and backend data source may be opened on different Grafana servers for the same channel.
|
||||||
|
|
||||||
|
To bypass these limitations, Grafana v8.1 has an experimental Live HA engine that requires Redis to work.
|
||||||
|
|
||||||
|
## Configure Redis Live engine
|
||||||
|
|
||||||
|
When the Redis engine is configured, Grafana Live keeps its state in Redis and uses Redis PUB/SUB functionality to deliver messages to all subscribers throughout all Grafana server nodes.
|
||||||
|
|
||||||
|
Here is an example configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
[live]
|
||||||
|
ha_engine = redis
|
||||||
|
ha_engine_address = 127.0.0.1:6379
|
||||||
|
```
|
||||||
|
|
||||||
|
After running:
|
||||||
|
|
||||||
|
- All built-in real-time notifications like dashboard changes are delivered to all Grafana server instances and broadcasted to all subscribers.
|
||||||
|
- Streaming from Telegraf delivers messages to all subscribers.
|
||||||
|
- A separate unidirectional stream between Grafana and backend data source opens on different Grafana servers. Publishing data to a channel delivers messages to instance subscribers, as a result, publications from different instances on different machines do not produce duplicate data on panels.
|
||||||
|
|
||||||
|
At the moment we only support single Redis node.
|
||||||
|
|
||||||
|
> **Note:** It's possible to use Redis Sentinel and Haproxy to achieve a highly available Redis setup. Redis nodes should be managed by [Redis Sentinel](https://redis.io/topics/sentinel) to achieve automatic failover. Haproxy configuration example:
|
||||||
|
> ```
|
||||||
|
> listen redis
|
||||||
|
> server redis-01 127.0.0.1:6380 check port 6380 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 on-marked-down shutdown-sessions on-marked-up shutdown-backup-sessions
|
||||||
|
> server redis-02 127.0.0.1:6381 check port 6381 check inter 2s weight 1 inter 2s downinter 5s rise 10 fall 2 backup
|
||||||
|
> bind *:6379
|
||||||
|
> mode tcp
|
||||||
|
> option tcpka
|
||||||
|
> option tcplog
|
||||||
|
> option tcp-check
|
||||||
|
> tcp-check send PING\r\n
|
||||||
|
> tcp-check expect string +PONG
|
||||||
|
> tcp-check send info\ replication\r\n
|
||||||
|
> tcp-check expect string role:master
|
||||||
|
> tcp-check send QUIT\r\n
|
||||||
|
> tcp-check expect string +OK
|
||||||
|
> balance roundrobin
|
||||||
|
> ```
|
||||||
|
> Next, point Grafana Live to Haproxy address:port.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ weight = 300
|
|||||||
|
|
||||||
> **Note:** This is a new beta transformation introduced in v8.1.
|
> **Note:** This is a new beta transformation introduced in v8.1.
|
||||||
|
|
||||||
This transformation allow you select one query and from it extract [standard options]({{< relref "./standard-options.md" >}}) like **Min**, **Max**, **Unit** and **Thresholds** and apply it to other query results. This enables dynamic query driven visualization configuration.
|
This transformation allow you select one query and from it extract [standard options]({{< relref "../standard-options.md" >}}) like **Min**, **Max**, **Unit** and **Thresholds** and apply it to other query results. This enables dynamic query driven visualization configuration.
|
||||||
|
|
||||||
If you want to extract a unique config for every row in the config query result then try the [Rows to fields]({{< relref "./rows-to-fields" >}}) transformation instead.
|
If you want to extract a unique config for every row in the config query result then try the [Rows to fields]({{< relref "./rows-to-fields" >}}) transformation instead.
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ weight = 300
|
|||||||
|
|
||||||
This transforms rows into separate fields. This can be useful as fields can be styled and configured individually, something rows cannot. It can also use additional fields as sources for dynamic field configuration or map them to field labels. The additional labels can then be used to define better display names for the resulting fields.
|
This transforms rows into separate fields. This can be useful as fields can be styled and configured individually, something rows cannot. It can also use additional fields as sources for dynamic field configuration or map them to field labels. The additional labels can then be used to define better display names for the resulting fields.
|
||||||
|
|
||||||
Useful when visualization data in:
|
Useful when visualizing data in:
|
||||||
|
|
||||||
- Gauge
|
- Gauge
|
||||||
- Stat
|
- Stat
|
||||||
|
|||||||
@@ -6,6 +6,67 @@ aliases =["/docs/grafana/latest/features/panels/anotations/"]
|
|||||||
weight = 600
|
weight = 600
|
||||||
+++
|
+++
|
||||||
|
|
||||||
# Annotations
|
# Annotations
|
||||||
|
|
||||||
The Annotations panel visualization allows you to view ...
|
The Annotations panel shows a list of available annotations you can use to view annotated data. Various options are available to filter the list based on tags and on the current dashboard.
|
||||||
|
|
||||||
|
## Annotation query
|
||||||
|
|
||||||
|
The following options control the source query for the list of annotations.
|
||||||
|
|
||||||
|
### Query Filter
|
||||||
|
|
||||||
|
Use the query filter to create a list of annotations from all dashboards in your organization or the current dashboard in which this panel is located. It has the following options:
|
||||||
|
|
||||||
|
* All dashboards - List annotations from all dashboards in the current organization.
|
||||||
|
* This dashboard - Limit the list to the annotations on the current dashboard.
|
||||||
|
|
||||||
|
### Time Range
|
||||||
|
|
||||||
|
Use the time range option to specify whether the list should be limited to the current time range. It has the following options:
|
||||||
|
|
||||||
|
* None - no time range limit for the annotations query.
|
||||||
|
* This dashboard - Limit the list to the time range of the dashboard where the annotation list panel is available.
|
||||||
|
|
||||||
|
### Tags
|
||||||
|
|
||||||
|
Use the tags option to filter the annotations by tags. You can add multiple tags in order to refine the list.
|
||||||
|
|
||||||
|
> **Note:** Optionally, leave the tag list empty and filter on the fly by selecting tags that are listed as part of the results on the panel itself.
|
||||||
|
|
||||||
|
### Limit
|
||||||
|
|
||||||
|
Use the limit option to limit the number of results returned.
|
||||||
|
|
||||||
|
## Display
|
||||||
|
|
||||||
|
These options control additional meta-data included in the annotations panel display.
|
||||||
|
|
||||||
|
### Show user
|
||||||
|
|
||||||
|
Use this option to show or hide which user created the annotation.
|
||||||
|
|
||||||
|
### Show time
|
||||||
|
|
||||||
|
Use this option to show or hide the time the annotation creation time.
|
||||||
|
|
||||||
|
### Show Tags
|
||||||
|
|
||||||
|
Use this option to show or hide the tags associated with an annotation. *NB*: You can use the tags to live-filter the annotation list on the panel itself.
|
||||||
|
|
||||||
|
## Link behavior
|
||||||
|
|
||||||
|
### Link target
|
||||||
|
|
||||||
|
Use this option to chose how to view the annotated data. It has the following options.
|
||||||
|
|
||||||
|
* Panel - This option will take you directly to a full-screen view of the panel with the corresponding annotation
|
||||||
|
* Dashboard - This option will focus the annotation in the context of a complete dashboard
|
||||||
|
|
||||||
|
### Time before
|
||||||
|
|
||||||
|
Use this option to set the time range before the annotation. Use duration string values like "1h" = 1 hour, "10m" = 10 minutes, etc.
|
||||||
|
|
||||||
|
### Time after
|
||||||
|
|
||||||
|
Use this option to set the time range after the annotation.
|
||||||
|
|||||||
@@ -8,4 +8,127 @@ weight = 600
|
|||||||
|
|
||||||
# Geomap
|
# Geomap
|
||||||
|
|
||||||
The Geomap panel visualization allows you to view ...
|
The Geomap panel visualization allows you to view and customize the world map using geospatial data. You can configure various overlay styles and map view settings to easily focus on the important location-based characteristics of the data.
|
||||||
|
|
||||||
|
{{< figure src="/static/img/docs/geomap-panel/geomap-example-8-1-0.png" max-width="1200px" caption="Geomap panel" >}}
|
||||||
|
|
||||||
|
## Base layer
|
||||||
|
|
||||||
|
The base layer loads in a blank world map from the tile server to the Grafana panel. Several base layer options are available each with specific configuration options to style the base map. The default base layer is CartoDB base map. Custom default base layers can be defined in the `.ini` configuration file.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Configure the default base layer with provisioning
|
||||||
|
|
||||||
|
You can configure the default base map using config files with Grafana’s provisioning system. For more information on all the settings, refer to the [provisioning docs page]({{< relref "../../administration/provisioning.md" >}}).
|
||||||
|
|
||||||
|
Use the JSON configuration option `default_baselayer_config` to define the default base map. There are currently four base map options to choose from: `carto`, `esri-xyz`, `osm-standard`, `xyz`. Here are some provisioning examples for each base map option.
|
||||||
|
|
||||||
|
- **carto** loads the CartoDB tile server. You can choose from `auto`, `dark`, and `light` theme for the base map and can be set as shown below. The `showLabels` tag determines whether or not Grafana shows the Country details on top of the map. Here is an example:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
geomap_default_baselayer = `{
|
||||||
|
"type": "carto",
|
||||||
|
"config": {
|
||||||
|
"theme": "auto",
|
||||||
|
"showLabels": true
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
```
|
||||||
|
|
||||||
|
- **esri-xyz** loads the ESRI tile server. There are already multiple server instances implemented to show the various map styles: `world-imagery`, `world-physical`, `topo`, `usa-topo`, and `ocean`. The `custom` server option allows you to configure your own ArcGIS map server. Here are some examples:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
geomap_default_baselayer = `{
|
||||||
|
"type": "esri-xyz",
|
||||||
|
"config": {
|
||||||
|
"server": "world-imagery"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
geomap_default_baselayer = `{
|
||||||
|
"type": "esri-xyz",
|
||||||
|
"config": {
|
||||||
|
"server": "custom",
|
||||||
|
"url": "[tile server url]",
|
||||||
|
"attribution": "[tile server attribution]"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
```
|
||||||
|
|
||||||
|
- **osm-standard** loads the OpenStreetMap tile server. There are no additional configurations needed and the `config` fields can be left blank. Here is an example:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
default_baselayer_config = `{
|
||||||
|
"type": "osm-standard",
|
||||||
|
"config": {}
|
||||||
|
}`
|
||||||
|
```
|
||||||
|
|
||||||
|
- **xyz** loads a custom tile server defined by the user. Set a valid tile server `url`, with {z}/{x}/{y} for this option in order to properly load a default base map. Here is an example:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
default_baselayer_config = `{
|
||||||
|
"type": "xyz",
|
||||||
|
"config": {
|
||||||
|
"attribution": "Open street map",
|
||||||
|
"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
```
|
||||||
|
|
||||||
|
`enable_custom_baselayers` allows you to enable or disable custom open source base maps that are already implemented. The default is `true`.
|
||||||
|
|
||||||
|
## Data layer
|
||||||
|
|
||||||
|
The data layer in the Geomap plugin determines how you visualize geospatial data on top of the base map.
|
||||||
|
|
||||||
|
### Location
|
||||||
|
The Geomap panel needs a source of geographical data. This data comes from a database query, and there are four mapping options for your data.
|
||||||
|
|
||||||
|
- **Auto** automatically searches for location data. Use this option when your query is based on one of the following names for data fields.
|
||||||
|
- geohash: “geohash”
|
||||||
|
- latitude: “latitude”, “lat”
|
||||||
|
- longitude: “longitude”, “lng”, “lon”
|
||||||
|
- lookup: “lookup”
|
||||||
|
- **Coords** specifies that your query holds coordinate data. You will get prompted to select numeric data fields for latitude and longitude from your database query.
|
||||||
|
- **Geohash** specifies that your query holds geohash data. You will get prompted to select a string data field for the geohash from your database query.
|
||||||
|
- **Lookup** specifies that your query holds location name data that needs to be mapped to a value. You will get prompted to select the lookup field from your database query and a gazetteer. The gazetteer is the directory that is used to map your queried data to a geographical point.
|
||||||
|
|
||||||
|
### Markers layer
|
||||||
|
|
||||||
|
The markers layer allows you to display data points as different marker shapes such as circle, squares, triangles, stars, and more.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **Marker Color** configures the color of the marker. The default `Fixed size` keeps all points a single color. There is an alternate option to have multiple colors depending on the data point values and the threshold set at the `Thresholds` section.
|
||||||
|
- **Marker Size** configures the size of the marker. Default is `Fixed size`, making all marker size the same regardless of the data points. However, there is also an option to scale the circles to the corresponding data points. `Min` and `Max` marker size has to be set such that the Marker layer can scale within these range.
|
||||||
|
- **Marker Shape** provides you with the flexibility to visualize the data points differently.
|
||||||
|
- Circle
|
||||||
|
- Square
|
||||||
|
- Triangle
|
||||||
|
- Star
|
||||||
|
- Cross
|
||||||
|
- X
|
||||||
|
- **Fill opacity** configures the transparency of each marker.
|
||||||
|
|
||||||
|
### Heatmap layer
|
||||||
|
|
||||||
|
The heatmap layer clusters various data points to visualize locations with different densities.
|
||||||
|
To add a heatmap layer:
|
||||||
|
|
||||||
|
Click on the drop down menu under Data Layer and choose `Heatmap`.
|
||||||
|
|
||||||
|
Similar to `Markers`, you are prompted with various options to determine which data points to visualize and how.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **Weight values** configures the intensity of the heatmap clusters. `Fixed value` keeps a constant weight value throughout all data points. This value should be in the range of 0~1. Similar to Markers, there is an alternate option in the drop down to automatically scale the weight values depending on data values.
|
||||||
|
- **Radius** configures the size of the heatmap clusters.
|
||||||
|
- **Blur** configures the amount of blur on each cluster.
|
||||||
@@ -35,5 +35,6 @@ Use these settings to refine your visualization:
|
|||||||
- **Unique labels -** Show or hide the unique labels column, which shows only non-common labels.
|
- **Unique labels -** Show or hide the unique labels column, which shows only non-common labels.
|
||||||
- **Common labels -** Show or hide the common labels.
|
- **Common labels -** Show or hide the common labels.
|
||||||
- **Wrap lines -** Toggle line wrapping.
|
- **Wrap lines -** Toggle line wrapping.
|
||||||
|
- **Prettify JSON -** Set this to `true` to pretty print all JSON logs. This setting does not affect logs in any format other than JSON.
|
||||||
- **Enable log details -** Toggle option to see the log details view for each log row. The default setting is true.
|
- **Enable log details -** Toggle option to see the log details view for each log row. The default setting is true.
|
||||||
- **Order -** Display results in descending or ascending time order. The default is **Descending**, showing the newest logs first. Set to **Ascending** to show the oldest log lines first.
|
- **Order -** Display results in descending or ascending time order. The default is **Descending**, showing the newest logs first. Set to **Ascending** to show the oldest log lines first.
|
||||||
|
|||||||
@@ -6,63 +6,75 @@ weight = 1
|
|||||||
|
|
||||||
# Plugin catalog
|
# Plugin catalog
|
||||||
|
|
||||||
The plugin catalog allows you to browse and manage plugins from within Grafana. Only a Grafana server administrator can access and use the catalog.
|
The Plugin catalog allows you to browse and manage plugins from within Grafana. Only Grafana server administrators and organization administrators can access and use the plugin catalog. The following access rules apply depending on the user role:
|
||||||
|
|
||||||
> **Note:** The plugin catalog is designed to work with a single Grafana server instance only. Support for Grafana clusters will be added in future Grafana releases.
|
| Org Admin | Server Admin | Permissions |
|
||||||
|
| --------- | ------------ | ------------------------------------------------------------------------------------------- |
|
||||||
|
| ✓ | ✓ | <ul><li>Can configure app plugins</li><li>Can install/uninstall/update plugins</li></ul> |
|
||||||
|
| ✓ | × | <ul><li>Can configure app plugins</li><li>Cannot install/uninstall/update plugins</li></ul> |
|
||||||
|
| × | ✓ | <ul><li>Cannot configure app plugins</li><li>Can install/uninstall/update plugins</li></ul> |
|
||||||
|
|
||||||
|
> **Note:** The Plugin catalog is designed to work with a single Grafana server instance only. Support for Grafana clusters will be added in future Grafana releases.
|
||||||
|
|
||||||
<div class="medium-6 columns">
|
<div class="medium-6 columns">
|
||||||
<video width="700" height="600" controls>
|
<video width="700" height="600" controls>
|
||||||
<source src="/static/assets/videos/plugins-catalog-install-8-0.mp4" type="video/mp4">
|
<source src="/static/assets/videos/plugins-catalog-install-8-1.mp4" type="video/mp4">
|
||||||
Your browser does not support the video tag.
|
Your browser does not support the video tag.
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Before you can use the plugin catalog, you must enable it in the Grafana [configuration]({{< relref "../administration/configuration.md#plugin_admin_enabled" >}}) file.
|
Before you can use the Plugin catalog, you must enable it in the Grafana [configuration]({{< relref "../administration/configuration.md#plugin_admin_enabled" >}}) file.
|
||||||
Before following the steps below, make sure you are logged in as a Grafana administrator.
|
Before following the steps below, make sure you are logged in as a Grafana administrator.
|
||||||
|
|
||||||
|
<a id="#plugin-catalog-entry"></a>
|
||||||
|
Currently, there are two entry points to the Plugin catalog.
|
||||||
|
- Grafana server administrators can find it at **Server Admin >
|
||||||
|
Plugins**.
|
||||||
|
- Organization administrators can find it at **Configuration > Plugins**.
|
||||||
|
|
||||||
## Browse plugins
|
## Browse plugins
|
||||||
|
|
||||||
To browse available plugins:
|
To browse for available plugins:
|
||||||
|
|
||||||
1. In Grafana, navigate to **Configuration > Plugins**.
|
1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins.
|
||||||
1. Click **Install & manage plugins**.
|
1. Click the **All** filter to browse all available plugins.
|
||||||
|
1. Click the **Data sources**, **Panels**, or **Applications** buttons to filter by plugin type.
|
||||||
|
|
||||||
You can also browse existing plugins by navigating to the **Library** tab.
|

|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Install a plugin
|
## Install a plugin
|
||||||
|
|
||||||
1. In Grafana, navigate to **Configuration > Plugins**.
|
To install a plugin:
|
||||||
1. Click **Install & manage plugins**.
|
|
||||||
|
1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins.
|
||||||
1. Browse and find a plugin.
|
1. Browse and find a plugin.
|
||||||
1. Click on the plugin logo.
|
1. Click on the plugin logo.
|
||||||
1. Click **Install**.
|
1. Click **Install**.
|
||||||
|
|
||||||
A confirmation message opens notifying that the installation was successful.
|
When the update is complete, you see a confirmation message that the installation was successful.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Update a plugin
|
## Update a plugin
|
||||||
|
|
||||||
1. In Grafana, navigate to **Configuration > Plugins**.
|
To update a plugin:
|
||||||
1. Click **Install & manage plugins**.
|
|
||||||
1. Navigate to the **Library** tab.
|
1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins.
|
||||||
1. Click on the plugin logo.
|
1. Click on the plugin logo.
|
||||||
1. Click **Update**.
|
1. Click **Update**.
|
||||||
|
|
||||||
A confirmation message opens notifying that the installation was successful.
|
When the update is complete, you see a confirmation message that the update was successful.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Uninstall a plugin
|
## Uninstall a plugin
|
||||||
|
|
||||||
1. In Grafana, navigate to **Configuration > Plugins**.
|
To uninstall a plugin:
|
||||||
1. Click **Install & manage plugins**.
|
|
||||||
1. Navigate to the **Library** tab.
|
1. In Grafana, [navigate to the Plugin catalog](#plugin-catalog-entry) to view installed plugins.
|
||||||
1. Click on the plugin logo.
|
1. Click on the plugin logo.
|
||||||
1. Click **Uninstall**.
|
1. Click **Uninstall**.
|
||||||
|
|
||||||
A confirmation message opens notifying that the installation was successful.
|
When the update is complete, you see a confirmation message that the uninstall was successful.
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -8,6 +8,9 @@ weight = 10000
|
|||||||
Here you can find detailed release notes that list everything that is included in every release as well as notices
|
Here you can find detailed release notes that list everything that is included in every release as well as notices
|
||||||
about deprecations, breaking changes as well as changes that relate to plugin development.
|
about deprecations, breaking changes as well as changes that relate to plugin development.
|
||||||
|
|
||||||
|
- [Release notes for 8.1.0-beta3]({{< relref "release-notes-8-1-0-beta3" >}})
|
||||||
|
- [Release notes for 8.1.0-beta2]({{< relref "release-notes-8-1-0-beta2" >}})
|
||||||
|
- [Release notes for 8.1.0-beta1]({{< relref "release-notes-8-1-0-beta1" >}})
|
||||||
- [Release notes for 8.0.6]({{< relref "release-notes-8-0-6" >}})
|
- [Release notes for 8.0.6]({{< relref "release-notes-8-0-6" >}})
|
||||||
- [Release notes for 8.0.5]({{< relref "release-notes-8-0-5" >}})
|
- [Release notes for 8.0.5]({{< relref "release-notes-8-0-5" >}})
|
||||||
- [Release notes for 8.0.4]({{< relref "release-notes-8-0-4" >}})
|
- [Release notes for 8.0.4]({{< relref "release-notes-8-0-4" >}})
|
||||||
|
|||||||
91
docs/sources/release-notes/release-notes-8-1-0-beta1.md
Normal file
91
docs/sources/release-notes/release-notes-8-1-0-beta1.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
+++
|
||||||
|
title = "Release notes for Grafana 8.1.0-beta1"
|
||||||
|
[_build]
|
||||||
|
list = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
<!-- Auto generated by update changelog github action -->
|
||||||
|
|
||||||
|
# Release notes for Grafana 8.1.0-beta1
|
||||||
|
|
||||||
|
### Features and enhancements
|
||||||
|
|
||||||
|
* **Alerting:** Add Alertmanager notifications tab. [#35759](https://github.com/grafana/grafana/pull/35759), [@nathanrodman](https://github.com/nathanrodman)
|
||||||
|
* **Alerting:** Add button to deactivate current Alertmanager configuration. [#36951](https://github.com/grafana/grafana/pull/36951), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** Add toggle in Loki/Prometheus data source configuration to opt out of alerting UI. [#36552](https://github.com/grafana/grafana/pull/36552), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** Allow any "evaluate for" value >=0 in the alert rule form. [#35807](https://github.com/grafana/grafana/pull/35807), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** Load default configuration from status endpoint, if Cortex Alertmanager returns empty user configuration. [#35769](https://github.com/grafana/grafana/pull/35769), [@domasx2](https://github.com/domasx2)
|
||||||
|
* **Alerting:** view to display alert rule and its underlying data. [#35546](https://github.com/grafana/grafana/pull/35546), [@mckn](https://github.com/mckn)
|
||||||
|
* **Annotation panel:** Release the annotation panel. [#36959](https://github.com/grafana/grafana/pull/36959), [@ryantxu](https://github.com/ryantxu)
|
||||||
|
* **Annotations:** Add typeahead support for tags in built-in annotations. [#36377](https://github.com/grafana/grafana/pull/36377), [@ashharrison90](https://github.com/ashharrison90)
|
||||||
|
* **AzureMonitor:** Add curated dashboards for Azure services. [#35356](https://github.com/grafana/grafana/pull/35356), [@avidhanju](https://github.com/avidhanju)
|
||||||
|
* **AzureMonitor:** Add support for deep links to Microsoft Azure portal for Metrics. [#32273](https://github.com/grafana/grafana/pull/32273), [@shuotli](https://github.com/shuotli)
|
||||||
|
* **AzureMonitor:** Remove support for different credentials for Azure Monitor Logs. [#35121](https://github.com/grafana/grafana/pull/35121), [@andresmgot](https://github.com/andresmgot)
|
||||||
|
* **AzureMonitor:** Support querying any Resource for Logs queries. [#33879](https://github.com/grafana/grafana/pull/33879), [@joshhunt](https://github.com/joshhunt)
|
||||||
|
* **Elasticsearch:** Add frozen indices search support. [#36018](https://github.com/grafana/grafana/pull/36018), [@Elfo404](https://github.com/Elfo404)
|
||||||
|
* **Elasticsearch:** Name fields after template variables values instead of their name. [#36035](https://github.com/grafana/grafana/pull/36035), [@Elfo404](https://github.com/Elfo404)
|
||||||
|
* **Elasticsearch:** add rate aggregation. [#33311](https://github.com/grafana/grafana/pull/33311), [@estermv](https://github.com/estermv)
|
||||||
|
* **Email:** Allow configuration of content types for email notifications. [#34530](https://github.com/grafana/grafana/pull/34530), [@djairhogeuens](https://github.com/djairhogeuens)
|
||||||
|
* **Explore:** Add more meta information when line limit is hit. [#33069](https://github.com/grafana/grafana/pull/33069), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Explore:** UI improvements to trace view. [#34276](https://github.com/grafana/grafana/pull/34276), [@aocenas](https://github.com/aocenas)
|
||||||
|
* **FieldOverrides:** Added support to change display name in an override field and have it be matched by a later rule. [#35893](https://github.com/grafana/grafana/pull/35893), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **HTTP Client:** Introduce `dataproxy_max_idle_connections` config variable. [#35864](https://github.com/grafana/grafana/pull/35864), [@dsotirakis](https://github.com/dsotirakis)
|
||||||
|
* **InfluxDB:** InfluxQL: adds tags to timeseries data. [#36702](https://github.com/grafana/grafana/pull/36702), [@gabor](https://github.com/gabor)
|
||||||
|
* **InfluxDB:** InfluxQL: make measurement search case insensitive. [#34563](https://github.com/grafana/grafana/pull/34563), [@gabor](https://github.com/gabor)
|
||||||
|
* **Legacy Alerting:** Replace simplejson with a struct in webhook notification channel. [#34952](https://github.com/grafana/grafana/pull/34952), [@KEVISONG](https://github.com/KEVISONG)
|
||||||
|
* **Legend:** Updates display name for Last (not null) to just Last*. [#35633](https://github.com/grafana/grafana/pull/35633), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **Logs panel:** Add option to show common labels. [#36166](https://github.com/grafana/grafana/pull/36166), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Loki:** Add $__range variable. [#36175](https://github.com/grafana/grafana/pull/36175), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Loki:** Add support for "label_values(log stream selector, label)" in templating. [#35488](https://github.com/grafana/grafana/pull/35488), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Loki:** Add support for ad-hoc filtering in dashboard. [#36393](https://github.com/grafana/grafana/pull/36393), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **MySQL Datasource:** Add timezone parameter. [#27535](https://github.com/grafana/grafana/pull/27535), [@andipabst](https://github.com/andipabst)
|
||||||
|
* **NodeGraph:** Show gradient fields in legend. [#34078](https://github.com/grafana/grafana/pull/34078), [@aocenas](https://github.com/aocenas)
|
||||||
|
* **PanelOptions:** Don't mutate panel options/field config object when updating. [#36441](https://github.com/grafana/grafana/pull/36441), [@dprokop](https://github.com/dprokop)
|
||||||
|
* **PieChart:** Make pie gradient more subtle to match other charts. [#36961](https://github.com/grafana/grafana/pull/36961), [@nikki-kiga](https://github.com/nikki-kiga)
|
||||||
|
* **Prometheus:** Update PromQL typeahead and highlighting. [#36730](https://github.com/grafana/grafana/pull/36730), [@ekpdt](https://github.com/ekpdt)
|
||||||
|
* **Prometheus:** interpolate variable for step field. [#36437](https://github.com/grafana/grafana/pull/36437), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **Provisioning:** Improve validation by validating across all dashboard providers. [#26742](https://github.com/grafana/grafana/pull/26742), [@nabokihms](https://github.com/nabokihms)
|
||||||
|
* **SQL Datasources:** Allow multiple string/labels columns with time series. [#36485](https://github.com/grafana/grafana/pull/36485), [@kylebrandt](https://github.com/kylebrandt)
|
||||||
|
* **Select:** Portal select menu to document.body. [#36398](https://github.com/grafana/grafana/pull/36398), [@ashharrison90](https://github.com/ashharrison90)
|
||||||
|
* **Team Sync:** Add group mapping to support team sync in the Generic OAuth provider. [#36307](https://github.com/grafana/grafana/pull/36307), [@wardbekker](https://github.com/wardbekker)
|
||||||
|
* **Tooltip:** Make active series more noticeable. [#36824](https://github.com/grafana/grafana/pull/36824), [@nikki-kiga](https://github.com/nikki-kiga)
|
||||||
|
* **Tracing:** Add support to configure trace to logs start and end time. [#34995](https://github.com/grafana/grafana/pull/34995), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **Transformations:** Skip merge when there is only a single data frame. [#36407](https://github.com/grafana/grafana/pull/36407), [@edgarpoce](https://github.com/edgarpoce)
|
||||||
|
* **ValueMapping:** Added support for mapping text to color, boolean values, NaN and Null. Improved UI for value mapping. [#33820](https://github.com/grafana/grafana/pull/33820), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **Visualizations:** Dynamically set any config (min, max, unit, color, thresholds) from query results. [#36548](https://github.com/grafana/grafana/pull/36548), [@torkelo](https://github.com/torkelo)
|
||||||
|
* **live:** Add support to handle origin without a value for the port when matching with root_url. [#36834](https://github.com/grafana/grafana/pull/36834), [@FZambia](https://github.com/FZambia)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* **Alerting:** Handle marshaling Inf values. [#36947](https://github.com/grafana/grafana/pull/36947), [@kylebrandt](https://github.com/kylebrandt)
|
||||||
|
* **AzureMonitor:** Fix macro resolution for template variables. [#36944](https://github.com/grafana/grafana/pull/36944), [@andresmgot](https://github.com/andresmgot)
|
||||||
|
* **AzureMonitor:** Fix queries with Microsoft.NetApp/../../volumes resources. [#32661](https://github.com/grafana/grafana/pull/32661), [@pckls](https://github.com/pckls)
|
||||||
|
* **AzureMonitor:** Request and concat subsequent resource pages. [#36958](https://github.com/grafana/grafana/pull/36958), [@andresmgot](https://github.com/andresmgot)
|
||||||
|
* **Bug:** Fix parse duration for day. [#36942](https://github.com/grafana/grafana/pull/36942), [@idafurjes](https://github.com/idafurjes)
|
||||||
|
* **Datasources:** Improve error handling for error messages. [#35120](https://github.com/grafana/grafana/pull/35120), [@ifrost](https://github.com/ifrost)
|
||||||
|
* **Explore:** Correct the functionality of shift-enter shortcut across all uses. [#36600](https://github.com/grafana/grafana/pull/36600), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Explore:** Show all dataFrames in data tab in Inspector. [#32161](https://github.com/grafana/grafana/pull/32161), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **GraphNG:** Fix Tooltip mode 'All' for XYChart. [#31260](https://github.com/grafana/grafana/pull/31260), [@Posnet](https://github.com/Posnet)
|
||||||
|
* **Loki:** Fix highlight of logs when using filter expressions with backticks. [#36024](https://github.com/grafana/grafana/pull/36024), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||||
|
* **Modal:** Force modal content to overflow with scroll. [#36754](https://github.com/grafana/grafana/pull/36754), [@ashharrison90](https://github.com/ashharrison90)
|
||||||
|
* **Plugins:** Ignore symlinked folders when verifying plugin signature. [#34434](https://github.com/grafana/grafana/pull/34434), [@wbrowne](https://github.com/wbrowne)
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
|
||||||
|
When parsing Elasticsearch query responses using template variables, each field gets named after the variable value instead of the name.
|
||||||
|
For example, executing a `terms` aggregation on a variable named `$groupBy` that has `@hostname` as a value, the resulting column in the table response will be `@hostname` instead of `$groupBy` Issue [#36035](https://github.com/grafana/grafana/issues/36035)
|
||||||
|
|
||||||
|
|
||||||
|
Azure Monitor data source no longer supports different credentials for Metrics and Logs in existing data sources. To use different credentials for Azure Monitor Logs, create another data source. Issue [#35121](https://github.com/grafana/grafana/issues/35121)
|
||||||
|
|
||||||
|
Existing Azure Metrics Logs queries for Log Analytics Workspaces should be backward compatible with this change and should not get impacted. Panels will be migrated to use the new resource-centric backend when you first edit and save them.
|
||||||
|
|
||||||
|
Application Insights and Insights Analytics queries are now read-only and cannot be modified. To update Application Insights queries, users can manually recreate them as Metrics queries, and Insights Analytics are recreated with Logs.
|
||||||
|
|
||||||
|
Issue [#33879](https://github.com/grafana/grafana/issues/33879)
|
||||||
|
|
||||||
|
### Plugin development fixes & changes
|
||||||
|
|
||||||
|
* **Toolkit:** Improve error messages when tasks fail. [#36381](https://github.com/grafana/grafana/pull/36381), [@joshhunt](https://github.com/joshhunt)
|
||||||
|
|
||||||
21
docs/sources/release-notes/release-notes-8-1-0-beta2.md
Normal file
21
docs/sources/release-notes/release-notes-8-1-0-beta2.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
+++
|
||||||
|
title = "Release notes for Grafana 8.1.0-beta2"
|
||||||
|
[_build]
|
||||||
|
list = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
<!-- Auto generated by update changelog github action -->
|
||||||
|
|
||||||
|
# Release notes for Grafana 8.1.0-beta2
|
||||||
|
|
||||||
|
### Features and enhancements
|
||||||
|
|
||||||
|
* **Alerting:** Expand the value string in alert annotations and labels. [#37051](https://github.com/grafana/grafana/pull/37051), [@gerobinson](https://github.com/gerobinson)
|
||||||
|
* **Auth:** Add Azure HTTP authentication middleware. [#36932](https://github.com/grafana/grafana/pull/36932), [@kostrse](https://github.com/kostrse)
|
||||||
|
* **Auth:** Auth: Pass user role when using the authentication proxy. [#36729](https://github.com/grafana/grafana/pull/36729), [@yuwaMSFT2](https://github.com/yuwaMSFT2)
|
||||||
|
* **Gazetteer:** Update countries.json file to allow for linking to 3-letter country codes. [#37129](https://github.com/grafana/grafana/pull/37129), [@bryanuribe](https://github.com/bryanuribe)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* **Config:** Fix Docker builds by correcting formatting in sample.ini. [#37106](https://github.com/grafana/grafana/pull/37106), [@FZambia](https://github.com/FZambia)
|
||||||
|
* **Explore:** Fix encoding of internal URLs. [#36919](https://github.com/grafana/grafana/pull/36919), [@aocenas](https://github.com/aocenas)
|
||||||
24
docs/sources/release-notes/release-notes-8-1-0-beta3.md
Normal file
24
docs/sources/release-notes/release-notes-8-1-0-beta3.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
+++
|
||||||
|
title = "Release notes for Grafana 8.1.0-beta3"
|
||||||
|
[_build]
|
||||||
|
list = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
<!-- Auto generated by update changelog github action -->
|
||||||
|
|
||||||
|
# Release notes for Grafana 8.1.0-beta3
|
||||||
|
|
||||||
|
### Features and enhancements
|
||||||
|
|
||||||
|
* **Alerting:** Support label matcher syntax in alert rule list filter. [#36408](https://github.com/grafana/grafana/pull/36408), [@nathanrodman](https://github.com/nathanrodman)
|
||||||
|
* **IconButton:** Put tooltip text as aria-label. [#36760](https://github.com/grafana/grafana/pull/36760), [@tskarhed](https://github.com/tskarhed)
|
||||||
|
* **Live:** Experimental HA with Redis. [#36787](https://github.com/grafana/grafana/pull/36787), [@FZambia](https://github.com/FZambia)
|
||||||
|
* **UI:** FileDropzone component. [#36646](https://github.com/grafana/grafana/pull/36646), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **[v8.1.x] CloudWatch:** Add AWS LookoutMetrics. [#37329](https://github.com/grafana/grafana/pull/37329), [@ilyastoli](https://github.com/ilyastoli)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* **Docker:** Fix builds by delaying go mod verify until all required files are copied over. [#37246](https://github.com/grafana/grafana/pull/37246), [@wbrowne](https://github.com/wbrowne)
|
||||||
|
* **Exemplars:** Fix disable exemplars only on the query that failed. [#37296](https://github.com/grafana/grafana/pull/37296), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||||
|
* **SQL:** Fix SQL dataframe resampling (fill mode + time intervals). [#36937](https://github.com/grafana/grafana/pull/36937), [@idafurjes](https://github.com/idafurjes)
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ You can use the `$__interval` variable as a parameter to group by time (for Infl
|
|||||||
|
|
||||||
Grafana automatically calculates an interval that can be used to group by time in queries. When there are more data points than can be shown on a graph then queries can be made more efficient by grouping by a larger interval. It is more efficient to group by 1 day than by 10s when looking at 3 months of data and the graph will look the same and the query will be faster. The `$__interval` is calculated using the time range and the width of the graph (the number of pixels).
|
Grafana automatically calculates an interval that can be used to group by time in queries. When there are more data points than can be shown on a graph then queries can be made more efficient by grouping by a larger interval. It is more efficient to group by 1 day than by 10s when looking at 3 months of data and the graph will look the same and the query will be faster. The `$__interval` is calculated using the time range and the width of the graph (the number of pixels).
|
||||||
|
|
||||||
Approximate Calculation: `(from - to) / resolution`
|
Approximate Calculation: `(to - from) / resolution`
|
||||||
|
|
||||||
For example, when the time range is 1 hour and the graph is full screen, then the interval might be calculated to `2m` - points are grouped in 2 minute intervals. If the time range is 6 months and the graph is full screen, then the interval might be `1d` (1 day) - points are grouped by day.
|
For example, when the time range is 1 hour and the graph is full screen, then the interval might be calculated to `2m` - points are grouped in 2 minute intervals. If the time range is 6 months and the graph is full screen, then the interval might be `1d` (1 day) - points are grouped by day.
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ as info on deprecations, breaking changes and plugin development read the [relea
|
|||||||
|
|
||||||
## Grafana 8
|
## Grafana 8
|
||||||
|
|
||||||
|
- [What's new in 8.1]({{< relref "whats-new-in-v8-1" >}})
|
||||||
- [What's new in 8.0]({{< relref "whats-new-in-v8-0" >}})
|
- [What's new in 8.0]({{< relref "whats-new-in-v8-0" >}})
|
||||||
|
|
||||||
## Grafana 7
|
## Grafana 7
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ list = false
|
|||||||
|
|
||||||
# What’s new in Grafana v8.0
|
# What’s new in Grafana v8.0
|
||||||
|
|
||||||
> **Note:** This topic will be updated frequently between now and the final release.
|
|
||||||
|
|
||||||
This topic includes the release notes for Grafana v8.0. For all details, read the full [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md).
|
This topic includes the release notes for Grafana v8.0. For all details, read the full [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
## Grafana OSS features
|
## Grafana OSS features
|
||||||
|
|||||||
170
docs/sources/whatsnew/whats-new-in-v8-1.md
Normal file
170
docs/sources/whatsnew/whats-new-in-v8-1.md
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
+++
|
||||||
|
title = "What's new in Grafana v8.1"
|
||||||
|
description = "Feature and improvement highlights for Grafana v8.1"
|
||||||
|
keywords = ["grafana", "new", "documentation", "8.1", "release notes"]
|
||||||
|
weight = -33
|
||||||
|
aliases = ["/docs/grafana/latest/guides/whats-new-in-v8-1/"]
|
||||||
|
[_build]
|
||||||
|
list = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
# What’s new in Grafana v8.1
|
||||||
|
|
||||||
|
> **Note:** This topic will be updated frequently between now and the final release.
|
||||||
|
|
||||||
|
Grafana 8.1 builds upon our promise of a composable, open observability platform with new panels and extends functionality launched in Grafana 8.0. We’ve got new Geomap and Annotations panels, and some great updates to the Time Series panel. We’ve also got new transformations and updates to data sources. For our enterprise customers, there are additions to fine grained access control, updates to the reporting schedule and query caching, and more. Read on to learn more.
|
||||||
|
|
||||||
|
In addition to what is summarized here, you might also be interested in our announcement blog post. For all the technical details, check out the complete [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
|
|
||||||
|
## Grafana OSS features
|
||||||
|
|
||||||
|
These features are included in the Grafana open source edition.
|
||||||
|
|
||||||
|
## Geomap panel
|
||||||
|
|
||||||
|
Grafana 8.1 introduces the foundation for our new map panel. This new panel leverages [OpenLayers](https://openlayers.org/) and gives us a flexible solution for extending the way we use the new Geomap panel moving forward. The new Geomap panel includes multiple base layer styles (map layer options), as well as a more open data layer. The data layer can use coordinates and geo-hashed data in addition to a lookup table.
|
||||||
|
|
||||||
|
The Geomap panel is also able to share views across multiple Geomap panels on the same dashboard, making it straightforward to visualize and explore multiple types of geospatial data using the same map zoom and focus settings. For more information, refer to [Geomap panel]({{< relref "../panels/visualizations/geomap.md" >}}).
|
||||||
|
{{< figure src="/static/img/docs/geomap-panel/geomap_with_heatmap.png" max-width="850px" caption="Geomap panel: Heatmap" >}}
|
||||||
|
|
||||||
|
## Annotation panel
|
||||||
|
|
||||||
|
The new Annotations panel shows a list of available annotations you can use to create lists of annotated data available within your organization. Various options are available to filter the list based on the tags and on the current dashboard. This panel makes it easy to find and filter annotated data within and across multiple dashboards.
|
||||||
|
|
||||||
|
{{< figure src="/static/img/docs/annotations-panel/annolist.png" max-width="900px" caption="Annotations panel" >}}
|
||||||
|
|
||||||
|
### Time series panel updates
|
||||||
|
|
||||||
|
The time series panel has been updated with the ability to color series and line by thresholds or gradient color scales. This allows users to create panels where the line color can change dynamically based on thresholds or using gradient color scales. It adds a layer of visibility to your data, making it easier to view the changes across thresholds at a glance quickly.
|
||||||
|
|
||||||
|
|
||||||
|
Color scheme **From thresholds**:
|
||||||
|
{{< figure src="/static/img/docs/time-series-panel/gradient_mode_scheme_thresholds_line.png" max-width="1200px" caption="Colors scheme: From thresholds" >}}
|
||||||
|
|
||||||
|
Color scheme: **Green-Yellow-Red (by value)**
|
||||||
|
{{< figure src="/static/img/docs/time-series-panel/gradient_mode_scheme_line.png" max-width="1200px" caption="Color scheme: Green-Yellow-Red" >}}
|
||||||
|
|
||||||
|
For more on how to configure Graphs with by value color schemes read [Graph and color schemes]({{< relref "../panels/visualizations/time-series/_index.md" >}}).
|
||||||
|
|
||||||
|
You can also create annotations on the new Time series panel]({{< relref "../panels/visualizations/time-series/annotate-time-series.md" >}}), bringing it closer to parity with the legacy Graph Panel. To learn more, refer to the [time series panel]({{< relref "../panels/visualizations/time-series/_index.md" >}}).
|
||||||
|
|
||||||
|
### Transformations improvements
|
||||||
|
|
||||||
|
Grafana 8.1 includes some significant enhancements to transformations, including two new transformations designed around providing dynamic configuration to your panels and visulizations.
|
||||||
|
|
||||||
|
#### Config from query (Beta)
|
||||||
|
|
||||||
|
This transformation enables panel config (Threshold, Min, Max, etc.) to be derived from query results. For more information, refer to [Config from query results transform]({{< relref "../panels/transformations/config-from-query.md" >}}).
|
||||||
|
|
||||||
|
#### Rows to fields (Beta)
|
||||||
|
|
||||||
|
This transformation enables rows in returned data to be converted into separate fields. Prior to this enhancement, you could style and configure fields individually, but not rows. For more information, refer to [Rows to fields transform].
|
||||||
|
|
||||||
|
Example, Input:
|
||||||
|
|
||||||
|
| Name | Value | Max |
|
||||||
|
| ------- | ----- | --- |
|
||||||
|
| ServerA | 10 | 100 |
|
||||||
|
| ServerB | 20 | 200 |
|
||||||
|
| ServerC | 30 | 300 |
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
| ServerA (config: max=100) | ServerB (config: max=200) | ServerC (config: max=300) |
|
||||||
|
| ------------------------- | ------------------------- | ------------------------- |
|
||||||
|
| 10 | 20 | 30 |
|
||||||
|
|
||||||
|
As you can see each row in the source data becomes a separate field. Each field now also has a max config option set. Options like **Min**, **Max**, **Unit** and **Thresholds** are all part of field configuration and if set like this will be used by the visualization instead of any options manually configured in the panel editor options pane.
|
||||||
|
|
||||||
|
For more on how to use this transformation, refer to [Rows to fields transform]({{< relref "../panels/transformations/rows-to-fields.md" >}}).
|
||||||
|
|
||||||
|
#### Contextual & Inline Help
|
||||||
|
|
||||||
|
Additional inline help will be available for Transformations. We can now share examples of how to use specific transformations and point users directly to the appropriate place in the docs for more information.
|
||||||
|
|
||||||
|
### Data source updates
|
||||||
|
|
||||||
|
The following data source updates are included with this Grafana release.
|
||||||
|
|
||||||
|
#### MySQL Data Source
|
||||||
|
|
||||||
|
We have added timezone support. As a result, you can now specify the time zone used in the database session, such as `Europe/Berlin` or `+02:00`.
|
||||||
|
|
||||||
|
### Trace to logs improvements
|
||||||
|
|
||||||
|
We changed the default behavior from creating a 1-hour span Loki query to the only query at the exact time the trace span started for the duration of it. For more fine grained control over this, you can shift this time in the tracing data source settings. Also, it is now possible to shift the start time and end time of the Loki query by the set amount. For more information, refer to [Trace to logs]({{< relref "../datasources/tempo.md#trace-to-logs" >}}).
|
||||||
|
|
||||||
|
### Prettify JSON for Logs in Explore
|
||||||
|
|
||||||
|
Added the ability to format JSON to make it easier to view, review and find relevant data in JSON format logs. This is a regular JSON log.
|
||||||
|
|
||||||
|
{{< figure src="/static/img/docs/panels/pretty-json/regular-log-line.png" max-width="1200px" caption="Regular log line" >}}
|
||||||
|
|
||||||
|
And here is the prettified JSON log.
|
||||||
|
|
||||||
|
{{< figure src="/static/img/docs/panels/pretty-json/prettified-json.png" max-width="1200px" caption="Prettified JSON" >}}
|
||||||
|
|
||||||
|
For more on how to prettify JSON logs, refer to [Visualization]({{< relref "../panels/visualizations/_index.md" >}}) and [Display]({{< relref "../panels/visualizations/logs-panel.md" >}}) options.
|
||||||
|
|
||||||
|
### Plugin catalog - Updated UX and extended features
|
||||||
|
|
||||||
|
We’ve made some changes to the plugins UI to help make it easier to discover and manage your plugins. Enterprise users can now also manage enterprise plugins from within the catalog.
|
||||||
|
|
||||||
|
|
||||||
|
#### Documentation updates
|
||||||
|
|
||||||
|
New panel summaries and preview on the top level [Visualizations]({{< relref "../panels/visualizations/_index.md" >}}) page to help users pick or learn about specific visualizations more easily.
|
||||||
|
|
||||||
|
### Upcoming changes to the Select component
|
||||||
|
|
||||||
|
The `@grafana/ui` exposes a `Select` component, and its variants `MultiSelect`, `AsyncSelect`, and `AsyncMultiSelect`. We have made some internal changes to these components to make the behavior and positioning more consistent in all scenarios.
|
||||||
|
|
||||||
|
To test the changes, you can use the `menuShouldPortal` property:
|
||||||
|
```jsx
|
||||||
|
<Select
|
||||||
|
menuShouldPortal
|
||||||
|
{...otherProps}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests are most likely to be affected. There are some tips for fixing these in the original pull request at [https://github.com/grafana/grafana/pull/36398](https://github.com/grafana/grafana/pull/36398).
|
||||||
|
|
||||||
|
We’d love as much feedback as possible about this change, because we are considering making this the default behavior in a future release of Grafana.
|
||||||
|
|
||||||
|
## Enterprise features
|
||||||
|
|
||||||
|
These features are included in the Grafana Enterprise edition.
|
||||||
|
|
||||||
|
### New permissions for fine-grained access control
|
||||||
|
|
||||||
|
Fine-grained access control remains in beta. You can now grant or revoke permissions for Viewers, Editors, or Admins to use Explore mode, configure LDAP or SAML settings, or view the admin/stats page. These new permissions enhance the existing permissions that can be customized, namely permissions to access Users, Orgs, LDAP settings, and Reports in Grafana.
|
||||||
|
|
||||||
|
Fine grained access control allows you to customize roles and permissions in Grafana beyond the built-in Viewer, Editor, and Admin roles. As of 8.1, you can modify some of the permissions for any of these built-in roles. This is helpful if you’d like users to have more or fewer access permissions than a given role allows for by default. For an overview of fine-grained access control and a complete list of available permissions, refer to the [Fine grained access control]({{< relref "../enterprise/access-control/_index.md" >}}) documentation.
|
||||||
|
|
||||||
|
|
||||||
|
### New and improved reporting scheduler
|
||||||
|
|
||||||
|
We’ve enhanced the scheduler for Reports to be more flexible, so you can send reports at just the right time. When scheduling a report, you can now choose to send a report at custom intervals such as every 4 hours or every 2 weeks. You can also send a report for a limited time period by providing a start and end date, or send a report only on weekdays or on the last day of each month. This change accompanies some other recent improvements to Reporting, like the ability to choose template variables for reports and an improved UX for authoring reports. To learn more, refer to the [reporting]({{< relref "../enterprise/reporting.md" >}}) documentation.
|
||||||
|
|
||||||
|
### Encrypt data in the query cache
|
||||||
|
|
||||||
|
Query caching was released in Grafana 8.0 and allows you to temporarily store the results of data source queries in a cache, so that Grafana reads repeated queries from there instead of from the data source itself. This reduces load on data sources, improves dashboard load times, and can save money for data sources that charge per query. To learn more about query caching see its [overview]({{< relref "../enterprise/query-caching.md" >}}) page. To find out how to turn on encryption, refer to the [caching configuration]({{< relref "../enterprise/enterprise-configuration.md#caching" >}}) documentation.
|
||||||
|
|
||||||
|
You can now encrypt the query data cached by Grafana. This improves the security of query data, especially when your cache (like Redis) is shared with other services.
|
||||||
|
|
||||||
|
|
||||||
|
### White labeling for the Grafana loading logo
|
||||||
|
|
||||||
|
You can now customize Grafana’s loading logo, which displays while Grafana is loading in a user’s browser. White labeling in Grafana Enterprise allows you to customize the look and feel of Grafana to match your product’s or company’s brand. This makes Grafana a more integrated part of your observability stack and keep Grafana consistent with other visualizations displayed in public.
|
||||||
|
|
||||||
|
To find out how you can configure it along with other Grafana UI elements, like the corner logo and application footer, refer to the [White labeling]({{< relref "../enterprise/white-labeling.md" >}}) topic of the Grafana Enterprise docs.
|
||||||
|
|
||||||
|
### Oauth2 - Team Sync to Group Mapping
|
||||||
|
|
||||||
|
With Team Sync you can map your Generic OAuth groups to teams in Grafana so that the users are automatically added to the correct teams.
|
||||||
|
|
||||||
|
### Support for AES-GCM encryption algorithm
|
||||||
|
|
||||||
|
Added support for AES-GCM cipher mode, preferred by some security standards and teams. By default, Grafana uses the AES-CFB cipher mode to encrypt data source credentials and usernames in Grafana’s database.
|
||||||
|
|
||||||
41
go.mod
41
go.mod
@@ -19,7 +19,7 @@ require (
|
|||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/Masterminds/semver v1.5.0
|
github.com/Masterminds/semver v1.5.0
|
||||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f
|
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f
|
||||||
github.com/aws/aws-sdk-go v1.38.34
|
github.com/aws/aws-sdk-go v1.38.68
|
||||||
github.com/beevik/etree v1.1.0
|
github.com/beevik/etree v1.1.0
|
||||||
github.com/benbjohnson/clock v1.1.0
|
github.com/benbjohnson/clock v1.1.0
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
||||||
@@ -36,7 +36,7 @@ require (
|
|||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/gchaincl/sqlhooks v1.3.0
|
github.com/gchaincl/sqlhooks v1.3.0
|
||||||
github.com/getsentry/sentry-go v0.10.0
|
github.com/getsentry/sentry-go v0.10.0
|
||||||
github.com/go-kit/kit v0.10.0
|
github.com/go-kit/kit v0.11.0
|
||||||
github.com/go-macaron/binding v0.0.0-20190806013118-0b4f37bab25b
|
github.com/go-macaron/binding v0.0.0-20190806013118-0b4f37bab25b
|
||||||
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
|
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
|
||||||
github.com/go-openapi/strfmt v0.20.1
|
github.com/go-openapi/strfmt v0.20.1
|
||||||
@@ -46,16 +46,17 @@ require (
|
|||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible
|
github.com/gofrs/uuid v4.0.0+incompatible
|
||||||
github.com/golang/mock v1.5.0
|
github.com/golang/mock v1.5.0
|
||||||
github.com/google/go-cmp v0.5.5
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/go-cmp v0.5.6
|
||||||
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/gosimple/slug v1.9.0
|
github.com/gosimple/slug v1.9.0
|
||||||
github.com/grafana/grafana-aws-sdk v0.7.0
|
github.com/grafana/grafana-aws-sdk v0.7.0
|
||||||
github.com/grafana/grafana-plugin-sdk-go v0.110.0
|
github.com/grafana/grafana-plugin-sdk-go v0.111.0
|
||||||
github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103
|
github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||||
github.com/hashicorp/go-hclog v0.16.0
|
github.com/hashicorp/go-hclog v0.16.1
|
||||||
github.com/hashicorp/go-plugin v1.4.0
|
github.com/hashicorp/go-plugin v1.4.2
|
||||||
github.com/hashicorp/go-version v1.3.0
|
github.com/hashicorp/go-version v1.3.0
|
||||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
|
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.2.3
|
github.com/influxdata/influxdb-client-go/v2 v2.2.3
|
||||||
@@ -76,10 +77,10 @@ require (
|
|||||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/alertmanager v0.22.2
|
github.com/prometheus/alertmanager v0.22.2
|
||||||
github.com/prometheus/client_golang v1.10.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/prometheus/client_model v0.2.0
|
github.com/prometheus/client_model v0.2.0
|
||||||
github.com/prometheus/common v0.27.0
|
github.com/prometheus/common v0.29.0
|
||||||
github.com/prometheus/prometheus v1.8.2-0.20210430082741-2a4b8e12bbf2
|
github.com/prometheus/prometheus v1.8.2-0.20210621150501-ff58416a0b02
|
||||||
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
|
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/russellhaering/goxmldsig v1.1.0
|
github.com/russellhaering/goxmldsig v1.1.0
|
||||||
@@ -88,25 +89,25 @@ require (
|
|||||||
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
|
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
|
||||||
github.com/timberio/go-datemath v0.1.1-0.20200323150745-74ddef604fff
|
github.com/timberio/go-datemath v0.1.1-0.20200323150745-74ddef604fff
|
||||||
github.com/ua-parser/uap-go v0.0.0-20190826212731-daf92ba38329
|
github.com/ua-parser/uap-go v0.0.0-20190826212731-daf92ba38329
|
||||||
github.com/uber/jaeger-client-go v2.27.0+incompatible
|
github.com/uber/jaeger-client-go v2.29.1+incompatible
|
||||||
github.com/unknwon/com v1.0.1
|
github.com/unknwon/com v1.0.1
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
github.com/weaveworks/common v0.0.0-20210419092856-009d1eebd624
|
github.com/weaveworks/common v0.0.0-20210419092856-009d1eebd624
|
||||||
github.com/xorcare/pointer v1.1.0
|
github.com/xorcare/pointer v1.1.0
|
||||||
github.com/yudai/gojsondiff v1.0.0
|
github.com/yudai/gojsondiff v1.0.0
|
||||||
go.opentelemetry.io/collector v0.27.0
|
go.opentelemetry.io/collector v0.31.0
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
|
go.opentelemetry.io/collector/model v0.31.0 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||||
golang.org/x/exp v0.0.0-20210220032938-85be41e4509f // indirect
|
golang.org/x/exp v0.0.0-20210220032938-85be41e4509f // indirect
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1 // indirect
|
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
golang.org/x/tools v0.1.3
|
||||||
golang.org/x/tools v0.1.0
|
|
||||||
gonum.org/v1/gonum v0.9.1
|
gonum.org/v1/gonum v0.9.1
|
||||||
google.golang.org/api v0.45.0
|
google.golang.org/api v0.48.0
|
||||||
google.golang.org/grpc v1.37.1
|
google.golang.org/grpc v1.39.0
|
||||||
google.golang.org/protobuf v1.26.0
|
google.golang.org/protobuf v1.27.1
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0
|
gopkg.in/ini.v1 v1.62.0
|
||||||
gopkg.in/ldap.v3 v3.1.0
|
gopkg.in/ldap.v3 v3.1.0
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"packages": ["packages/*"],
|
"packages": [
|
||||||
"version": "8.1.0-pre"
|
"packages/*"
|
||||||
|
],
|
||||||
|
"version": "8.1.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "grafana",
|
"name": "grafana",
|
||||||
"version": "8.1.0-pre",
|
"version": "8.1.0",
|
||||||
"repository": "github:grafana/grafana",
|
"repository": "github:grafana/grafana",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
|
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/data",
|
"name": "@grafana/data",
|
||||||
"version": "8.1.0-pre",
|
"version": "8.1.0",
|
||||||
"description": "Grafana Data Library",
|
"description": "Grafana Data Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"typescript"
|
"typescript"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Vector, QueryResultMeta } from '../types';
|
|||||||
import { guessFieldTypeFromNameAndValue, toDataFrameDTO } from './processDataFrame';
|
import { guessFieldTypeFromNameAndValue, toDataFrameDTO } from './processDataFrame';
|
||||||
import { FunctionalVector } from '../vector/FunctionalVector';
|
import { FunctionalVector } from '../vector/FunctionalVector';
|
||||||
|
|
||||||
|
/** @public */
|
||||||
export type ValueConverter<T = any> = (val: any) => T;
|
export type ValueConverter<T = any> = (val: any) => T;
|
||||||
|
|
||||||
const NOOP: ValueConverter = (v) => v;
|
const NOOP: ValueConverter = (v) => v;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DataFrame } from '../types';
|
import { AnnotationEvent, DataFrame } from '../types';
|
||||||
import { BusEventWithPayload } from './types';
|
import { BusEventWithPayload } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,3 +34,8 @@ export class DataHoverClearEvent extends BusEventWithPayload<DataHoverPayload> {
|
|||||||
export class DataSelectEvent extends BusEventWithPayload<DataHoverPayload> {
|
export class DataSelectEvent extends BusEventWithPayload<DataHoverPayload> {
|
||||||
static type = 'data-select';
|
static type = 'data-select';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @alpha */
|
||||||
|
export class AnnotationChangeEvent extends BusEventWithPayload<Partial<AnnotationEvent>> {
|
||||||
|
static type = 'annotation-event';
|
||||||
|
}
|
||||||
|
|||||||
@@ -644,7 +644,7 @@ describe('getLinksSupplier', () => {
|
|||||||
expect(links[0]).toEqual(
|
expect(links[0]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: 'testDS',
|
title: 'testDS',
|
||||||
href: '/explore?left={"datasource":"testDS","queries":["12345"]}',
|
href: `/explore?left=${encodeURIComponent('{"datasource":"testDS","queries":["12345"]}')}`,
|
||||||
onClick: undefined,
|
onClick: undefined,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { getFrameDisplayName } from './fieldState';
|
|||||||
import { getTimeField } from '../dataframe/processDataFrame';
|
import { getTimeField } from '../dataframe/processDataFrame';
|
||||||
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
import { mapInternalLinkToExplore } from '../utils/dataLinks';
|
||||||
import { getTemplateProxyForField } from './templateProxies';
|
import { getTemplateProxyForField } from './templateProxies';
|
||||||
|
import { asHexString } from '../themes/colorManipulator';
|
||||||
|
|
||||||
interface OverrideProps {
|
interface OverrideProps {
|
||||||
match: FieldMatcher;
|
match: FieldMatcher;
|
||||||
@@ -168,16 +169,16 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
|
|||||||
range = { min, max, delta: max! - min! };
|
range = { min, max, delta: max! - min! };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
field.state!.seriesIndex = seriesIndex;
|
||||||
|
field.state!.range = range;
|
||||||
|
field.type = type;
|
||||||
|
|
||||||
// Some color modes needs series index to assign field color so we count
|
// Some color modes needs series index to assign field color so we count
|
||||||
// up series index here but ignore time fields
|
// up series index here but ignore time fields
|
||||||
if (field.type !== FieldType.time) {
|
if (field.type !== FieldType.time) {
|
||||||
seriesIndex++;
|
seriesIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
field.state!.seriesIndex = seriesIndex;
|
|
||||||
field.state!.range = range;
|
|
||||||
field.type = type;
|
|
||||||
|
|
||||||
// and set the display processor using it
|
// and set the display processor using it
|
||||||
field.display = getDisplayProcessor({
|
field.display = getDisplayProcessor({
|
||||||
field: field,
|
field: field,
|
||||||
@@ -204,11 +205,17 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is a significant optimization for streaming, where we currently re-process all values in the buffer on ech update
|
||||||
|
// via field.display(value). this can potentially be removed once we...
|
||||||
|
// 1. process data packets incrementally and/if cache the results in the streaming datafame (maybe by buffer index)
|
||||||
|
// 2. have the ability to selectively get display color or text (but not always both, which are each quite expensive)
|
||||||
|
// 3. sufficently optimize text formating and threshold color determinitation
|
||||||
function cachingDisplayProcessor(disp: DisplayProcessor, maxCacheSize = 2500): DisplayProcessor {
|
function cachingDisplayProcessor(disp: DisplayProcessor, maxCacheSize = 2500): DisplayProcessor {
|
||||||
const cache = new Map<any, DisplayValue>();
|
const cache = new Map<any, DisplayValue>();
|
||||||
|
|
||||||
return (value: any) => {
|
return (value: any) => {
|
||||||
let v = cache.get(value);
|
let v = cache.get(value);
|
||||||
|
|
||||||
if (!v) {
|
if (!v) {
|
||||||
// Don't grow too big
|
// Don't grow too big
|
||||||
if (cache.size === maxCacheSize) {
|
if (cache.size === maxCacheSize) {
|
||||||
@@ -216,8 +223,16 @@ function cachingDisplayProcessor(disp: DisplayProcessor, maxCacheSize = 2500): D
|
|||||||
}
|
}
|
||||||
|
|
||||||
v = disp(value);
|
v = disp(value);
|
||||||
|
|
||||||
|
// convert to hex6 or hex8 so downstream we can cheaply test for alpha (and set new alpha)
|
||||||
|
// via a simple length check (in colorManipulator) rather using slow parsing via tinycolor
|
||||||
|
if (v.color) {
|
||||||
|
v.color = asHexString(v.color);
|
||||||
|
}
|
||||||
|
|
||||||
cache.set(value, v);
|
cache.set(value, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ export interface MapLayerOptions<TConfig = any> {
|
|||||||
*/
|
*/
|
||||||
export interface MapLayerHandler {
|
export interface MapLayerHandler {
|
||||||
init: () => BaseLayer;
|
init: () => BaseLayer;
|
||||||
legend?: () => ReactNode;
|
|
||||||
update?: (data: PanelData) => void;
|
update?: (data: PanelData) => void;
|
||||||
|
legend?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,5 +102,5 @@ export interface MapLayerRegistryItem<TConfig = MapLayerOptions> extends Registr
|
|||||||
/**
|
/**
|
||||||
* Show custom elements in the panel edit UI
|
* Show custom elements in the panel edit UI
|
||||||
*/
|
*/
|
||||||
registerOptionsUI?: (builder: PanelOptionsEditorBuilder<TConfig>) => void;
|
registerOptionsUI?: (builder: PanelOptionsEditorBuilder<MapLayerOptions<TConfig>>) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,6 +279,10 @@ describe('utils/colorManipulator', () => {
|
|||||||
expect(alpha('hsla(0, 100%, 50%, 0.2)', 0.5)).toEqual('hsla(0, 100%, 50%, 0.5)');
|
expect(alpha('hsla(0, 100%, 50%, 0.2)', 0.5)).toEqual('hsla(0, 100%, 50%, 0.5)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts an rgb hex color with the alpha value provided', () => {
|
||||||
|
expect(alpha('#FFFFFF', 0)).toEqual('#FFFFFF00');
|
||||||
|
});
|
||||||
|
|
||||||
it('throw on invalid colors', () => {
|
it('throw on invalid colors', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
alpha('white', 0.4);
|
alpha('white', 0.4);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// https://github.com/mui-org/material-ui/blob/1b096070faf102281f8e3c4f9b2bf50acf91f412/packages/material-ui/src/styles/colorManipulator.js#L97
|
// https://github.com/mui-org/material-ui/blob/1b096070faf102281f8e3c4f9b2bf50acf91f412/packages/material-ui/src/styles/colorManipulator.js#L97
|
||||||
// MIT License Copyright (c) 2014 Call-Em-All
|
// MIT License Copyright (c) 2014 Call-Em-All
|
||||||
|
|
||||||
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a number whose value is limited to the given range.
|
* Returns a number whose value is limited to the given range.
|
||||||
* @param value The value to be clamped
|
* @param value The value to be clamped
|
||||||
@@ -66,6 +68,19 @@ export function rgbToHex(color: string) {
|
|||||||
return `#${values.map((n: number) => intToHex(n)).join('')}`;
|
return `#${values.map((n: number) => intToHex(n)).join('')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a color to hex6 format if there is no alpha, hex8 if there is.
|
||||||
|
* @param color - Hex, RGB, HSL color
|
||||||
|
* @returns A hex color string, i.e. #ff0000 or #ff0000ff
|
||||||
|
*/
|
||||||
|
export function asHexString(color: string): string {
|
||||||
|
if (color[0] === '#') {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
const tColor = tinycolor(color);
|
||||||
|
return tColor.getAlpha() === 1 ? tColor.toHexString() : tColor.toHex8String();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a color from hsl format to rgb format.
|
* Converts a color from hsl format to rgb format.
|
||||||
* @param color - HSL color values
|
* @param color - HSL color values
|
||||||
@@ -248,7 +263,12 @@ export function alpha(color: string, value: number) {
|
|||||||
color = color.substring(0, 7);
|
color = color.substring(0, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
return color + Math.round(value * 255).toString(16);
|
return (
|
||||||
|
color +
|
||||||
|
Math.round(value * 255)
|
||||||
|
.toString(16)
|
||||||
|
.padStart(2, '0')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// rgb(, hsl(
|
// rgb(, hsl(
|
||||||
else if (color[3] === '(') {
|
else if (color[3] === '(') {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { ReducerID } from '../fieldReducer';
|
|||||||
import { DataTransformerID } from './ids';
|
import { DataTransformerID } from './ids';
|
||||||
import { toDataFrame } from '../../dataframe/processDataFrame';
|
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||||
import { reduceFields, reduceTransformer } from './reduce';
|
import { reduceFields, reduceTransformer, ReduceTransformerOptions } from './reduce';
|
||||||
import { transformDataFrame } from '../transformDataFrame';
|
import { transformDataFrame } from '../transformDataFrame';
|
||||||
import { Field, FieldType } from '../../types';
|
import { DataTransformerConfig, Field, FieldType } from '../../types';
|
||||||
import { ArrayVector } from '../../vector';
|
import { ArrayVector } from '../../vector';
|
||||||
import { notTimeFieldMatcher } from '../matchers/predicates';
|
import { notTimeFieldMatcher } from '../matchers/predicates';
|
||||||
import { DataFrameView } from '../../dataframe';
|
import { DataFrameView } from '../../dataframe';
|
||||||
@@ -366,4 +366,84 @@ describe('Reducer Transformer', () => {
|
|||||||
expect(processed[0].fields).toEqual(expected);
|
expect(processed[0].fields).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reduces keeping label field', async () => {
|
||||||
|
const cfg: DataTransformerConfig<ReduceTransformerOptions> = {
|
||||||
|
id: DataTransformerID.reduce,
|
||||||
|
options: {
|
||||||
|
reducers: [ReducerID.max],
|
||||||
|
labelsToFields: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const seriesA = toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000] },
|
||||||
|
{ name: 'value', labels: { state: 'CA' }, type: FieldType.number, values: [3, 4, 5, 6] },
|
||||||
|
{ name: 'value', labels: { state: 'NY' }, type: FieldType.number, values: [3, 4, 5, 6] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesB = toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000] },
|
||||||
|
{ name: 'value', labels: { state: 'CA', country: 'USA' }, type: FieldType.number, values: [3, 4, 5, 6] },
|
||||||
|
{ name: 'value', labels: { country: 'USA' }, type: FieldType.number, values: [3, 4, 5, 6] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(transformDataFrame([cfg], [seriesA, seriesB])).toEmitValuesWith((received) => {
|
||||||
|
const processed = received[0];
|
||||||
|
|
||||||
|
expect(processed.length).toEqual(1);
|
||||||
|
expect(processed[0].fields).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"config": Object {},
|
||||||
|
"name": "Field",
|
||||||
|
"type": "string",
|
||||||
|
"values": Array [
|
||||||
|
"value",
|
||||||
|
"value",
|
||||||
|
"value",
|
||||||
|
"value",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"config": Object {},
|
||||||
|
"name": "state",
|
||||||
|
"type": "string",
|
||||||
|
"values": Array [
|
||||||
|
"CA",
|
||||||
|
"NY",
|
||||||
|
"CA",
|
||||||
|
undefined,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"config": Object {},
|
||||||
|
"name": "country",
|
||||||
|
"type": "string",
|
||||||
|
"values": Array [
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
"USA",
|
||||||
|
"USA",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"config": Object {},
|
||||||
|
"name": "Max",
|
||||||
|
"type": "number",
|
||||||
|
"values": Array [
|
||||||
|
6,
|
||||||
|
6,
|
||||||
|
6,
|
||||||
|
6,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface ReduceTransformerOptions {
|
|||||||
fields?: MatcherConfig; // Assume all fields
|
fields?: MatcherConfig; // Assume all fields
|
||||||
mode?: ReduceTransformerMode;
|
mode?: ReduceTransformerMode;
|
||||||
includeTimeField?: boolean;
|
includeTimeField?: boolean;
|
||||||
|
labelsToFields?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> = {
|
export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> = {
|
||||||
@@ -53,7 +54,7 @@ export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a row for each series
|
// Add a row for each series
|
||||||
const res = reduceSeriesToRows(data, matcher, options.reducers);
|
const res = reduceSeriesToRows(data, matcher, options.reducers, options.labelsToFields);
|
||||||
return res ? [res] : [];
|
return res ? [res] : [];
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
@@ -65,77 +66,109 @@ export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> =
|
|||||||
export function reduceSeriesToRows(
|
export function reduceSeriesToRows(
|
||||||
data: DataFrame[],
|
data: DataFrame[],
|
||||||
matcher: FieldMatcher,
|
matcher: FieldMatcher,
|
||||||
reducerId: ReducerID[]
|
reducerId: ReducerID[],
|
||||||
|
labelsToFields?: boolean
|
||||||
): DataFrame | undefined {
|
): DataFrame | undefined {
|
||||||
const calculators = fieldReducers.list(reducerId);
|
const calculators = fieldReducers.list(reducerId);
|
||||||
const reducers = calculators.map((c) => c.id);
|
const reducers = calculators.map((c) => c.id);
|
||||||
const processed: DataFrame[] = [];
|
const processed: DataFrame[] = [];
|
||||||
|
const distinctLabels = labelsToFields ? getDistinctLabelKeys(data) : [];
|
||||||
|
|
||||||
for (const series of data) {
|
for (const series of data) {
|
||||||
const values: ArrayVector[] = [];
|
const source = series.fields.filter((f) => matcher(f, series, data));
|
||||||
const fields: Field[] = [];
|
|
||||||
const byId: KeyValue<ArrayVector> = {};
|
|
||||||
|
|
||||||
values.push(new ArrayVector()); // The name
|
const size = source.length;
|
||||||
|
const fields: Field[] = [];
|
||||||
|
const names = new ArrayVector<string>(new Array(size));
|
||||||
fields.push({
|
fields.push({
|
||||||
name: 'Field',
|
name: 'Field',
|
||||||
type: FieldType.string,
|
type: FieldType.string,
|
||||||
values: values[0],
|
values: names,
|
||||||
config: {},
|
config: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const info of calculators) {
|
const labels: KeyValue<ArrayVector> = {};
|
||||||
const vals = new ArrayVector();
|
if (labelsToFields) {
|
||||||
byId[info.id] = vals;
|
for (const key of distinctLabels) {
|
||||||
values.push(vals);
|
labels[key] = new ArrayVector<string>(new Array(size));
|
||||||
|
fields.push({
|
||||||
|
name: key,
|
||||||
|
type: FieldType.string,
|
||||||
|
values: labels[key],
|
||||||
|
config: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcs: KeyValue<ArrayVector> = {};
|
||||||
|
for (const info of calculators) {
|
||||||
|
calcs[info.id] = new ArrayVector(new Array(size));
|
||||||
fields.push({
|
fields.push({
|
||||||
name: info.name,
|
name: info.name,
|
||||||
type: FieldType.other, // UNKNOWN until after we call the functions
|
type: FieldType.other, // UNKNOWN until after we call the functions
|
||||||
values: values[values.length - 1],
|
values: calcs[info.id],
|
||||||
config: {},
|
config: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < series.fields.length; i++) {
|
for (let i = 0; i < source.length; i++) {
|
||||||
const field = series.fields[i];
|
const field = source[i];
|
||||||
|
const results = reduceField({
|
||||||
|
field,
|
||||||
|
reducers,
|
||||||
|
});
|
||||||
|
|
||||||
if (matcher(field, series, data)) {
|
if (labelsToFields) {
|
||||||
const results = reduceField({
|
names.buffer[i] = field.name;
|
||||||
field,
|
if (field.labels) {
|
||||||
reducers,
|
for (const key of Object.keys(field.labels)) {
|
||||||
});
|
labels[key].set(i, field.labels[key]);
|
||||||
|
}
|
||||||
// Update the name list
|
|
||||||
const fieldName = getFieldDisplayName(field, series, data);
|
|
||||||
|
|
||||||
values[0].buffer.push(fieldName);
|
|
||||||
|
|
||||||
for (const info of calculators) {
|
|
||||||
const v = results[info.id];
|
|
||||||
byId[info.id].buffer.push(v);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
names.buffer[i] = getFieldDisplayName(field, series, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const info of calculators) {
|
||||||
|
const v = results[info.id];
|
||||||
|
calcs[info.id].buffer[i] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For reduced fields, we don't know the type until we see the value
|
||||||
for (const f of fields) {
|
for (const f of fields) {
|
||||||
const t = guessFieldTypeForField(f);
|
if (f.type === FieldType.other) {
|
||||||
|
const t = guessFieldTypeForField(f);
|
||||||
if (t) {
|
if (t) {
|
||||||
f.type = t;
|
f.type = t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processed.push({
|
processed.push({
|
||||||
...series, // Same properties, different fields
|
...series, // Same properties, different fields
|
||||||
fields,
|
fields,
|
||||||
length: values[0].length,
|
length: size,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return mergeResults(processed);
|
return mergeResults(processed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDistinctLabelKeys(frames: DataFrame[]): string[] {
|
||||||
|
const keys = new Set<string>();
|
||||||
|
for (const frame of frames) {
|
||||||
|
for (const field of frame.fields) {
|
||||||
|
if (field.labels) {
|
||||||
|
for (const k of Object.keys(field.labels)) {
|
||||||
|
keys.add(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...keys];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal only exported for testing
|
* @internal only exported for testing
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -128,7 +128,6 @@ export interface QueryResultBase {
|
|||||||
export interface Labels {
|
export interface Labels {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Column {
|
export interface Column {
|
||||||
text: string; // For a Column, the 'text' is the field name
|
text: string; // For a Column, the 'text' is the field name
|
||||||
filterable?: boolean;
|
filterable?: boolean;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export enum PluginSignatureType {
|
|||||||
commercial = 'commercial',
|
commercial = 'commercial',
|
||||||
community = 'community',
|
community = 'community',
|
||||||
private = 'private',
|
private = 'private',
|
||||||
|
core = 'core',
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Describes error code returned from Grafana plugins API call */
|
/** Describes error code returned from Grafana plugins API call */
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ describe('mapInternalLinkToExplore', () => {
|
|||||||
expect(link).toEqual(
|
expect(link).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: 'dsName',
|
title: 'dsName',
|
||||||
href: '/explore?left={"datasource":"dsName","queries":[{"query":"12344"}]}',
|
href: `/explore?left=${encodeURIComponent('{"datasource":"dsName","queries":[{"query":"12344"}]}')}`,
|
||||||
onClick: undefined,
|
onClick: undefined,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -67,11 +67,13 @@ export function mapInternalLinkToExplore(options: LinkToExploreOptions): LinkMod
|
|||||||
*/
|
*/
|
||||||
function generateInternalHref<T extends DataQuery = any>(datasourceName: string, query: T, range: TimeRange): string {
|
function generateInternalHref<T extends DataQuery = any>(datasourceName: string, query: T, range: TimeRange): string {
|
||||||
return locationUtil.assureBaseUrl(
|
return locationUtil.assureBaseUrl(
|
||||||
`/explore?left=${serializeStateToUrlParam({
|
`/explore?left=${encodeURIComponent(
|
||||||
range: range.raw,
|
serializeStateToUrlParam({
|
||||||
datasource: datasourceName,
|
range: range.raw,
|
||||||
queries: [query],
|
datasource: datasourceName,
|
||||||
})}`
|
queries: [query],
|
||||||
|
})
|
||||||
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { vectorToArray } from './vectorToArray';
|
import { vectorToArray } from './vectorToArray';
|
||||||
import { Vector } from '../types';
|
import { Vector } from '../types';
|
||||||
|
|
||||||
|
/** @public */
|
||||||
export abstract class FunctionalVector<T = any> implements Vector<T>, Iterable<T> {
|
export abstract class FunctionalVector<T = any> implements Vector<T>, Iterable<T> {
|
||||||
abstract get length(): number;
|
abstract get length(): number;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/e2e-selectors",
|
"name": "@grafana/e2e-selectors",
|
||||||
"version": "8.1.0-pre",
|
"version": "8.1.0",
|
||||||
"description": "Grafana End-to-End Test Selectors Library",
|
"description": "Grafana End-to-End Test Selectors Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cli",
|
"cli",
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
// prefix your selector string with 'data-test-id' so that when create the selectors we know to search for it on the right attribute
|
// prefix your selector string with 'data-test-id' so that when create the selectors we know to search for it on the right attribute
|
||||||
export const Components = {
|
export const Components = {
|
||||||
TimePicker: {
|
TimePicker: {
|
||||||
openButton: 'TimePicker Open Button',
|
openButton: 'data-testid TimePicker Open Button',
|
||||||
fromField: 'TimePicker from field',
|
fromField: 'TimePicker from field',
|
||||||
toField: 'TimePicker to field',
|
toField: 'TimePicker to field',
|
||||||
applyTimeRange: 'TimePicker submit button',
|
applyTimeRange: 'data-testid TimePicker submit button',
|
||||||
calendar: 'TimePicker calendar',
|
calendar: 'TimePicker calendar',
|
||||||
|
absoluteTimeRangeTitle: 'data-testid-absolute-time-range-narrow',
|
||||||
},
|
},
|
||||||
DataSource: {
|
DataSource: {
|
||||||
TestData: {
|
TestData: {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/e2e",
|
"name": "@grafana/e2e",
|
||||||
"version": "8.1.0-pre",
|
"version": "8.1.0",
|
||||||
"description": "Grafana End-to-End Test Library",
|
"description": "Grafana End-to-End Test Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cli",
|
"cli",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cypress/webpack-preprocessor": "5.9.0",
|
"@cypress/webpack-preprocessor": "5.9.0",
|
||||||
"@grafana/e2e-selectors": "8.1.0-pre",
|
"@grafana/e2e-selectors": "8.1.0",
|
||||||
"@grafana/tsconfig": "^1.0.0-rc1",
|
"@grafana/tsconfig": "^1.0.0-rc1",
|
||||||
"@mochajs/json-file-reporter": "^1.2.0",
|
"@mochajs/json-file-reporter": "^1.2.0",
|
||||||
"blink-diff": "1.0.13",
|
"blink-diff": "1.0.13",
|
||||||
|
|||||||
@@ -157,8 +157,9 @@ const addVariable = (config: PartialAddVariableConfig, isFirst: boolean): AddVar
|
|||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelect()
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelect()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.within(() => {
|
.within(() => {
|
||||||
e2e.components.Select.singleValue().should('have.text', 'Query').click().type(`${type}{enter}`);
|
e2e.components.Select.singleValue().should('have.text', 'Query').click();
|
||||||
});
|
});
|
||||||
|
e2e.components.Select.option().should('be.visible').contains(type).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (label) {
|
if (label) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export interface TimeRangeConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const setTimeRange = ({ from, to, zone }: TimeRangeConfig) => {
|
export const setTimeRange = ({ from, to, zone }: TimeRangeConfig) => {
|
||||||
e2e().get('[aria-label="TimePicker Open Button"]').click();
|
e2e.components.TimePicker.openButton().click();
|
||||||
|
|
||||||
if (zone) {
|
if (zone) {
|
||||||
e2e().contains('button', 'Change time zone').click();
|
e2e().contains('button', 'Change time zone').click();
|
||||||
@@ -21,9 +21,10 @@ export const setTimeRange = ({ from, to, zone }: TimeRangeConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For smaller screens
|
// For smaller screens
|
||||||
e2e().get('[aria-label="TimePicker absolute time range"]').click();
|
e2e.components.TimePicker.absoluteTimeRangeTitle().click();
|
||||||
|
|
||||||
e2e().get('[aria-label="TimePicker from field"]').clear().type(from);
|
e2e.components.TimePicker.fromField().clear().type(from);
|
||||||
e2e().get('[aria-label="TimePicker to field"]').clear().type(to);
|
e2e.components.TimePicker.toField().clear().type(to);
|
||||||
e2e().get('[aria-label="TimePicker submit button"]').click();
|
|
||||||
|
e2e.components.TimePicker.applyTimeRange().click();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/runtime",
|
"name": "@grafana/runtime",
|
||||||
"version": "8.1.0-pre",
|
"version": "8.1.0",
|
||||||
"description": "Grafana Runtime Library",
|
"description": "Grafana Runtime Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"grafana",
|
"grafana",
|
||||||
@@ -22,9 +22,9 @@
|
|||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grafana/data": "8.1.0-pre",
|
"@grafana/data": "8.1.0",
|
||||||
"@grafana/e2e-selectors": "8.1.0-pre",
|
"@grafana/e2e-selectors": "8.1.0",
|
||||||
"@grafana/ui": "8.1.0-pre",
|
"@grafana/ui": "8.1.0",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"systemjs": "0.20.19",
|
"systemjs": "0.20.19",
|
||||||
"systemjs-plugin-css": "0.1.37"
|
"systemjs-plugin-css": "0.1.37"
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ export class DataSourcePicker extends PureComponent<DataSourcePickerProps, DataS
|
|||||||
return (
|
return (
|
||||||
<div aria-label={selectors.components.DataSourcePicker.container}>
|
<div aria-label={selectors.components.DataSourcePicker.container}>
|
||||||
<Select
|
<Select
|
||||||
|
menuShouldPortal
|
||||||
className={styles.select}
|
className={styles.select}
|
||||||
isMulti={false}
|
isMulti={false}
|
||||||
isClearable={false}
|
isClearable={false}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/toolkit",
|
"name": "@grafana/toolkit",
|
||||||
"version": "8.1.0-pre",
|
"version": "8.1.0",
|
||||||
"description": "Grafana Toolkit",
|
"description": "Grafana Toolkit",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"grafana",
|
"grafana",
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "7.13.14",
|
"@babel/core": "7.13.14",
|
||||||
"@babel/preset-env": "7.13.12",
|
"@babel/preset-env": "7.13.12",
|
||||||
"@grafana/data": "8.1.0-pre",
|
"@grafana/data": "8.1.0",
|
||||||
"@grafana/eslint-config": "2.5.0",
|
"@grafana/eslint-config": "2.5.0",
|
||||||
"@grafana/tsconfig": "^1.0.0-rc1",
|
"@grafana/tsconfig": "^1.0.0-rc1",
|
||||||
"@grafana/ui": "8.1.0-pre",
|
"@grafana/ui": "8.1.0",
|
||||||
"@types/command-exists": "^1.2.0",
|
"@types/command-exists": "^1.2.0",
|
||||||
"@types/expect-puppeteer": "3.3.1",
|
"@types/expect-puppeteer": "3.3.1",
|
||||||
"@types/fs-extra": "^8.1.0",
|
"@types/fs-extra": "^8.1.0",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ module.exports = {
|
|||||||
backgrounds: false,
|
backgrounds: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'@storybook/addon-a11y',
|
||||||
'@storybook/addon-knobs',
|
'@storybook/addon-knobs',
|
||||||
'@storybook/addon-storysource',
|
'@storybook/addon-storysource',
|
||||||
'storybook-dark-mode',
|
'storybook-dark-mode',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"author": "Grafana Labs",
|
"author": "Grafana Labs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"name": "@grafana/ui",
|
"name": "@grafana/ui",
|
||||||
"version": "8.1.0-pre",
|
"version": "8.1.0",
|
||||||
"description": "Grafana Components Library",
|
"description": "Grafana Components Library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"grafana",
|
"grafana",
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
"@emotion/css": "11.1.3",
|
"@emotion/css": "11.1.3",
|
||||||
"@emotion/react": "11.1.5",
|
"@emotion/react": "11.1.5",
|
||||||
"@grafana/aws-sdk": "0.0.3",
|
"@grafana/aws-sdk": "0.0.3",
|
||||||
"@grafana/data": "8.1.0-pre",
|
"@grafana/data": "8.1.0",
|
||||||
"@grafana/e2e-selectors": "8.1.0-pre",
|
"@grafana/e2e-selectors": "8.1.0",
|
||||||
"@grafana/slate-react": "0.22.10-grafana",
|
"@grafana/slate-react": "0.22.10-grafana",
|
||||||
"@grafana/tsconfig": "^1.0.0-rc1",
|
"@grafana/tsconfig": "^1.0.0-rc1",
|
||||||
"@monaco-editor/react": "4.1.1",
|
"@monaco-editor/react": "4.1.1",
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
"react-colorful": "5.1.2",
|
"react-colorful": "5.1.2",
|
||||||
"react-custom-scrollbars": "4.2.1",
|
"react-custom-scrollbars": "4.2.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
|
"react-dropzone": "11.3.4",
|
||||||
"react-highlight-words": "0.16.0",
|
"react-highlight-words": "0.16.0",
|
||||||
"react-hook-form": "7.5.3",
|
"react-hook-form": "7.5.3",
|
||||||
"react-inlinesvg": "2.3.0",
|
"react-inlinesvg": "2.3.0",
|
||||||
@@ -79,11 +80,12 @@
|
|||||||
"@rollup/plugin-commonjs": "16.0.0",
|
"@rollup/plugin-commonjs": "16.0.0",
|
||||||
"@rollup/plugin-image": "2.0.5",
|
"@rollup/plugin-image": "2.0.5",
|
||||||
"@rollup/plugin-node-resolve": "10.0.0",
|
"@rollup/plugin-node-resolve": "10.0.0",
|
||||||
"@storybook/addon-essentials": "6.3.0",
|
"@storybook/addon-a11y": "6.3.5",
|
||||||
|
"@storybook/addon-essentials": "6.3.5",
|
||||||
"@storybook/addon-knobs": "6.3.0",
|
"@storybook/addon-knobs": "6.3.0",
|
||||||
"@storybook/addon-storysource": "6.3.0",
|
"@storybook/addon-storysource": "6.3.5",
|
||||||
"@storybook/react": "6.3.0",
|
"@storybook/react": "6.3.5",
|
||||||
"@storybook/theming": "6.3.0",
|
"@storybook/theming": "6.3.5",
|
||||||
"@testing-library/jest-dom": "5.11.9",
|
"@testing-library/jest-dom": "5.11.9",
|
||||||
"@types/classnames": "2.2.7",
|
"@types/classnames": "2.2.7",
|
||||||
"@types/common-tags": "^1.8.0",
|
"@types/common-tags": "^1.8.0",
|
||||||
|
|||||||
@@ -69,7 +69,13 @@ export const ToolbarButton = forwardRef<HTMLButtonElement, Props>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<button ref={ref} className={buttonStyles} aria-label={getButttonAriaLabel(ariaLabel, tooltip)} {...rest}>
|
<button
|
||||||
|
ref={ref}
|
||||||
|
className={buttonStyles}
|
||||||
|
aria-label={getButttonAriaLabel(ariaLabel, tooltip)}
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
{renderIcon(icon)}
|
{renderIcon(icon)}
|
||||||
{imgSrc && <img className={styles.img} src={imgSrc} />}
|
{imgSrc && <img className={styles.img} src={imgSrc} />}
|
||||||
{children && !iconOnly && <div className={contentStyles}>{children}</div>}
|
{children && !iconOnly && <div className={contentStyles}>{children}</div>}
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ export class Cascader extends React.PureComponent<CascaderProps, CascaderState>
|
|||||||
<div>
|
<div>
|
||||||
{isSearching ? (
|
{isSearching ? (
|
||||||
<Select
|
<Select
|
||||||
|
menuShouldPortal
|
||||||
allowCustomValue={allowCustomValue}
|
allowCustomValue={allowCustomValue}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoFocus={!focusCascade}
|
autoFocus={!focusCascade}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { mount, ReactWrapper } from 'enzyme';
|
import { mount, ReactWrapper } from 'enzyme';
|
||||||
import { ColorPickerPopover } from './ColorPickerPopover';
|
import { ColorPickerPopover } from './ColorPickerPopover';
|
||||||
import { ColorSwatch } from './ColorSwatch';
|
import { ColorSwatch } from './ColorSwatch';
|
||||||
import { createTheme, getColorForTheme } from '@grafana/data';
|
import { createTheme } from '@grafana/data';
|
||||||
|
|
||||||
describe('ColorPickerPopover', () => {
|
describe('ColorPickerPopover', () => {
|
||||||
const theme = createTheme();
|
const theme = createTheme();
|
||||||
@@ -35,7 +35,7 @@ describe('ColorPickerPopover', () => {
|
|||||||
basicBlueSwatch.simulate('click');
|
basicBlueSwatch.simulate('click');
|
||||||
|
|
||||||
expect(onChangeSpy).toBeCalledTimes(1);
|
expect(onChangeSpy).toBeCalledTimes(1);
|
||||||
expect(onChangeSpy).toBeCalledWith(getColorForTheme('green', theme.v1));
|
expect(onChangeSpy).toBeCalledWith(theme.visualization.getColorByName('green'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass color name to onChange prop when named colors enabled', () => {
|
it('should pass color name to onChange prop when named colors enabled', () => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import SpectrumPalette from './SpectrumPalette';
|
|||||||
import { Themeable2 } from '../../types/theme';
|
import { Themeable2 } from '../../types/theme';
|
||||||
import { warnAboutColorPickerPropsDeprecation } from './warnAboutColorPickerPropsDeprecation';
|
import { warnAboutColorPickerPropsDeprecation } from './warnAboutColorPickerPropsDeprecation';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
|
||||||
import { stylesFactory, withTheme2 } from '../../themes';
|
import { stylesFactory, withTheme2 } from '../../themes';
|
||||||
|
|
||||||
export type ColorPickerChangeHandler = (color: string) => void;
|
export type ColorPickerChangeHandler = (color: string) => void;
|
||||||
@@ -55,11 +55,10 @@ class UnThemedColorPickerPopover<T extends CustomPickersDescriptor> extends Reac
|
|||||||
handleChange = (color: any) => {
|
handleChange = (color: any) => {
|
||||||
const { onColorChange, onChange, enableNamedColors, theme } = this.props;
|
const { onColorChange, onChange, enableNamedColors, theme } = this.props;
|
||||||
const changeHandler = onColorChange || onChange;
|
const changeHandler = onColorChange || onChange;
|
||||||
|
|
||||||
if (enableNamedColors) {
|
if (enableNamedColors) {
|
||||||
return changeHandler(color);
|
return changeHandler(color);
|
||||||
}
|
}
|
||||||
changeHandler(theme.visualization.getColorByName(color));
|
changeHandler(colorManipulator.asHexString(theme.visualization.getColorByName(color)));
|
||||||
};
|
};
|
||||||
|
|
||||||
onTabChange = (tab: PickerType | keyof T) => {
|
onTabChange = (tab: PickerType | keyof T) => {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import React, { useMemo, useState } from 'react';
|
|||||||
import { RgbaStringColorPicker } from 'react-colorful';
|
import { RgbaStringColorPicker } from 'react-colorful';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import ColorInput from './ColorInput';
|
import ColorInput from './ColorInput';
|
||||||
import { GrafanaTheme, getColorForTheme } from '@grafana/data';
|
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { useStyles, useTheme2 } from '../../themes';
|
import { useStyles2, useTheme2 } from '../../themes';
|
||||||
import { useThrottleFn } from 'react-use';
|
import { useThrottleFn } from 'react-use';
|
||||||
|
|
||||||
export interface SpectrumPaletteProps {
|
export interface SpectrumPaletteProps {
|
||||||
@@ -15,26 +15,33 @@ export interface SpectrumPaletteProps {
|
|||||||
|
|
||||||
const SpectrumPalette: React.FunctionComponent<SpectrumPaletteProps> = ({ color, onChange }) => {
|
const SpectrumPalette: React.FunctionComponent<SpectrumPaletteProps> = ({ color, onChange }) => {
|
||||||
const [currentColor, setColor] = useState(color);
|
const [currentColor, setColor] = useState(color);
|
||||||
useThrottleFn(onChange, 500, [currentColor]);
|
|
||||||
|
useThrottleFn(
|
||||||
|
(c) => {
|
||||||
|
onChange(colorManipulator.asHexString(theme.visualization.getColorByName(c)));
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
[currentColor]
|
||||||
|
);
|
||||||
|
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = useStyles(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const rgbaString = useMemo(() => {
|
const rgbaString = useMemo(() => {
|
||||||
return currentColor.startsWith('rgba')
|
return currentColor.startsWith('rgba')
|
||||||
? currentColor
|
? currentColor
|
||||||
: tinycolor(getColorForTheme(currentColor, theme.v1)).toRgbString();
|
: tinycolor(theme.visualization.getColorByName(color)).toRgbString();
|
||||||
}, [currentColor, theme]);
|
}, [currentColor, theme, color]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<RgbaStringColorPicker className={cx(styles.root)} color={rgbaString} onChange={setColor} />
|
<RgbaStringColorPicker className={cx(styles.root)} color={rgbaString} onChange={setColor} />
|
||||||
<ColorInput theme={theme} color={currentColor} onChange={setColor} className={styles.colorInput} />
|
<ColorInput theme={theme} color={rgbaString} onChange={setColor} className={styles.colorInput} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
wrapper: css`
|
wrapper: css`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
`,
|
`,
|
||||||
@@ -45,24 +52,24 @@ const getStyles = (theme: GrafanaTheme) => ({
|
|||||||
|
|
||||||
.react-colorful {
|
.react-colorful {
|
||||||
&__saturation {
|
&__saturation {
|
||||||
border-radius: ${theme.border.radius.sm} ${theme.border.radius.sm} 0 0;
|
border-radius: ${theme.v1.border.radius.sm} ${theme.v1.border.radius.sm} 0 0;
|
||||||
}
|
}
|
||||||
&__alpha {
|
&__alpha {
|
||||||
border-radius: 0 0 ${theme.border.radius.sm} ${theme.border.radius.sm};
|
border-radius: 0 0 ${theme.v1.border.radius.sm} ${theme.v1.border.radius.sm};
|
||||||
}
|
}
|
||||||
&__alpha,
|
&__alpha,
|
||||||
&__hue {
|
&__hue {
|
||||||
height: ${theme.spacing.md};
|
height: ${theme.spacing(2)};
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
&__pointer {
|
&__pointer {
|
||||||
height: ${theme.spacing.md};
|
height: ${theme.spacing(2)};
|
||||||
width: ${theme.spacing.md};
|
width: ${theme.spacing(2)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
colorInput: css`
|
colorInput: css`
|
||||||
margin-top: ${theme.spacing.md};
|
margin-top: ${theme.spacing(2)};
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,14 @@ const HttpAccessHelp = () => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
||||||
const { defaultUrl, dataSourceConfig, onChange, showAccessOptions, sigV4AuthToggleEnabled } = props;
|
const {
|
||||||
|
defaultUrl,
|
||||||
|
dataSourceConfig,
|
||||||
|
onChange,
|
||||||
|
showAccessOptions,
|
||||||
|
sigV4AuthToggleEnabled,
|
||||||
|
azureAuthSettings,
|
||||||
|
} = props;
|
||||||
let urlTooltip;
|
let urlTooltip;
|
||||||
const [isAccessHelpVisible, setIsAccessHelpVisible] = useState(false);
|
const [isAccessHelpVisible, setIsAccessHelpVisible] = useState(false);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -93,6 +100,7 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
|||||||
|
|
||||||
const accessSelect = (
|
const accessSelect = (
|
||||||
<Select
|
<Select
|
||||||
|
menuShouldPortal
|
||||||
width={20}
|
width={20}
|
||||||
options={ACCESS_OPTIONS}
|
options={ACCESS_OPTIONS}
|
||||||
value={ACCESS_OPTIONS.filter((o) => o.value === dataSourceConfig.access)[0] || DEFAULT_ACCESS_OPTION}
|
value={ACCESS_OPTIONS.filter((o) => o.value === dataSourceConfig.access)[0] || DEFAULT_ACCESS_OPTION}
|
||||||
@@ -207,6 +215,22 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{azureAuthSettings?.azureAuthEnabled && (
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<Switch
|
||||||
|
label="Azure Authentication"
|
||||||
|
labelClass="width-13"
|
||||||
|
checked={dataSourceConfig.jsonData.azureAuth || false}
|
||||||
|
onChange={(event) => {
|
||||||
|
onSettingsChange({
|
||||||
|
jsonData: { ...dataSourceConfig.jsonData, azureAuth: event!.currentTarget.checked },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
tooltip="Use Azure authentication for Azure endpoint."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{sigV4AuthToggleEnabled && (
|
{sigV4AuthToggleEnabled && (
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline">
|
||||||
<Switch
|
<Switch
|
||||||
@@ -238,6 +262,12 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{azureAuthSettings?.azureAuthEnabled &&
|
||||||
|
azureAuthSettings?.azureSettingsUI &&
|
||||||
|
dataSourceConfig.jsonData.azureAuth && (
|
||||||
|
<azureAuthSettings.azureSettingsUI dataSourceConfig={dataSourceConfig} onChange={onChange} />
|
||||||
|
)}
|
||||||
|
|
||||||
{dataSourceConfig.jsonData.sigV4Auth && <SigV4AuthSettings {...props} />}
|
{dataSourceConfig.jsonData.sigV4Auth && <SigV4AuthSettings {...props} />}
|
||||||
|
|
||||||
{(dataSourceConfig.jsonData.tlsAuth || dataSourceConfig.jsonData.tlsAuthWithCACert) && (
|
{(dataSourceConfig.jsonData.tlsAuth || dataSourceConfig.jsonData.tlsAuthWithCACert) && (
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
import { DataSourceSettings } from '@grafana/data';
|
import { DataSourceSettings } from '@grafana/data';
|
||||||
|
|
||||||
|
export interface AzureAuthSettings {
|
||||||
|
azureAuthEnabled: boolean;
|
||||||
|
azureSettingsUI?: React.ComponentType<HttpSettingsBaseProps>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HttpSettingsBaseProps {
|
export interface HttpSettingsBaseProps {
|
||||||
/** The configuration object of the data source */
|
/** The configuration object of the data source */
|
||||||
dataSourceConfig: DataSourceSettings<any, any>;
|
dataSourceConfig: DataSourceSettings<any, any>;
|
||||||
@@ -14,4 +20,6 @@ export interface HttpSettingsProps extends HttpSettingsBaseProps {
|
|||||||
showAccessOptions?: boolean;
|
showAccessOptions?: boolean;
|
||||||
/** Show the SigV4 auth toggle option */
|
/** Show the SigV4 auth toggle option */
|
||||||
sigV4AuthToggleEnabled?: boolean;
|
sigV4AuthToggleEnabled?: boolean;
|
||||||
|
/** Azure authentication settings **/
|
||||||
|
azureAuthSettings?: AzureAuthSettings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { ChangeEvent } from 'react';
|
import React, { ChangeEvent } from 'react';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { dateTimeFormat } from '@grafana/data';
|
import { dateTime, dateTimeFormat } from '@grafana/data';
|
||||||
import { DatePicker } from '../DatePicker/DatePicker';
|
import { DatePicker } from '../DatePicker/DatePicker';
|
||||||
import { Props as InputProps, Input } from '../../Input/Input';
|
import { Props as InputProps, Input } from '../../Input/Input';
|
||||||
import { useStyles } from '../../../themes';
|
import { useStyles } from '../../../themes';
|
||||||
@@ -46,7 +46,7 @@ export const DatePickerWithInput = ({
|
|||||||
/>
|
/>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
isOpen={open}
|
isOpen={open}
|
||||||
value={value && typeof value !== 'string' ? value : new Date()}
|
value={value && typeof value !== 'string' ? value : dateTime().toDate()}
|
||||||
onChange={(ev) => {
|
onChange={(ev) => {
|
||||||
onChange(ev);
|
onChange(ev);
|
||||||
if (closeOnSelect) {
|
if (closeOnSelect) {
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, disabled = false) => {
|
|||||||
inputStyles.suffix,
|
inputStyles.suffix,
|
||||||
css`
|
css`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
margin-left: ${theme.v1.spacing.xs};
|
margin-left: ${theme.v1.spacing.xs};
|
||||||
`
|
`
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ describe('TimePicker', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(container.queryByLabelText(/timepicker open button/i)).toBeInTheDocument();
|
expect(container.queryByLabelText(/Time range picker/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
import { Themeable } from '../../types';
|
import { Themeable } from '../../types';
|
||||||
import { otherOptions, quickOptions } from './rangeOptions';
|
import { otherOptions, quickOptions } from './rangeOptions';
|
||||||
import { ButtonGroup, ToolbarButton } from '../Button';
|
import { ButtonGroup, ToolbarButton } from '../Button';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface TimeRangePickerProps extends Themeable {
|
export interface TimeRangePickerProps extends Themeable {
|
||||||
@@ -89,11 +90,21 @@ export class UnthemedTimeRangePicker extends PureComponent<TimeRangePickerProps,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonGroup className={styles.container}>
|
<ButtonGroup className={styles.container}>
|
||||||
{hasAbsolute && <ToolbarButton variant={variant} onClick={onMoveBackward} icon="angle-left" narrow />}
|
{hasAbsolute && (
|
||||||
|
<ToolbarButton
|
||||||
|
aria-label="Move time range backwards"
|
||||||
|
variant={variant}
|
||||||
|
onClick={onMoveBackward}
|
||||||
|
icon="angle-left"
|
||||||
|
narrow
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Tooltip content={<TimePickerTooltip timeRange={value} timeZone={timeZone} />} placement="bottom">
|
<Tooltip content={<TimePickerTooltip timeRange={value} timeZone={timeZone} />} placement="bottom">
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
aria-label="TimePicker Open Button"
|
data-testid={selectors.components.TimePicker.openButton}
|
||||||
|
aria-label={`Time range picker with current time range ${formattedRange(value, timeZone)} selected`}
|
||||||
|
aria-controls="TimePickerContent"
|
||||||
onClick={this.onOpen}
|
onClick={this.onOpen}
|
||||||
icon="clock-nine"
|
icon="clock-nine"
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
@@ -120,10 +131,18 @@ export class UnthemedTimeRangePicker extends PureComponent<TimeRangePickerProps,
|
|||||||
|
|
||||||
{timeSyncButton}
|
{timeSyncButton}
|
||||||
|
|
||||||
{hasAbsolute && <ToolbarButton onClick={onMoveForward} icon="angle-right" narrow variant={variant} />}
|
{hasAbsolute && (
|
||||||
|
<ToolbarButton
|
||||||
|
aria-label="Move time range forwards"
|
||||||
|
onClick={onMoveForward}
|
||||||
|
icon="angle-right"
|
||||||
|
narrow
|
||||||
|
variant={variant}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Tooltip content={ZoomOutTooltip} placement="bottom">
|
<Tooltip content={ZoomOutTooltip} placement="bottom">
|
||||||
<ToolbarButton onClick={onZoom} icon="search-minus" variant={variant} />
|
<ToolbarButton aria-label="Zoom out time range" onClick={onZoom} icon="search-minus" variant={variant} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -210,13 +210,13 @@ export const TimePickerCalendar = memo<Props>((props) => {
|
|||||||
if (isFullscreen) {
|
if (isFullscreen) {
|
||||||
return (
|
return (
|
||||||
<ClickOutsideWrapper onClick={props.onClose}>
|
<ClickOutsideWrapper onClick={props.onClose}>
|
||||||
<div
|
<section
|
||||||
className={styles.container}
|
className={styles.container}
|
||||||
onClick={stopPropagation}
|
onClick={stopPropagation}
|
||||||
aria-label={selectors.components.TimePicker.calendar}
|
aria-label={selectors.components.TimePicker.calendar}
|
||||||
>
|
>
|
||||||
<Body {...props} />
|
<Body {...props} />
|
||||||
</div>
|
</section>
|
||||||
</ClickOutsideWrapper>
|
</ClickOutsideWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { TimePickerTitle } from './TimePickerTitle';
|
|||||||
import { TimeRangeForm } from './TimeRangeForm';
|
import { TimeRangeForm } from './TimeRangeForm';
|
||||||
import { TimeRangeList } from './TimeRangeList';
|
import { TimeRangeList } from './TimeRangeList';
|
||||||
import { TimePickerFooter } from './TimePickerFooter';
|
import { TimePickerFooter } from './TimePickerFooter';
|
||||||
|
import { getFocusStyles } from '../../../themes/mixins';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed, hideQuickRanges, isContainerTall) => {
|
const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed, hideQuickRanges, isContainerTall) => {
|
||||||
return {
|
return {
|
||||||
@@ -30,6 +32,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed, hideQuickRang
|
|||||||
`,
|
`,
|
||||||
body: css`
|
body: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
height: ${isContainerTall ? '381px' : '217px'};
|
height: ${isContainerTall ? '381px' : '217px'};
|
||||||
`,
|
`,
|
||||||
leftSide: css`
|
leftSide: css`
|
||||||
@@ -64,6 +67,16 @@ const getNarrowScreenStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|||||||
border-bottom: 1px solid ${theme.colors.border.weak};
|
border-bottom: 1px solid ${theme.colors.border.weak};
|
||||||
padding: 7px 9px 7px 9px;
|
padding: 7px 9px 7px 9px;
|
||||||
`,
|
`,
|
||||||
|
expandButton: css`
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
${getFocusStyles(theme)}
|
||||||
|
}
|
||||||
|
`,
|
||||||
body: css`
|
body: css`
|
||||||
border-bottom: 1px solid ${theme.colors.border.weak};
|
border-bottom: 1px solid ${theme.colors.border.weak};
|
||||||
`,
|
`,
|
||||||
@@ -163,13 +176,8 @@ export const TimePickerContentWithScreenSize: React.FC<PropsWithScreenSize> = (p
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(styles.container, className)}>
|
<div id="TimePickerContent" className={cx(styles.container, className)}>
|
||||||
<div className={styles.body}>
|
<div className={styles.body}>
|
||||||
{isFullscreen && (
|
|
||||||
<div className={styles.leftSide}>
|
|
||||||
<FullScreenForm {...props} historyOptions={historyOptions} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(!isFullscreen || !hideQuickRanges) && (
|
{(!isFullscreen || !hideQuickRanges) && (
|
||||||
<CustomScrollbar className={styles.rightSide}>
|
<CustomScrollbar className={styles.rightSide}>
|
||||||
{!isFullscreen && <NarrowScreenForm {...props} historyOptions={historyOptions} />}
|
{!isFullscreen && <NarrowScreenForm {...props} historyOptions={historyOptions} />}
|
||||||
@@ -192,6 +200,11 @@ export const TimePickerContentWithScreenSize: React.FC<PropsWithScreenSize> = (p
|
|||||||
)}
|
)}
|
||||||
</CustomScrollbar>
|
</CustomScrollbar>
|
||||||
)}
|
)}
|
||||||
|
{isFullscreen && (
|
||||||
|
<div className={styles.leftSide}>
|
||||||
|
<FullScreenForm {...props} historyOptions={historyOptions} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!hideTimeZone && isFullscreen && <TimePickerFooter timeZone={timeZone} onChangeTimeZone={onChangeTimeZone} />}
|
{!hideTimeZone && isFullscreen && <TimePickerFooter timeZone={timeZone} onChangeTimeZone={onChangeTimeZone} />}
|
||||||
</div>
|
</div>
|
||||||
@@ -218,21 +231,25 @@ const NarrowScreenForm: React.FC<FormProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<fieldset>
|
||||||
<div
|
<div className={styles.header}>
|
||||||
aria-label="TimePicker absolute time range"
|
<button
|
||||||
className={styles.header}
|
className={styles.expandButton}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!hideQuickRanges) {
|
if (!hideQuickRanges) {
|
||||||
setCollapsedFlag(!collapsed);
|
setCollapsedFlag(!collapsed);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
data-testid={selectors.components.TimePicker.absoluteTimeRangeTitle}
|
||||||
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
aria-expanded={!collapsed}
|
||||||
{!hideQuickRanges && <Icon name={!collapsed ? 'angle-up' : 'angle-down'} />}
|
aria-controls="expanded-timerange"
|
||||||
|
>
|
||||||
|
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
||||||
|
{!hideQuickRanges && <Icon name={!collapsed ? 'angle-up' : 'angle-down'} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<div className={styles.body}>
|
<div className={styles.body} id="expanded-timerange">
|
||||||
<div className={styles.form}>
|
<div className={styles.form}>
|
||||||
<TimeRangeForm value={value} onApply={onChange} timeZone={timeZone} isFullscreen={false} />
|
<TimeRangeForm value={value} onApply={onChange} timeZone={timeZone} isFullscreen={false} />
|
||||||
</div>
|
</div>
|
||||||
@@ -246,7 +263,7 @@ const NarrowScreenForm: React.FC<FormProps> = (props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</fieldset>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -261,7 +278,7 @@ const FullScreenForm: React.FC<FormProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div aria-label="TimePicker absolute time range" className={styles.title}>
|
<div className={styles.title} data-testid={selectors.components.TimePicker.absoluteTimeRangeTitle}>
|
||||||
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
<TimePickerTitle>Absolute time range</TimePickerTitle>
|
||||||
</div>
|
</div>
|
||||||
<TimeRangeForm
|
<TimeRangeForm
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const TimePickerFooter: FC<Props> = (props) => {
|
|||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
<div className={cx(style.container, style.editContainer)}>
|
<div className={cx(style.container, style.editContainer)}>
|
||||||
<div aria-label={selectors.components.TimeZonePicker.container} className={style.timeZoneContainer}>
|
<section aria-label={selectors.components.TimeZonePicker.container} className={style.timeZoneContainer}>
|
||||||
<TimeZonePicker
|
<TimeZonePicker
|
||||||
includeInternal={true}
|
includeInternal={true}
|
||||||
onChange={(timeZone) => {
|
onChange={(timeZone) => {
|
||||||
@@ -59,13 +59,13 @@ export const TimePickerFooter: FC<Props> = (props) => {
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
onBlur={onToggleChangeTz}
|
onBlur={onToggleChangeTz}
|
||||||
/>
|
/>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.container}>
|
<section aria-label="Time zone selection" className={style.container}>
|
||||||
<div className={style.timeZoneContainer}>
|
<div className={style.timeZoneContainer}>
|
||||||
<div className={style.timeZone}>
|
<div className={style.timeZone}>
|
||||||
<TimeZoneTitle title={info.name} />
|
<TimeZoneTitle title={info.name} />
|
||||||
@@ -78,7 +78,7 @@ export const TimePickerFooter: FC<Props> = (props) => {
|
|||||||
<Button variant="secondary" onClick={onToggleChangeTz} size="sm">
|
<Button variant="secondary" onClick={onToggleChangeTz} size="sm">
|
||||||
Change time zone
|
Change time zone
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ const getStyle = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
font-size: ${theme.typography.size.md};
|
font-size: ${theme.typography.size.md};
|
||||||
font-weight: ${theme.typography.weight.semibold};
|
font-weight: ${theme.typography.weight.semibold};
|
||||||
color: ${theme.colors.formLabel};
|
color: ${theme.colors.formLabel};
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -17,7 +19,7 @@ export const TimePickerTitle = memo<PropsWithChildren<{}>>(({ children }) => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const styles = getStyle(theme);
|
const styles = getStyle(theme);
|
||||||
|
|
||||||
return <span className={styles.text}>{children}</span>;
|
return <h3 className={styles.text}>{children}</h3>;
|
||||||
});
|
});
|
||||||
|
|
||||||
TimePickerTitle.displayName = 'TimePickerTitle';
|
TimePickerTitle.displayName = 'TimePickerTitle';
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ function setup(initial: TimeRange = defaultTimeRange, timeZone = 'utc'): TimeRan
|
|||||||
|
|
||||||
describe('TimeRangeForm', () => {
|
describe('TimeRangeForm', () => {
|
||||||
it('should render form correcty', () => {
|
it('should render form correcty', () => {
|
||||||
const { getByLabelText } = setup();
|
const { getByLabelText, getByText } = setup();
|
||||||
const { TimePicker } = selectors.components;
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
expect(getByLabelText(TimePicker.applyTimeRange)).toBeInTheDocument();
|
expect(getByText('Apply time range')).toBeInTheDocument();
|
||||||
expect(getByLabelText(TimePicker.fromField)).toBeInTheDocument();
|
expect(getByLabelText(TimePicker.fromField)).toBeInTheDocument();
|
||||||
expect(getByLabelText(TimePicker.toField)).toBeInTheDocument();
|
expect(getByLabelText(TimePicker.toField)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export const TimeRangeForm: React.FC<Props> = (props) => {
|
|||||||
const icon = isFullscreen ? null : <Button icon="calendar-alt" variant="secondary" onClick={onOpen} />;
|
const icon = isFullscreen ? null : <Button icon="calendar-alt" variant="secondary" onClick={onOpen} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div aria-label="Absolute time ranges">
|
||||||
<Field label="From" invalid={from.invalid} error={from.errorMessage}>
|
<Field label="From" invalid={from.invalid} error={from.errorMessage}>
|
||||||
<Input
|
<Input
|
||||||
onClick={(event) => event.stopPropagation()}
|
onClick={(event) => event.stopPropagation()}
|
||||||
@@ -117,7 +117,7 @@ export const TimeRangeForm: React.FC<Props> = (props) => {
|
|||||||
value={to.value}
|
value={to.value}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Button aria-label={selectors.components.TimePicker.applyTimeRange} onClick={onApply}>
|
<Button data-testid={selectors.components.TimePicker.applyTimeRange} onClick={onApply}>
|
||||||
Apply time range
|
Apply time range
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ export const TimeRangeForm: React.FC<Props> = (props) => {
|
|||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
isReversed={isReversed}
|
isReversed={isReversed}
|
||||||
/>
|
/>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const getOptionsStyles = stylesFactory(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string;
|
title: string;
|
||||||
options: TimeOption[];
|
options: TimeOption[];
|
||||||
value?: TimeOption;
|
value?: TimeOption;
|
||||||
onChange: (option: TimeOption) => void;
|
onChange: (option: TimeOption) => void;
|
||||||
@@ -46,30 +46,33 @@ export const TimeRangeList: React.FC<Props> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<section aria-label={title}>
|
||||||
<div className={styles.title}>
|
<fieldset>
|
||||||
<TimePickerTitle>{title}</TimePickerTitle>
|
<div className={styles.title}>
|
||||||
</div>
|
<TimePickerTitle>{title}</TimePickerTitle>
|
||||||
<Options {...props} />
|
</div>
|
||||||
</>
|
<Options {...props} />
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Options: React.FC<Props> = ({ options, value, onChange }) => {
|
const Options: React.FC<Props> = ({ options, value, onChange, title }) => {
|
||||||
const styles = getOptionsStyles();
|
const styles = getOptionsStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<ul aria-roledescription="Time range selection">
|
||||||
{options.map((option, index) => (
|
{options.map((option, index) => (
|
||||||
<TimeRangeOption
|
<TimeRangeOption
|
||||||
key={keyForOption(option, index)}
|
key={keyForOption(option, index)}
|
||||||
value={option}
|
value={option}
|
||||||
selected={isEqual(option, value)}
|
selected={isEqual(option, value)}
|
||||||
onSelect={onChange}
|
onSelect={onChange}
|
||||||
|
name={title}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</ul>
|
||||||
<div className={styles.grow} />
|
<div className={styles.grow} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ import React, { memo } from 'react';
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { GrafanaTheme2, TimeOption } from '@grafana/data';
|
import { GrafanaTheme2, TimeOption } from '@grafana/data';
|
||||||
import { useStyles2 } from '../../../themes/ThemeContext';
|
import { useStyles2 } from '../../../themes/ThemeContext';
|
||||||
|
import { getFocusStyles } from '../../../themes/mixins';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
container: css`
|
container: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-direction: row-reverse;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 7px 9px 7px 9px;
|
padding: 7px 9px 7px 9px;
|
||||||
|
|
||||||
@@ -16,10 +19,20 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
selected: css`
|
selected: css`
|
||||||
background: ${theme.colors.action.selected};
|
background: ${theme.colors.action.selected};
|
||||||
}
|
font-weight: ${theme.typography.fontWeightMedium};
|
||||||
`,
|
`,
|
||||||
|
radio: css`
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&:focus-visible + label {
|
||||||
|
${getFocusStyles(theme)};
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
label: css`
|
||||||
|
cursor: pointer;
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,15 +40,31 @@ interface Props {
|
|||||||
value: TimeOption;
|
value: TimeOption;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
onSelect: (option: TimeOption) => void;
|
onSelect: (option: TimeOption) => void;
|
||||||
|
/**
|
||||||
|
* Input identifier. This should be the same for all options in a group.
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TimeRangeOption = memo<Props>(({ value, onSelect, selected = false }) => {
|
export const TimeRangeOption = memo<Props>(({ value, onSelect, selected = false, name }) => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
// In case there are more of the same timerange in the list
|
||||||
|
const id = uuidv4();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(styles.container, selected && styles.selected)} onClick={() => onSelect(value)} tabIndex={-1}>
|
<li className={cx(styles.container, selected && styles.selected)}>
|
||||||
<span>{value.display}</span>
|
<input
|
||||||
</div>
|
className={styles.radio}
|
||||||
|
checked={selected}
|
||||||
|
name={name}
|
||||||
|
type="checkbox"
|
||||||
|
id={id}
|
||||||
|
onChange={() => onSelect(value)}
|
||||||
|
/>
|
||||||
|
<label className={styles.label} htmlFor={id}>
|
||||||
|
{value.display}
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const TimeZonePicker: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
menuShouldPortal
|
||||||
value={selected}
|
value={selected}
|
||||||
placeholder="Type to search (country, city, abbreviation)"
|
placeholder="Type to search (country, city, abbreviation)"
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Story, Preview, Props } from '@storybook/addon-docs/blocks';
|
||||||
|
import { FileDropzone } from './FileDropzone';
|
||||||
|
|
||||||
|
# FileDropzone
|
||||||
|
|
||||||
|
A dropzone component to use for file uploads.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { FileDropzone } from '@grafana/ui';
|
||||||
|
|
||||||
|
<FileDropzone onLoad={(result) => console.log(result)} />;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
<Props of={FileDropzone} />
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { FileDropzone, FileDropzoneProps } from '@grafana/ui';
|
||||||
|
import { Meta, Story } from '@storybook/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||||
|
import mdx from './FileDropzone.mdx';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Forms/FileDropzone',
|
||||||
|
component: FileDropzone,
|
||||||
|
decorators: [withCenteredStory],
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
page: mdx,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
onLoad: { action: 'onLoad' },
|
||||||
|
},
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
export const Basic: Story<FileDropzoneProps> = (args) => {
|
||||||
|
return <FileDropzone {...args} />;
|
||||||
|
};
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { FileDropzone } from './FileDropzone';
|
||||||
|
import { REMOVE_FILE } from './FileListItem';
|
||||||
|
|
||||||
|
const file = ({
|
||||||
|
fileBits = JSON.stringify({ ping: true }),
|
||||||
|
fileName = 'ping.json',
|
||||||
|
options = { type: 'application/json' },
|
||||||
|
}) => new File([fileBits], fileName, options);
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
file({}),
|
||||||
|
file({ fileName: 'pong.json' }),
|
||||||
|
file({ fileBits: 'something', fileName: 'something.jpg', options: { type: 'image/jpeg' } }),
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('The FileDropzone component', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show the default text of the dropzone component when no props passed', () => {
|
||||||
|
render(<FileDropzone />);
|
||||||
|
|
||||||
|
expect(screen.getByText('Upload file')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show accepted file type when passed in the options as a string', () => {
|
||||||
|
render(<FileDropzone options={{ accept: '.json' }} />);
|
||||||
|
|
||||||
|
expect(screen.getByText('Accepted file type: .json')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show accepted file types when passed in the options as a string array', () => {
|
||||||
|
render(<FileDropzone options={{ accept: ['.json', '.txt'] }} />);
|
||||||
|
|
||||||
|
expect(screen.getByText('Accepted file types: .json, .txt')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle file removal from the list', async () => {
|
||||||
|
render(<FileDropzone />);
|
||||||
|
|
||||||
|
dispatchEvt(screen.getByTestId('dropzone'), 'drop', mockData(files));
|
||||||
|
|
||||||
|
expect(await screen.findAllByLabelText(REMOVE_FILE)).toHaveLength(3);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getAllByLabelText(REMOVE_FILE)[0]);
|
||||||
|
|
||||||
|
expect(await screen.findAllByLabelText(REMOVE_FILE)).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite selected file when multiple false', async () => {
|
||||||
|
render(<FileDropzone options={{ multiple: false }} />);
|
||||||
|
|
||||||
|
dispatchEvt(screen.getByTestId('dropzone'), 'drop', mockData([file({})]));
|
||||||
|
|
||||||
|
expect(await screen.findAllByLabelText(REMOVE_FILE)).toHaveLength(1);
|
||||||
|
expect(screen.getByText('ping.json')).toBeInTheDocument();
|
||||||
|
|
||||||
|
dispatchEvt(screen.getByTestId('dropzone'), 'drop', mockData([file({ fileName: 'newFile.jpg' })]));
|
||||||
|
|
||||||
|
expect(await screen.findByText('newFile.jpg')).toBeInTheDocument();
|
||||||
|
expect(screen.getAllByLabelText(REMOVE_FILE)).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the passed readAs prop with the FileReader API', async () => {
|
||||||
|
render(<FileDropzone readAs="readAsDataURL" />);
|
||||||
|
const fileReaderSpy = jest.spyOn(FileReader.prototype, 'readAsDataURL');
|
||||||
|
|
||||||
|
dispatchEvt(screen.getByTestId('dropzone'), 'drop', mockData([file({})]));
|
||||||
|
|
||||||
|
expect(await screen.findByText('ping.json')).toBeInTheDocument();
|
||||||
|
expect(fileReaderSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the readAsText FileReader API if no readAs prop passed', async () => {
|
||||||
|
render(<FileDropzone />);
|
||||||
|
const fileReaderSpy = jest.spyOn(FileReader.prototype, 'readAsText');
|
||||||
|
|
||||||
|
dispatchEvt(screen.getByTestId('dropzone'), 'drop', mockData([file({})]));
|
||||||
|
|
||||||
|
expect(await screen.findByText('ping.json')).toBeInTheDocument();
|
||||||
|
expect(fileReaderSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the onDrop that is passed', async () => {
|
||||||
|
const onDrop = jest.fn();
|
||||||
|
const fileToUpload = file({});
|
||||||
|
render(<FileDropzone options={{ onDrop }} />);
|
||||||
|
const fileReaderSpy = jest.spyOn(FileReader.prototype, 'readAsText');
|
||||||
|
|
||||||
|
dispatchEvt(screen.getByTestId('dropzone'), 'drop', mockData([fileToUpload]));
|
||||||
|
|
||||||
|
expect(await screen.findByText('ping.json')).toBeInTheDocument();
|
||||||
|
expect(fileReaderSpy).not.toBeCalled();
|
||||||
|
expect(onDrop).toBeCalledWith([fileToUpload], [], expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show children inside the dropzone', () => {
|
||||||
|
const component = (
|
||||||
|
<FileDropzone>
|
||||||
|
<p>Custom dropzone text</p>
|
||||||
|
</FileDropzone>
|
||||||
|
);
|
||||||
|
render(component);
|
||||||
|
|
||||||
|
screen.getByText('Custom dropzone text');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function dispatchEvt(node: HTMLElement, type: string, data: any) {
|
||||||
|
const event = new Event(type, { bubbles: true });
|
||||||
|
Object.assign(event, data);
|
||||||
|
fireEvent(node, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockData(files: File[]) {
|
||||||
|
return {
|
||||||
|
dataTransfer: {
|
||||||
|
files,
|
||||||
|
items: files.map((file) => ({
|
||||||
|
kind: 'file',
|
||||||
|
type: file.type,
|
||||||
|
getAsFile: () => file,
|
||||||
|
})),
|
||||||
|
types: ['Files'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user