init alerting
This commit is contained in:
parent
5a7f978fea
commit
05300a6a01
|
@ -8,6 +8,7 @@ import (
|
|||
"infini.sh/search-center/api/index_management"
|
||||
"infini.sh/search-center/config"
|
||||
"path"
|
||||
"infini.sh/search-center/service/alerting"
|
||||
)
|
||||
|
||||
func Init(cfg *config.AppConfig) {
|
||||
|
@ -36,6 +37,13 @@ func Init(cfg *config.AppConfig) {
|
|||
ui.HandleUIMethod(api.DELETE, path.Join(pathPrefix, "index/:index"), handler.HandleDeleteIndexAction)
|
||||
ui.HandleUIMethod(api.POST, path.Join(pathPrefix, "index/:index"), handler.HandleCreateIndexAction)
|
||||
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/alerts", alerting.GetAlerts)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors/_search", alerting.Search)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_indices", alerting.GetIndices)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_aliases", alerting.GetAliases)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_mappings", alerting.GetMappings)
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/_plugins", alerting.GetPlugins)
|
||||
|
||||
|
||||
task.RegisterScheduleTask(task.ScheduleTask{
|
||||
Description: "sync reindex task result",
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
package alerting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
httprouter "infini.sh/framework/core/api/router"
|
||||
"infini.sh/framework/core/elastic"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getQueryParam(req *http.Request, key string, or ...string) string {
|
||||
query := req.URL.Query()
|
||||
val := query.Get(key)
|
||||
if val == "" && len(or)>0 {
|
||||
return or[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
from = getQueryParam(req, "from", "0")
|
||||
size = getQueryParam(req, "size", "20")
|
||||
search = getQueryParam(req, "search")
|
||||
sortDirection = getQueryParam(req, "sortDirection", "desc")
|
||||
sortField = getQueryParam(req, "sortField", "start_time")
|
||||
severityLevel = getQueryParam(req, "severityLevel", "ALL")
|
||||
alertState = getQueryParam(req, "alertState", "ALL")
|
||||
//monitorIds = getQueryParam(req, "monitorIds")
|
||||
params = map[string]string{
|
||||
"startIndex": from,
|
||||
"size": size,
|
||||
"severityLevel": severityLevel,
|
||||
"alertState": alertState,
|
||||
"searchString": search,
|
||||
}
|
||||
)
|
||||
|
||||
switch sortField {
|
||||
case "monitor_name", "trigger_name":
|
||||
params["sortString"] = fmt.Sprintf(`%s.keyword`, sortField)
|
||||
params["sortOrder"] = sortDirection
|
||||
case "start_time":
|
||||
params["sortString"] = sortField
|
||||
params["sortOrder"] = sortDirection
|
||||
case "end_time":
|
||||
params["sortString"] = sortField
|
||||
params["sortOrder"] = sortDirection
|
||||
params["missing"] = "_first"
|
||||
if sortDirection == "asc" {
|
||||
params["missing"] = "_last"
|
||||
}
|
||||
case "acknowledged_time":
|
||||
params["sortString"] = sortField
|
||||
params["sortOrder"] = sortDirection
|
||||
params["missing"] = "_last"
|
||||
}
|
||||
|
||||
if clearSearch := strings.TrimSpace(search); clearSearch != ""{
|
||||
searches := strings.Split(clearSearch, " ")
|
||||
clearSearch = strings.Join(searches, "* *")
|
||||
params["searchString"] = fmt.Sprintf("*%s*", clearSearch)
|
||||
}
|
||||
reqUrl := conf.Endpoint + "/_opendistro/_alerting/monitors/alerts"
|
||||
|
||||
res, err := doRequest(reqUrl, http.MethodGet, params, nil)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
var alertRes = AlertResponse{}
|
||||
err = decodeJSON(res.Body, &alertRes)
|
||||
defer res.Body.Close()
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var alerts = []IfaceMap{}
|
||||
for _, hit := range alertRes.Alerts {
|
||||
alert := IfaceMap{
|
||||
"id": hit["alert_id"],
|
||||
}
|
||||
for k, v := range hit {
|
||||
alert[k] = v
|
||||
}
|
||||
alert["version"] = hit["alert_version"]
|
||||
}
|
||||
writeJSON(w, IfaceMap{
|
||||
"ok": true,
|
||||
"alerts": alerts,
|
||||
"totalAlerts": alertRes.TotalAlerts,
|
||||
}, http.StatusOK)
|
||||
|
||||
}
|
||||
func writeError(w http.ResponseWriter, err error) {
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"body": map[string]interface{}{
|
||||
"ok": false,
|
||||
"err": err.Error(),
|
||||
},
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
type IfaceMap map[string]interface{}
|
||||
|
||||
type AlertResponse struct {
|
||||
Alerts []IfaceMap `json:"alerts"`
|
||||
TotalAlerts int `json:"totalAlerts"`
|
||||
}
|
||||
|
||||
func decodeJSON(reader io.Reader, obj interface{}) error{
|
||||
dec := json.NewDecoder(reader)
|
||||
return dec.Decode(obj)
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, data interface{}, statusCode int){
|
||||
w.WriteHeader(statusCode)
|
||||
w.Header().Set("content-type", "application/json")
|
||||
buf, _ := json.Marshal(data)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
var alertClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
|
||||
func doRequest(requestUrl string, method string, params map[string]string, body interface{}) (*http.Response, error){
|
||||
var req *http.Request
|
||||
if params != nil && len(params) > 0 {
|
||||
var queryValues = url.Values{}
|
||||
for k, v := range params {
|
||||
queryValues.Set(k, v)
|
||||
}
|
||||
requestUrl += "?"+ queryValues.Encode()
|
||||
}
|
||||
var reader io.Reader
|
||||
if body != nil {
|
||||
switch body.(type) {
|
||||
case string:
|
||||
reader = bytes.NewBufferString(body.(string))
|
||||
case io.Reader:
|
||||
reader = body.(io.Reader)
|
||||
default:
|
||||
rw := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(rw)
|
||||
err := enc.Encode(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader = rw
|
||||
}
|
||||
}
|
||||
req, _ = http.NewRequest(method, requestUrl, reader)
|
||||
req.Header.Set("content-type", "application/json")
|
||||
return alertClient.Do(req)
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
package alerting
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
httprouter "infini.sh/framework/core/api/router"
|
||||
"infini.sh/framework/core/elastic"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
type SearchBody struct {
|
||||
Query IfaceMap `json:"query"`
|
||||
Index string `json:"index"`
|
||||
Size int `json:""size`
|
||||
}
|
||||
|
||||
func Search(w http.ResponseWriter, req *http.Request, ps httprouter.Params){
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
var body = SearchBody{}
|
||||
err := decodeJSON(req.Body, &body)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
reqUrl := conf.Endpoint +"/_opendistro/_alerting/monitors/_search"
|
||||
params := map[string]string{
|
||||
"index": body.Index,
|
||||
}
|
||||
body.Query["size"] = body.Size
|
||||
res, err := doRequest(reqUrl, http.MethodPost, params, body.Query)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
var resBody = IfaceMap{}
|
||||
err = decodeJSON(res.Body, &resBody)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, IfaceMap{
|
||||
"body": IfaceMap{
|
||||
"ok": true,
|
||||
"resp": resBody,
|
||||
},
|
||||
}, http.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
func GetIndices(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
|
||||
var body = struct{
|
||||
Index string `json:"index"`
|
||||
}{}
|
||||
err := decodeJSON(req.Body, &body)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
reqUrl := fmt.Sprintf("%s/_cat/indices/%s", conf.Endpoint, body.Index)
|
||||
params := map[string]string{
|
||||
"format": "json",
|
||||
"h": "health,index,status",
|
||||
}
|
||||
res, err := doRequest(reqUrl, http.MethodGet, params, nil)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
var resBody = []IfaceMap{}
|
||||
err = decodeJSON(res.Body, &resBody)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, IfaceMap{
|
||||
"body": IfaceMap{
|
||||
"ok": true,
|
||||
"resp": resBody,
|
||||
},
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
func GetAliases(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
|
||||
var body = struct{
|
||||
Alias string `json:"alias"`
|
||||
}{}
|
||||
err := decodeJSON(req.Body, &body)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
reqUrl := fmt.Sprintf("%s/_cat/aliases/%s", conf.Endpoint, body.Alias)
|
||||
params := map[string]string{
|
||||
"format": "json",
|
||||
"h": "alias,index",
|
||||
}
|
||||
res, err := doRequest(reqUrl, http.MethodGet, params, nil)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
var resBody = []IfaceMap{}
|
||||
err = decodeJSON(res.Body, &resBody)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, IfaceMap{
|
||||
"body": IfaceMap{
|
||||
"ok": true,
|
||||
"resp": resBody,
|
||||
},
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
func GetMappings(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
|
||||
var body = struct{
|
||||
Index string `json:"index"`
|
||||
}{}
|
||||
err := decodeJSON(req.Body, &body)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
reqUrl := fmt.Sprintf("%s/%s/_mapping", conf.Endpoint, body.Index)
|
||||
res, err := doRequest(reqUrl, http.MethodGet, nil, nil)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
var resBody = IfaceMap{}
|
||||
err = decodeJSON(res.Body, &resBody)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, IfaceMap{
|
||||
"body": IfaceMap{
|
||||
"ok": true,
|
||||
"resp": resBody,
|
||||
},
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
|
||||
func GetPlugins(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
|
||||
reqUrl := fmt.Sprintf("%s/_cat/plugins", conf.Endpoint)
|
||||
res, err := doRequest(reqUrl, http.MethodGet, map[string]string{
|
||||
"format": "json",
|
||||
"h": "component",
|
||||
}, nil)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
var resBody = []IfaceMap{}
|
||||
err = decodeJSON(res.Body, &resBody)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, IfaceMap{
|
||||
"body": IfaceMap{
|
||||
"ok": true,
|
||||
"resp": resBody,
|
||||
},
|
||||
}, http.StatusOK)
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package alerting
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
httprouter "infini.sh/framework/core/api/router"
|
||||
"infini.sh/framework/core/elastic"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
|
||||
mid := ps.ByName("monitorID")
|
||||
// /_opendistro/_alerting/monitors/uiSjqXsBHT9Hsiy5Dq6g
|
||||
reqUrl := fmt.Sprintf("%s/_opendistro/_alerting/monitors/%s", conf.Endpoint, mid)
|
||||
res, err := doRequest(reqUrl, http.MethodGet, nil, nil)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
var resBody = IfaceMap{}
|
||||
err = decodeJSON(res.Body, &resBody)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
if _, ok := resBody["monitor"]; !ok {
|
||||
writeJSON(w, IfaceMap{
|
||||
"body": IfaceMap{
|
||||
"ok": false,
|
||||
},
|
||||
}, http.StatusOK)
|
||||
return
|
||||
}
|
||||
///_opendistro/_alerting/monitors/_search?index=.opendistro-alerting-alert*
|
||||
|
||||
queryDSL := ` {
|
||||
"size": 0,
|
||||
"query"": {
|
||||
"bool": {
|
||||
"must": {
|
||||
"term"": {
|
||||
"monitor_id": "%s",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"aggs": {
|
||||
"active_count": {
|
||||
"terms": {
|
||||
"field": "state",
|
||||
}
|
||||
},
|
||||
"24_hour_count": {
|
||||
"date_range": {
|
||||
"field": "start_time",
|
||||
"ranges": [{ "from": "now-24h/h" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
queryDSL = fmt.Sprintf(queryDSL, id)
|
||||
reqUrl = fmt.Sprintf("%s/_opendistro/_alerting/monitors/_search", conf.Endpoint)
|
||||
res, err = doRequest(reqUrl, http.MethodPost, map[string]string{
|
||||
"index": ".opendistro-alerting-alert*",
|
||||
}, queryDSL)
|
||||
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var searchResBody = IfaceMap{}
|
||||
err = decodeJSON(res.Body, &searchResBody)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
//dayCount := queryValue(searchResBody, "aggregations.24_hour_count.buckets.0.doc_count", 0)
|
||||
//activeBuckets := queryValue(searchResBody, "aggregations.active_count.buckets",[]interface{}{})
|
||||
writeJSON(w, IfaceMap{
|
||||
"body": IfaceMap{
|
||||
"ok": true,
|
||||
"resp": resBody,
|
||||
},
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
func queryValue(obj map[string]interface{}, key string, defaultValue interface{}) interface{} {
|
||||
if key == "" {
|
||||
return obj
|
||||
}
|
||||
idx := strings.Index(key, ".")
|
||||
if idx == -1 {
|
||||
if v, ok := obj[key]; ok {
|
||||
return v
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
ckey := key[0:idx]
|
||||
|
||||
if v, ok := obj[ckey]; ok {
|
||||
if vmap, ok := v.(map[string]interface{}); ok {
|
||||
return queryValue(vmap, key[idx+1:], defaultValue)
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
|
@ -104,6 +104,17 @@ export default [
|
|||
component: './DevTool/Console',
|
||||
},
|
||||
|
||||
//alerting
|
||||
{
|
||||
routes:[
|
||||
{ path: '/', redirect: '/' },
|
||||
],
|
||||
path: '/alerting',
|
||||
name: 'alerting',
|
||||
icon: 'alert',
|
||||
component: './Alerting/index',
|
||||
},
|
||||
|
||||
//data
|
||||
{
|
||||
path: '/data',
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
"dns": "^0.2.2",
|
||||
"dva": "^2.4.0",
|
||||
"enquire-js": "^0.2.1",
|
||||
"formik": "^2.2.9",
|
||||
"fp-ts": "^2.10.5",
|
||||
"hash.js": "^1.1.5",
|
||||
"honeycomb-grid": "^3.1.7",
|
||||
|
@ -63,13 +64,15 @@
|
|||
"react-json-view": "^1.19.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-use": "^17.2.4",
|
||||
"react-vis": "^1.11.7",
|
||||
"readline": "^1.3.0",
|
||||
"repl": "^0.1.3",
|
||||
"reqwest": "^2.0.5",
|
||||
"rison-node": "^2.1.1",
|
||||
"rxjs": "^7.2.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"use-query-params": "^1.2.3"
|
||||
"use-query-params": "^1.2.3",
|
||||
"uuidv4": "^6.2.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"antd-pro-merge-less": "^0.1.0",
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
EuiSwitch,
|
||||
} from '@elastic/eui';
|
||||
import {useState} from 'react';
|
||||
import {Button, Icon} from 'antd';
|
||||
|
||||
|
||||
interface HeaderProps {
|
||||
|
@ -140,18 +141,15 @@ export const Header: React.FC<HeaderProps> = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false} >
|
||||
<EuiFormRow hasEmptyLabelSpace style={{marginBottom:60,marginTop:'auto'}}>
|
||||
<EuiButton
|
||||
fill
|
||||
iconSide="right"
|
||||
iconType="arrowRight"
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
goToNextStep(query, viewName)
|
||||
}}
|
||||
isDisabled={isNextStepDisabled || viewName.replace(' ', '').length <= 0}
|
||||
data-test-subj="createIndexPatternGoToStep2Button"
|
||||
disabled={isNextStepDisabled || viewName.replace(' ', '').length <= 0}
|
||||
>
|
||||
下一步
|
||||
</EuiButton>
|
||||
下一步<Icon type="right"/>
|
||||
</Button>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -105,7 +105,7 @@ export class IndicesList extends React.Component<IndicesListProps, IndicesListSt
|
|||
iconSide="right"
|
||||
onClick={this.openPerPageControl}
|
||||
>
|
||||
{`Rows per page: ${perPage}`}
|
||||
{`每页行数: ${perPage}`}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty } from '@elastic/eui';
|
||||
import {Button} from 'antd';
|
||||
|
||||
export const ActionButtons = ({
|
||||
goToPreviousStep,
|
||||
|
@ -32,19 +33,18 @@ export const ActionButtons = ({
|
|||
}) => (
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="arrowLeft" onClick={goToPreviousStep}>
|
||||
<Button icon="left" onClick={goToPreviousStep}>
|
||||
返回
|
||||
</EuiButtonEmpty>
|
||||
</Button>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
isDisabled={!submittable}
|
||||
data-test-subj="createIndexPatternButton"
|
||||
fill
|
||||
<Button
|
||||
disabled={!submittable}
|
||||
type="primary"
|
||||
onClick={createIndexPattern}
|
||||
>
|
||||
创建视图
|
||||
</EuiButton>
|
||||
</Button>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -39,6 +39,10 @@ import { IndexPatternTableItem } from '../types';
|
|||
import { getIndexPatterns } from '../utils';
|
||||
import {useGlobalContext} from '../../context';
|
||||
import { IndexPattern, IndexPatternField } from '../../import';
|
||||
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
|
||||
import styles from '@/pages/System/Cluster/step.less';
|
||||
import clusterBg from '@/assets/cluster_bg.png';
|
||||
|
||||
|
||||
export interface EditIndexPatternProps extends RouteComponentProps {
|
||||
indexPattern: IndexPattern;
|
||||
|
@ -152,16 +156,43 @@ export const EditIndexPattern = withRouter(
|
|||
|
||||
const showTagsSection = Boolean(indexPattern.timeFieldName || (tags && tags.length > 0));
|
||||
|
||||
const content = (
|
||||
<div className={styles.pageHeaderContent}>
|
||||
<EuiText>
|
||||
<p>
|
||||
当前页面列出匹配 <strong>{indexPattern.title}</strong> 索引的所有字段,字段类型为 Elasticsearch 定义类型。 若需要更改类型,请使用 Elasticsearch{' '}
|
||||
<EuiLink
|
||||
href="http://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html"
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
{mappingAPILink}
|
||||
</EuiLink>
|
||||
</p>
|
||||
</EuiText>
|
||||
</div>
|
||||
);
|
||||
|
||||
const extraContent = (
|
||||
<div className={styles.extraImg}>
|
||||
<img
|
||||
alt="数据视图"
|
||||
src={clusterBg}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<PageHeaderWrapper title={indexPattern.viewName} content={content} extraContent={extraContent}>
|
||||
<EuiPanel paddingSize={'l'}>
|
||||
<div data-test-subj="editIndexPattern" role="region" aria-label={headingAriaLabel}>
|
||||
<IndexHeader
|
||||
{/* <IndexHeader
|
||||
indexPattern={indexPattern}
|
||||
setDefault={setDefaultPattern}
|
||||
refreshFields={refreshFields}
|
||||
deleteIndexPatternClick={removePattern}
|
||||
defaultIndex={defaultIndex}
|
||||
/>
|
||||
/> */}
|
||||
<EuiSpacer size="s" />
|
||||
{showTagsSection && (
|
||||
<EuiFlexGroup wrap>
|
||||
|
@ -208,6 +239,7 @@ export const EditIndexPattern = withRouter(
|
|||
/>
|
||||
</div>
|
||||
</EuiPanel>
|
||||
</PageHeaderWrapper>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ import { Table } from './components/table';
|
|||
import { getFieldFormat } from './lib';
|
||||
import { IndexedFieldItem } from './types';
|
||||
import { IndexPatternField, IndexPattern, IFieldType } from '../../../import';
|
||||
import {EuiContext} from '@elastic/eui';
|
||||
|
||||
interface IndexedFieldsTableProps {
|
||||
fields: IndexPatternField[];
|
||||
|
@ -108,11 +109,18 @@ export class IndexedFieldsTable extends Component<
|
|||
|
||||
return (
|
||||
<div>
|
||||
<EuiContext i18n={{
|
||||
mapping: {
|
||||
'euiTablePagination.rowsPerPage': '每页行数',
|
||||
'euiTablePagination.rowsPerPageOption': '{rowsPerPage} 行'
|
||||
}
|
||||
}}>
|
||||
<Table
|
||||
indexPattern={indexPattern}
|
||||
items={fields}
|
||||
editField={(field) => this.props.helpers.redirectToRoute(field)}
|
||||
/>
|
||||
</EuiContext>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ export default {
|
|||
'component.noticeIcon.empty': 'No notifications',
|
||||
'menu.home': 'Home',
|
||||
'menu.devtool': 'CONSOLE',
|
||||
'menu.alerting': 'AERTING',
|
||||
|
||||
'menu.cluster': 'CLUSTER',
|
||||
'menu.cluster.overview': 'OVERVIEW',
|
||||
|
|
|
@ -92,6 +92,7 @@ export default {
|
|||
|
||||
'menu.home': '首页',
|
||||
'menu.devtool': '开发工具',
|
||||
'menu.alerting': '告警管理',
|
||||
|
||||
'menu.cluster': '集群管理',
|
||||
'menu.cluster.overview': '概览',
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { HashRouter as Router, Route } from 'react-router-dom';
|
||||
|
||||
import 'react-vis/dist/style.css';
|
||||
// TODO: review the CSS style and migrate the necessary style to SASS, as Less is not supported in Kibana "new platform" anymore
|
||||
// import './less/main.less';
|
||||
import Main from './pages/Main';
|
||||
import { CoreContext } from './utils/CoreContext';
|
||||
|
||||
export function renderApp(coreStart, params) {
|
||||
const isDarkMode = coreStart.uiSettings.get('theme:darkMode') || false;
|
||||
coreStart.chrome.setBreadcrumbs([{ text: 'Alerting' }]); // Set Breadcrumbs for the plugin
|
||||
|
||||
// Load Chart's dark mode CSS
|
||||
if (isDarkMode) {
|
||||
require('@elastic/charts/dist/theme_only_dark.css');
|
||||
} else {
|
||||
require('@elastic/charts/dist/theme_only_light.css');
|
||||
}
|
||||
|
||||
// render react to DOM
|
||||
ReactDOM.render(
|
||||
<Router>
|
||||
<CoreContext.Provider
|
||||
value={{ http: coreStart.http, isDarkMode, notifications: coreStart.notifications }}
|
||||
>
|
||||
<Route render={(props) => <Main title="Alerting" {...props} />} />
|
||||
</CoreContext.Provider>
|
||||
</Router>,
|
||||
params.element
|
||||
);
|
||||
return () => ReactDOM.unmountComponentAtNode(params.element);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiText } from '@elastic/eui';
|
||||
|
||||
const propTypes = {
|
||||
titleText: PropTypes.string,
|
||||
emptyText: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
onRemove: PropTypes.func.isRequired,
|
||||
onRenderKeyField: PropTypes.func.isRequired,
|
||||
onRenderValueField: PropTypes.func.isRequired,
|
||||
addButtonText: PropTypes.string,
|
||||
removeButtonText: PropTypes.string,
|
||||
isEnabled: PropTypes.bool,
|
||||
};
|
||||
const defaultProps = {
|
||||
titleText: '',
|
||||
emptyText: 'No attributes found.',
|
||||
addButtonText: 'Add',
|
||||
removeButtonText: 'Remove',
|
||||
isEnabled: true,
|
||||
};
|
||||
|
||||
const AttributeEditor = ({
|
||||
titleText,
|
||||
emptyText,
|
||||
name,
|
||||
items,
|
||||
onAdd,
|
||||
onRemove,
|
||||
onRenderKeyField,
|
||||
onRenderValueField,
|
||||
addButtonText,
|
||||
removeButtonText,
|
||||
isEnabled,
|
||||
}) => {
|
||||
return (
|
||||
<EuiFlexGroup direction="column" alignItems="flexStart" style={{ paddingLeft: '10px' }}>
|
||||
{!_.isEmpty(titleText) ? (
|
||||
<EuiFlexItem>
|
||||
<EuiText size="xs">{titleText}</EuiText>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
{!_.isEmpty(items) ? (
|
||||
items.map((item, index) => (
|
||||
<EuiFlexItem style={{ marginBottom: 0 }} key={`${name}.${index}.key`}>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem>
|
||||
{onRenderKeyField(`${name}.${index}.key`, index, isEnabled)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{onRenderValueField(`${name}.${index}.value`, index, isEnabled)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
style={{ marginTop: 10 }}
|
||||
size="s"
|
||||
onClick={e => onRemove(index)}
|
||||
disabled={!isEnabled}
|
||||
>
|
||||
{removeButtonText}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
))
|
||||
) : (
|
||||
<EuiFlexItem style={{ marginBottom: 0 }}>
|
||||
<EuiText size="xs"> {emptyText} </EuiText>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem>
|
||||
<EuiButton size="s" onClick={onAdd} disabled={!isEnabled}>
|
||||
{addButtonText}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
AttributeEditor.propTypes = propTypes;
|
||||
AttributeEditor.defaultProps = defaultProps;
|
||||
|
||||
export default AttributeEditor;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import AttributeEditor from './AttributeEditor';
|
||||
|
||||
export default AttributeEditor;
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import queryString from 'query-string';
|
||||
import { EuiBreadcrumbs } from '@elastic/eui';
|
||||
import {
|
||||
APP_PATH,
|
||||
DESTINATION_ACTIONS,
|
||||
MONITOR_ACTIONS,
|
||||
TRIGGER_ACTIONS,
|
||||
} from '../../utils/constants';
|
||||
|
||||
const propTypes = {
|
||||
history: PropTypes.object.isRequired,
|
||||
httpClient: PropTypes.object.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default class Breadcrumbs extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { breadcrumbs: [] };
|
||||
|
||||
this.getBreadcrumbs = this.getBreadcrumbs.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getBreadcrumbs();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
location: { pathname: prevPathname, search: prevSearch },
|
||||
} = prevProps;
|
||||
const {
|
||||
location: { pathname, search },
|
||||
} = this.props;
|
||||
if (prevPathname + prevSearch !== pathname + search) {
|
||||
this.getBreadcrumbs();
|
||||
}
|
||||
}
|
||||
|
||||
async getBreadcrumbs() {
|
||||
const {
|
||||
httpClient,
|
||||
history,
|
||||
location: { state: routeState },
|
||||
} = this.props;
|
||||
const rawBreadcrumbs = await getBreadcrumbs(window.location.hash, routeState, httpClient);
|
||||
const breadcrumbs = rawBreadcrumbs.map((breadcrumb) =>
|
||||
createEuiBreadcrumb(breadcrumb, history)
|
||||
);
|
||||
this.setState({ breadcrumbs });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { breadcrumbs } = this.state;
|
||||
return (
|
||||
<EuiBreadcrumbs
|
||||
breadcrumbs={breadcrumbs}
|
||||
responsive={false}
|
||||
truncate={true}
|
||||
style={{ padding: '0px 15px' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Breadcrumbs.propTypes = propTypes;
|
||||
|
||||
export function createEuiBreadcrumb(breadcrumb, history) {
|
||||
const { text, href } = breadcrumb;
|
||||
return {
|
||||
text,
|
||||
href: `#${href}`,
|
||||
onClick: (e) => {
|
||||
e.preventDefault();
|
||||
history.push(href);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function getBreadcrumbs(hash, routeState, httpClient) {
|
||||
const routes = parseLocationHash(hash);
|
||||
const asyncBreadcrumbs = await Promise.all(
|
||||
routes.map((route) => getBreadcrumb(route, routeState, httpClient))
|
||||
);
|
||||
const breadcrumbs = _.flatten(asyncBreadcrumbs).filter((breadcrumb) => !!breadcrumb);
|
||||
return breadcrumbs;
|
||||
}
|
||||
|
||||
export function parseLocationHash(hash) {
|
||||
return hash.split('/').filter((route) => !!route);
|
||||
}
|
||||
|
||||
export async function getBreadcrumb(route, routeState, httpClient) {
|
||||
const [base, queryParams] = route.split('?');
|
||||
if (!base) return null;
|
||||
// This condition is true for any auto generated 20 character long,
|
||||
// URL-safe, base64-encoded document ID by elasticsearch
|
||||
if (RegExp(/^[0-9a-z_-]{20}$/i).test(base)) {
|
||||
const { action } = queryString.parse(`?${queryParams}`);
|
||||
switch (action) {
|
||||
case DESTINATION_ACTIONS.UPDATE_DESTINATION:
|
||||
const destinationName = _.get(routeState, 'destinationToEdit.name', base);
|
||||
const destinationBreadcrumbs = [{ text: destinationName, href: `/destinations/${base}` }];
|
||||
if (action === DESTINATION_ACTIONS.UPDATE_DESTINATION) {
|
||||
destinationBreadcrumbs.push({ text: 'Update destination', href: '/' });
|
||||
}
|
||||
return destinationBreadcrumbs;
|
||||
default:
|
||||
// TODO::Everything else is considered as monitor, we should break this.
|
||||
let monitorName = base;
|
||||
try {
|
||||
const response = await httpClient.get(`../api/alerting/monitors/${base}`);
|
||||
if (response.ok) {
|
||||
monitorName = response.resp.name;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
const breadcrumbs = [{ text: monitorName, href: `/monitors/${base}` }];
|
||||
if (action === MONITOR_ACTIONS.UPDATE_MONITOR)
|
||||
breadcrumbs.push({ text: 'Update monitor', href: '/' });
|
||||
if (action === TRIGGER_ACTIONS.CREATE_TRIGGER)
|
||||
breadcrumbs.push({ text: 'Create trigger', href: '/' });
|
||||
if (action === TRIGGER_ACTIONS.UPDATE_TRIGGER)
|
||||
breadcrumbs.push({ text: 'Update trigger', href: '/' });
|
||||
return breadcrumbs;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'#': { text: 'Alerting', href: '/' },
|
||||
monitors: { text: 'Monitors', href: '/monitors' },
|
||||
dashboard: { text: 'Dashboard', href: '/dashboard' },
|
||||
destinations: { text: 'Destinations', href: '/destinations' },
|
||||
'create-monitor': [
|
||||
{ text: 'Monitors', href: '/monitors' },
|
||||
{ text: 'Create monitor', href: APP_PATH.CREATE_MONITOR },
|
||||
],
|
||||
'create-destination': [
|
||||
{ text: 'Destinations', href: '/destinations' },
|
||||
{ text: 'Create destination', href: APP_PATH.CREATE_DESTINATION },
|
||||
],
|
||||
}[base];
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { EuiBreadcrumbs } from '@elastic/eui';
|
||||
|
||||
import Breadcrumbs, {
|
||||
createEuiBreadcrumb,
|
||||
getBreadcrumbs,
|
||||
parseLocationHash,
|
||||
getBreadcrumb,
|
||||
} from './Breadcrumbs';
|
||||
import { historyMock, httpClientMock } from '../../../test/mocks';
|
||||
import { MONITOR_ACTIONS, TRIGGER_ACTIONS } from '../../utils/constants';
|
||||
|
||||
const monitorId = 'soDk30SjdsekoaSoMcj1';
|
||||
const location = {
|
||||
hash: '',
|
||||
pathname: '/monitors/random_id_20_chars__',
|
||||
search: '',
|
||||
state: undefined,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Breadcrumbs', () => {
|
||||
const title = 'Alerting';
|
||||
httpClientMock.get = jest.fn().mockResolvedValue({ ok: true, resp: { name: 'random monitor' } });
|
||||
delete global.window.location;
|
||||
global.window.location = { hash: '' };
|
||||
|
||||
test('renders', () => {
|
||||
const wrapper = shallow(
|
||||
<Breadcrumbs
|
||||
title={title}
|
||||
location={location}
|
||||
httpClient={httpClientMock}
|
||||
history={historyMock}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('calls getBreadcrumbs on mount and when pathname+search are updated', () => {
|
||||
const getBreadcrumbs = jest.spyOn(Breadcrumbs.prototype, 'getBreadcrumbs');
|
||||
const wrapper = mount(
|
||||
<Breadcrumbs
|
||||
title={title}
|
||||
location={location}
|
||||
httpClient={httpClientMock}
|
||||
history={historyMock}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getBreadcrumbs).toHaveBeenCalledTimes(1);
|
||||
wrapper.setProps({ location: { ...location, search: '?search=new' } });
|
||||
expect(getBreadcrumbs).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createEuiBreadcrumbs', () => {
|
||||
test('creates breadcrumbs for EuiBreadcrumbs', () => {
|
||||
const breadcrumb = { text: 'This is a breadcrumb', href: '/this-is-the-href' };
|
||||
expect(createEuiBreadcrumb(breadcrumb, historyMock)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseLocationHash', () => {
|
||||
test('correctly parses location hash', () => {
|
||||
const hash = `#/monitors/${monitorId}?action=${TRIGGER_ACTIONS.CREATE_TRIGGER}`;
|
||||
expect(parseLocationHash(hash)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('filters out falsy string values', () => {
|
||||
const hash = '#/monitors/';
|
||||
expect(parseLocationHash(hash)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBreadcrumb', () => {
|
||||
const routeState = { destinationToEdit: { name: 'unique_name' } };
|
||||
test('returns null if falsy base value', async () => {
|
||||
expect(await getBreadcrumb('', {}, httpClientMock)).toBe(null);
|
||||
expect(
|
||||
await getBreadcrumb(`?action=${TRIGGER_ACTIONS.CREATE_TRIGGER}`, {}, httpClientMock)
|
||||
).toBe(null);
|
||||
});
|
||||
|
||||
test('returns correct constant breadcrumbs', async () => {
|
||||
expect(await getBreadcrumb('#', {}, httpClientMock)).toMatchSnapshot();
|
||||
expect(await getBreadcrumb('monitors', {}, httpClientMock)).toMatchSnapshot();
|
||||
expect(await getBreadcrumb('dashboard', {}, httpClientMock)).toMatchSnapshot();
|
||||
expect(await getBreadcrumb('destinations', {}, httpClientMock)).toMatchSnapshot();
|
||||
expect(await getBreadcrumb('create-monitor', {}, httpClientMock)).toMatchSnapshot();
|
||||
expect(await getBreadcrumb('create-destination', {}, httpClientMock)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('when matching document IDs', () => {
|
||||
test('calls get monitor route', async () => {
|
||||
httpClientMock.get.mockResolvedValue({ ok: true, resp: { name: 'random_name' } });
|
||||
await getBreadcrumb(monitorId, {}, httpClientMock);
|
||||
expect(httpClientMock.get).toHaveBeenCalled();
|
||||
expect(httpClientMock.get).toHaveBeenCalledWith(`../api/alerting/monitors/${monitorId}`);
|
||||
});
|
||||
|
||||
test('returns monitor name', async () => {
|
||||
httpClientMock.get.mockResolvedValue({ ok: true, resp: { name: 'random_name' } });
|
||||
expect(await getBreadcrumb(monitorId, {}, httpClientMock)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('uses monitor id as name if request fails', async () => {
|
||||
httpClientMock.get.mockRejectedValue({ ok: true, resp: { name: 'random_name' } });
|
||||
expect(await getBreadcrumb(monitorId, {}, httpClientMock)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('uses monitor id as name if ok=false', async () => {
|
||||
httpClientMock.get.mockResolvedValue({ ok: false, resp: { name: 'random_name' } });
|
||||
expect(await getBreadcrumb(monitorId, {}, httpClientMock)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('adds appropriate action breadcrumb', async () => {
|
||||
httpClientMock.get.mockResolvedValue({ ok: true, resp: { name: 'random_name' } });
|
||||
expect(
|
||||
await getBreadcrumb(
|
||||
`${monitorId}?action=${MONITOR_ACTIONS.UPDATE_MONITOR}`,
|
||||
{},
|
||||
httpClientMock
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
await getBreadcrumb(
|
||||
`${monitorId}?action=${TRIGGER_ACTIONS.CREATE_TRIGGER}`,
|
||||
{},
|
||||
httpClientMock
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
await getBreadcrumb(
|
||||
`${monitorId}?action=${TRIGGER_ACTIONS.UPDATE_TRIGGER}`,
|
||||
{},
|
||||
httpClientMock
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,157 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Breadcrumbs renders 1`] = `
|
||||
<EuiBreadcrumbs
|
||||
breadcrumbs={Array []}
|
||||
responsive={false}
|
||||
style={
|
||||
Object {
|
||||
"padding": "0px 15px",
|
||||
}
|
||||
}
|
||||
truncate={true}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`createEuiBreadcrumbs creates breadcrumbs for EuiBreadcrumbs 1`] = `
|
||||
Object {
|
||||
"href": "#/this-is-the-href",
|
||||
"onClick": [Function],
|
||||
"text": "This is a breadcrumb",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb returns correct constant breadcrumbs 1`] = `
|
||||
Object {
|
||||
"href": "/",
|
||||
"text": "Alerting",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb returns correct constant breadcrumbs 2`] = `
|
||||
Object {
|
||||
"href": "/monitors",
|
||||
"text": "Monitors",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb returns correct constant breadcrumbs 3`] = `
|
||||
Object {
|
||||
"href": "/dashboard",
|
||||
"text": "Dashboard",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb returns correct constant breadcrumbs 4`] = `
|
||||
Object {
|
||||
"href": "/destinations",
|
||||
"text": "Destinations",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb returns correct constant breadcrumbs 5`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/monitors",
|
||||
"text": "Monitors",
|
||||
},
|
||||
Object {
|
||||
"href": "/create-monitor",
|
||||
"text": "Create monitor",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb returns correct constant breadcrumbs 6`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/destinations",
|
||||
"text": "Destinations",
|
||||
},
|
||||
Object {
|
||||
"href": "/create-destination",
|
||||
"text": "Create destination",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb when matching document IDs adds appropriate action breadcrumb 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/monitors/soDk30SjdsekoaSoMcj1",
|
||||
"text": "random_name",
|
||||
},
|
||||
Object {
|
||||
"href": "/",
|
||||
"text": "Update monitor",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb when matching document IDs adds appropriate action breadcrumb 2`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/monitors/soDk30SjdsekoaSoMcj1",
|
||||
"text": "random_name",
|
||||
},
|
||||
Object {
|
||||
"href": "/",
|
||||
"text": "Create trigger",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb when matching document IDs adds appropriate action breadcrumb 3`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/monitors/soDk30SjdsekoaSoMcj1",
|
||||
"text": "random_name",
|
||||
},
|
||||
Object {
|
||||
"href": "/",
|
||||
"text": "Update trigger",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb when matching document IDs returns monitor name 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/monitors/soDk30SjdsekoaSoMcj1",
|
||||
"text": "random_name",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb when matching document IDs uses monitor id as name if ok=false 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/monitors/soDk30SjdsekoaSoMcj1",
|
||||
"text": "soDk30SjdsekoaSoMcj1",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getBreadcrumb when matching document IDs uses monitor id as name if request fails 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"href": "/monitors/soDk30SjdsekoaSoMcj1",
|
||||
"text": "soDk30SjdsekoaSoMcj1",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseLocationHash correctly parses location hash 1`] = `
|
||||
Array [
|
||||
"#",
|
||||
"monitors",
|
||||
"soDk30SjdsekoaSoMcj1?action=create-trigger",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseLocationHash filters out falsy string values 1`] = `
|
||||
Array [
|
||||
"#",
|
||||
"monitors",
|
||||
]
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import Breadcrumbs from './Breadcrumbs';
|
||||
|
||||
export default Breadcrumbs;
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
const ChartContainer = ({ children, style }) => (
|
||||
<div
|
||||
style={{
|
||||
borderRadius: '5px',
|
||||
padding: '10px',
|
||||
border: '1px solid #D9D9D9',
|
||||
height: '250px',
|
||||
width: '100%',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
ChartContainer.propTypes = {
|
||||
style: PropTypes.object,
|
||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf([PropTypes.node])]).isRequired,
|
||||
};
|
||||
ChartContainer.defaultPropTypes = {
|
||||
style: {},
|
||||
};
|
||||
|
||||
export { ChartContainer };
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file has been cloned and modified from the original source(https://github.com/uber/react-vis/blob/35c8950722e9ad60214399291eda731ac31af267/src/plot/highlight.js) due to its limitations.
|
||||
We should revert this to an Actual Highlight Component from react-vis when it is ready for use with
|
||||
Controlled Highlight, allow only Drag and not to rebrush.
|
||||
* Removes capability for brushing on Area
|
||||
* Adds capability for dragging on chart
|
||||
* Styles Component as per the our mocks
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { ScaleUtils, AbstractSeries } from 'react-vis';
|
||||
import { isEqual } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const getLocs = evt => {
|
||||
const targetBounding = evt.currentTarget.getBoundingClientRect();
|
||||
const offsetX = evt.clientX - targetBounding.left;
|
||||
const offsetY = evt.clientY - targetBounding.top;
|
||||
return {
|
||||
xLoc: offsetX,
|
||||
yLoc: offsetY,
|
||||
};
|
||||
};
|
||||
|
||||
class Highlight extends AbstractSeries {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dragging: false,
|
||||
brushArea: { bottom: 0, right: 0, left: 0, top: 0 },
|
||||
brushing: false,
|
||||
startLocX: 0,
|
||||
startLocY: 0,
|
||||
dragArea: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { highlightRef } = this.props;
|
||||
if (highlightRef) {
|
||||
highlightRef({
|
||||
setHighlightedArea: this.setHighlightedArea,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!isEqual(this.props, prevProps)) {
|
||||
this.setHighlightedArea({
|
||||
...this.props.highlightedArea,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_getDrawArea(xLoc, yLoc) {
|
||||
const { startLocX, startLocY } = this.state;
|
||||
const {
|
||||
enableX,
|
||||
enableY,
|
||||
highlightWidth,
|
||||
highlightHeight,
|
||||
innerWidth,
|
||||
innerHeight,
|
||||
marginLeft,
|
||||
marginRight,
|
||||
marginBottom,
|
||||
marginTop,
|
||||
} = this.props;
|
||||
const plotHeight = innerHeight + marginTop + marginBottom;
|
||||
const plotWidth = innerWidth + marginLeft + marginRight;
|
||||
const touchWidth = highlightWidth || plotWidth;
|
||||
const touchHeight = highlightHeight || plotHeight;
|
||||
|
||||
return {
|
||||
bottom: enableY ? Math.max(startLocY, yLoc) : touchHeight,
|
||||
right: enableX ? Math.max(startLocX, xLoc) : touchWidth,
|
||||
left: enableX ? Math.min(xLoc, startLocX) : 0,
|
||||
top: enableY ? Math.min(yLoc, startLocY) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
_getDragArea(xLoc, yLoc) {
|
||||
const { enableX, enableY } = this.props;
|
||||
const { startLocX, startLocY, dragArea } = this.state;
|
||||
|
||||
return {
|
||||
bottom: dragArea.bottom + (enableY ? yLoc - startLocY : 0),
|
||||
left: dragArea.left + (enableX ? xLoc - startLocX : 0),
|
||||
right: dragArea.right + (enableX ? xLoc - startLocX : 0),
|
||||
top: dragArea.top + (enableY ? yLoc - startLocY : 0),
|
||||
};
|
||||
}
|
||||
|
||||
_clickedOutsideDrag(xLoc, yLoc) {
|
||||
const { enableX, enableY, marginLeft, marginTop } = this.props;
|
||||
const {
|
||||
dragArea,
|
||||
brushArea: { left, right, top, bottom },
|
||||
} = this.state;
|
||||
const actualXLoc = xLoc + marginLeft;
|
||||
const actualYLoc = yLoc + marginTop;
|
||||
const clickedOutsideDragX = dragArea && (actualXLoc < left || actualXLoc > right);
|
||||
const clickedOutsideDragY = dragArea && (actualYLoc < top || actualYLoc > bottom);
|
||||
if (enableX && enableY) {
|
||||
return clickedOutsideDragX || clickedOutsideDragY;
|
||||
}
|
||||
if (enableX) {
|
||||
return clickedOutsideDragX;
|
||||
}
|
||||
if (enableY) {
|
||||
return clickedOutsideDragY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_convertAreaToCoordinates(brushArea) {
|
||||
// NOTE only continuous scales are supported for brushing/getting coordinates back
|
||||
const { enableX, enableY, marginLeft, marginTop } = this.props;
|
||||
const xScale = ScaleUtils.getAttributeScale(this.props, 'x');
|
||||
const yScale = ScaleUtils.getAttributeScale(this.props, 'y');
|
||||
|
||||
// Ensure that users wishes are being respected about which scales are evaluated
|
||||
// this is specifically enabled to ensure brushing on mixed categorical and linear
|
||||
// charts will run as expected
|
||||
|
||||
if (enableX && enableY) {
|
||||
return {
|
||||
bottom: yScale.invert(brushArea.bottom),
|
||||
left: xScale.invert(brushArea.left - marginLeft),
|
||||
right: xScale.invert(brushArea.right - marginLeft),
|
||||
top: yScale.invert(brushArea.top),
|
||||
};
|
||||
}
|
||||
|
||||
if (enableY) {
|
||||
return {
|
||||
bottom: yScale.invert(brushArea.bottom - marginTop),
|
||||
top: yScale.invert(brushArea.top - marginTop),
|
||||
};
|
||||
}
|
||||
|
||||
if (enableX) {
|
||||
return {
|
||||
left: xScale.invert(brushArea.left - marginLeft),
|
||||
right: xScale.invert(brushArea.right - marginLeft),
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
This is a public method which can be accessed via ref which will allow
|
||||
consumer to control Highlight Window.
|
||||
*/
|
||||
setHighlightedArea = highlightArea => {
|
||||
const {
|
||||
highlightHeight,
|
||||
highlightWidth,
|
||||
innerWidth,
|
||||
innerHeight,
|
||||
marginLeft,
|
||||
marginBottom,
|
||||
marginTop,
|
||||
marginRight,
|
||||
} = this.props;
|
||||
|
||||
const xScale = ScaleUtils.getAttributeScale(this.props, 'x');
|
||||
const yScale = ScaleUtils.getAttributeScale(this.props, 'y');
|
||||
|
||||
const plotHeight = innerHeight + marginTop + marginBottom;
|
||||
const plotWidth = innerWidth + marginLeft + marginRight;
|
||||
|
||||
let bottomPos = highlightHeight || plotHeight;
|
||||
if (highlightArea.bottom) {
|
||||
bottomPos = yScale(highlightArea.bottom) + marginTop;
|
||||
}
|
||||
let topPos = 0;
|
||||
if (highlightArea.top) {
|
||||
topPos = yScale(highlightArea.top) + marginTop;
|
||||
}
|
||||
let leftPos = 0 + marginLeft;
|
||||
if (highlightArea.left) {
|
||||
leftPos = xScale(highlightArea.left) + marginLeft;
|
||||
}
|
||||
let rightPos = highlightWidth || plotWidth;
|
||||
if (highlightArea.right) {
|
||||
rightPos = xScale(highlightArea.right) + marginLeft;
|
||||
}
|
||||
|
||||
const brushArea = {
|
||||
bottom: bottomPos,
|
||||
right: rightPos,
|
||||
left: leftPos,
|
||||
top: topPos,
|
||||
};
|
||||
this.setState({ brushArea, dragArea: brushArea });
|
||||
};
|
||||
|
||||
startBrushing(e) {
|
||||
e.preventDefault();
|
||||
const { onDragStart, drag } = this.props;
|
||||
const { dragArea } = this.state;
|
||||
const { xLoc, yLoc } = getLocs(e);
|
||||
const clickedOutsideDrag = this._clickedOutsideDrag(xLoc, yLoc);
|
||||
if (!clickedOutsideDrag && drag) {
|
||||
this.setState({
|
||||
dragging: true,
|
||||
brushArea: dragArea,
|
||||
brushing: false,
|
||||
startLocX: xLoc,
|
||||
startLocY: yLoc,
|
||||
});
|
||||
if (onDragStart) {
|
||||
onDragStart(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopBrushing(e) {
|
||||
const { brushing, dragging, brushArea } = this.state;
|
||||
// Quickly short-circuit if the user isn't brushing in our component
|
||||
if (!brushing && !dragging) {
|
||||
return;
|
||||
}
|
||||
const { onBrushEnd, onDragEnd, drag } = this.props;
|
||||
const noHorizontal = Math.abs(brushArea.right - brushArea.left) < 5;
|
||||
const noVertical = Math.abs(brushArea.top - brushArea.bottom) < 5;
|
||||
// Invoke the callback with null if the selected area was < 5px
|
||||
const isNulled = noVertical || noHorizontal;
|
||||
// Clear the draw area
|
||||
this.setState({
|
||||
brushing: false,
|
||||
dragging: false,
|
||||
brushArea: drag ? brushArea : { top: 0, right: 0, bottom: 0, left: 0 },
|
||||
startLocX: 0,
|
||||
startLocY: 0,
|
||||
dragArea: drag && !isNulled && brushArea,
|
||||
});
|
||||
|
||||
if (brushing && onBrushEnd) {
|
||||
onBrushEnd(!isNulled ? this._convertAreaToCoordinates(brushArea) : null);
|
||||
}
|
||||
|
||||
if (drag && onDragEnd) {
|
||||
onDragEnd(!isNulled ? this._convertAreaToCoordinates(brushArea) : null);
|
||||
}
|
||||
}
|
||||
|
||||
onBrush(e) {
|
||||
const { marginLeft, marginRight, innerWidth, onBrush, onDrag, drag } = this.props;
|
||||
const { brushing, dragging } = this.state;
|
||||
const { xLoc, yLoc } = getLocs(e);
|
||||
if (brushing) {
|
||||
const brushArea = this._getDrawArea(xLoc, yLoc);
|
||||
this.setState({ brushArea });
|
||||
if (onBrush) {
|
||||
onBrush(this._convertAreaToCoordinates(brushArea));
|
||||
}
|
||||
}
|
||||
|
||||
if (drag && dragging) {
|
||||
const brushArea = this._getDragArea(xLoc, yLoc);
|
||||
const rightBoundary = innerWidth + marginRight;
|
||||
const leftBoundary = marginLeft;
|
||||
if (brushArea.right <= rightBoundary && brushArea.left >= leftBoundary) {
|
||||
this.setState({ brushArea });
|
||||
if (onDrag) {
|
||||
onDrag(this._convertAreaToCoordinates(brushArea));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getHighlighterStyles() {
|
||||
const { isDarkMode } = this.props;
|
||||
if (isDarkMode) {
|
||||
return {
|
||||
fill: 'black',
|
||||
opacity: 0.1,
|
||||
};
|
||||
}
|
||||
return {
|
||||
fill: 'white',
|
||||
opacity: 0.8,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
color,
|
||||
className,
|
||||
highlightHeight,
|
||||
highlightWidth,
|
||||
highlightX,
|
||||
highlightY,
|
||||
innerWidth,
|
||||
innerHeight,
|
||||
marginLeft,
|
||||
marginRight,
|
||||
marginTop,
|
||||
marginBottom,
|
||||
opacity,
|
||||
} = this.props;
|
||||
const {
|
||||
brushArea: { left, right, top, bottom },
|
||||
} = this.state;
|
||||
|
||||
let leftPos = 0;
|
||||
|
||||
if (highlightX) {
|
||||
const xScale = ScaleUtils.getAttributeScale(this.props, 'x');
|
||||
leftPos = xScale(highlightX);
|
||||
}
|
||||
|
||||
let topPos = 0;
|
||||
if (highlightY) {
|
||||
const yScale = ScaleUtils.getAttributeScale(this.props, 'y');
|
||||
topPos = yScale(highlightY);
|
||||
}
|
||||
|
||||
const plotWidth = marginLeft + marginRight + innerWidth;
|
||||
const plotHeight = marginTop + marginBottom + innerHeight;
|
||||
const touchWidth = highlightWidth || plotWidth;
|
||||
const touchHeight = highlightHeight || plotHeight;
|
||||
|
||||
return (
|
||||
<g
|
||||
transform={`translate(${leftPos}, ${topPos})`}
|
||||
className={`${className} rv-highlight-container`}
|
||||
>
|
||||
{/* An overlay on a graph which allows dragging */}
|
||||
<rect
|
||||
className="rv-mouse-target"
|
||||
opacity="0"
|
||||
cursor="move"
|
||||
x={marginLeft}
|
||||
y={0}
|
||||
width={Math.max(touchWidth, 0)}
|
||||
height={Math.max(touchHeight, 0)}
|
||||
onMouseDown={e => this.startBrushing(e)}
|
||||
onMouseMove={e => this.onBrush(e)}
|
||||
onMouseUp={e => this.stopBrushing(e)}
|
||||
onMouseLeave={e => this.stopBrushing(e)}
|
||||
// preventDefault() so that mouse event emulation does not happen
|
||||
onTouchEnd={e => {
|
||||
e.preventDefault();
|
||||
this.stopBrushing(e);
|
||||
}}
|
||||
onTouchCancel={e => {
|
||||
e.preventDefault();
|
||||
this.stopBrushing(e);
|
||||
}}
|
||||
onContextMenu={e => e.preventDefault()}
|
||||
onContextMenuCapture={e => e.preventDefault()}
|
||||
/>
|
||||
|
||||
{/* Left side of overlay which allows us to give an opacity to cover */}
|
||||
<rect
|
||||
pointerEvents="none"
|
||||
className="rv-mouse-target"
|
||||
x={marginLeft}
|
||||
y="0"
|
||||
width={Math.max(left - marginLeft, 0)}
|
||||
height={Math.max(touchHeight, 0)}
|
||||
onMouseLeave={e => e.preventDefault()}
|
||||
{...this.getHighlighterStyles()}
|
||||
/>
|
||||
{/* A Center Highlighter */}
|
||||
<rect
|
||||
pointerEvents="none"
|
||||
opacity={opacity}
|
||||
fill={color}
|
||||
x={left}
|
||||
y={top}
|
||||
width={Math.max(0, right - left)}
|
||||
height={Math.max(0, bottom - top)}
|
||||
onMouseLeave={e => e.preventDefault()}
|
||||
/>
|
||||
{/* Right side of overlay which allows us to give an opacity to cover */}
|
||||
<rect
|
||||
pointerEvents="none"
|
||||
className="rv-mouse-target"
|
||||
x={right}
|
||||
y="0"
|
||||
width={Math.max(touchWidth - right, 0)}
|
||||
height={Math.max(touchHeight, 0)}
|
||||
onMouseLeave={e => e.preventDefault()}
|
||||
{...this.getHighlighterStyles()}
|
||||
/>
|
||||
|
||||
{/* Draws border lines on Highlighted area */}
|
||||
<g>
|
||||
<rect
|
||||
pointerEvents="none"
|
||||
x={left}
|
||||
y={top}
|
||||
width="1"
|
||||
height={Math.max(0, bottom - top)}
|
||||
fill="rgb(151,151,151)"
|
||||
stroke="none"
|
||||
onMouseLeave={e => e.preventDefault()}
|
||||
/>
|
||||
<rect
|
||||
pointerEvents="none"
|
||||
x={right}
|
||||
y={top}
|
||||
width="1"
|
||||
height={Math.max(0, bottom - top)}
|
||||
fill="rgb(151,151,151)"
|
||||
stroke="none"
|
||||
onMouseLeave={e => e.preventDefault()}
|
||||
/>
|
||||
<line
|
||||
pointerEvents="none"
|
||||
x1={left}
|
||||
x2={right}
|
||||
stroke="rgb(151,151,151)"
|
||||
strokeWidth={2}
|
||||
onMouseLeave={e => e.preventDefault()}
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Highlight.displayName = 'HighlightOverlay';
|
||||
|
||||
Highlight.defaultProps = {
|
||||
color: 'rgb(77, 182, 172)',
|
||||
className: '',
|
||||
enableX: true,
|
||||
enableY: true,
|
||||
highlightRef: null,
|
||||
opacity: 0.3,
|
||||
};
|
||||
|
||||
Highlight.propTypes = {
|
||||
...AbstractSeries.propTypes,
|
||||
enableX: PropTypes.bool,
|
||||
enableY: PropTypes.bool,
|
||||
highlightHeight: PropTypes.number,
|
||||
highlightWidth: PropTypes.number,
|
||||
highlightX: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
highlightY: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
highlightRef: PropTypes.func,
|
||||
onBrushStart: PropTypes.func,
|
||||
onDragStart: PropTypes.func,
|
||||
onBrush: PropTypes.func,
|
||||
onDrag: PropTypes.func,
|
||||
onBrushEnd: PropTypes.func,
|
||||
onDragEnd: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Highlight;
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiPanel, EuiTitle } from '@elastic/eui';
|
||||
|
||||
const ContentPanel = ({
|
||||
title = '',
|
||||
titleSize = 'l',
|
||||
bodyStyles = {},
|
||||
panelStyles = {},
|
||||
horizontalRuleClassName = '',
|
||||
actions,
|
||||
children,
|
||||
}) => (
|
||||
<EuiPanel style={{ paddingLeft: '0px', paddingRight: '0px', ...panelStyles }}>
|
||||
<EuiFlexGroup style={{ padding: '0px 10px' }} justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size={titleSize}>
|
||||
<h3>{title}</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
{Array.isArray(actions) ? (
|
||||
actions.map((action, idx) => <EuiFlexItem key={idx}>{action}</EuiFlexItem>)
|
||||
) : (
|
||||
<EuiFlexItem>{actions}</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiHorizontalRule margin="xs" className={horizontalRuleClassName} />
|
||||
|
||||
<div style={{ padding: '0px 10px', ...bodyStyles }}>{children}</div>
|
||||
</EuiPanel>
|
||||
);
|
||||
|
||||
ContentPanel.propTypes = {
|
||||
title: PropTypes.string,
|
||||
titleSize: PropTypes.string,
|
||||
bodyStyles: PropTypes.object,
|
||||
panelStyles: PropTypes.object,
|
||||
horizontalRuleClassName: PropTypes.string,
|
||||
actions: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf([PropTypes.node])]),
|
||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf([PropTypes.node])]).isRequired,
|
||||
};
|
||||
|
||||
export default ContentPanel;
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
|
||||
import ContentPanel from './ContentPanel';
|
||||
|
||||
describe('ContentPanel', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<ContentPanel title="Test Content Panel">
|
||||
<div>Test</div>
|
||||
</ContentPanel>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ContentPanel renders 1`] = `
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingMedium"
|
||||
style="padding-left:0px;padding-right:0px"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
style="padding:0px 10px"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem"
|
||||
>
|
||||
<h3
|
||||
class="euiTitle euiTitle--large"
|
||||
>
|
||||
Test Content Panel
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr
|
||||
class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginXSmall"
|
||||
/>
|
||||
<div
|
||||
style="padding:0px 10px"
|
||||
>
|
||||
<div>
|
||||
Test
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import ContentPanel from './ContentPanel';
|
||||
|
||||
export default ContentPanel;
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class DelayedLoader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
displayLoader: false,
|
||||
};
|
||||
if (typeof props.children !== 'function') {
|
||||
throw new Error('Children should be function');
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
if (this.props.isLoading) {
|
||||
this.setTimer();
|
||||
}
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
const { isLoading } = this.props;
|
||||
// Setting up the loader to be visible only when network is too slow
|
||||
if (isLoading !== prevProps.isLoading) {
|
||||
if (isLoading) {
|
||||
this.setTimer();
|
||||
} else {
|
||||
this.clearTimer();
|
||||
this.setState({ displayLoader: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.clearTimer();
|
||||
}
|
||||
|
||||
clearTimer = () => {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
setTimer = () => {
|
||||
this.timer = setTimeout(this.handleDisplayLoader, 1000);
|
||||
};
|
||||
|
||||
handleDisplayLoader = () => this.setState({ displayLoader: true });
|
||||
|
||||
render() {
|
||||
const { displayLoader } = this.state;
|
||||
return this.props.children(displayLoader);
|
||||
}
|
||||
}
|
||||
|
||||
DelayedLoader.propTypes = {
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
children: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default DelayedLoader;
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, mount } from 'enzyme';
|
||||
import DelayedLoader from '../DelayedLoader';
|
||||
|
||||
describe('<DelayedLoader/>', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
test('renders', () => {
|
||||
expect(
|
||||
render(
|
||||
<DelayedLoader isLoading={false}>
|
||||
{showLoader => <div style={{ opacity: showLoader ? '0.2' : '1' }} />}
|
||||
</DelayedLoader>
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should set Timer for 1 seconds if initial loading is true', () => {
|
||||
const wrapper = mount(
|
||||
<DelayedLoader isLoading={true}>
|
||||
{showLoader => <div style={{ opacity: showLoader ? '0.2' : '1' }} />}
|
||||
</DelayedLoader>
|
||||
);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
test('should clear Timer on componentWillUnmount if exists', () => {
|
||||
const wrapper = mount(
|
||||
<DelayedLoader isLoading={true}>
|
||||
{showLoader => <div style={{ opacity: showLoader ? '0.2' : '1' }} />}
|
||||
</DelayedLoader>
|
||||
);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
|
||||
wrapper.unmount();
|
||||
expect(clearTimeout).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should not show loader if data fetching is finished before threshold', () => {
|
||||
const wrapper = mount(
|
||||
<DelayedLoader isLoading={true}>
|
||||
{showLoader => <div style={{ opacity: showLoader ? '0.2' : '1' }} />}
|
||||
</DelayedLoader>
|
||||
);
|
||||
wrapper.setProps({ isLoading: false });
|
||||
expect(clearTimeout).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should show loader if data fetching takes more than threshold', () => {
|
||||
const wrapper = mount(
|
||||
<DelayedLoader isLoading={false}>
|
||||
{showLoader => <div style={{ opacity: showLoader ? '0.2' : '1' }} />}
|
||||
</DelayedLoader>
|
||||
);
|
||||
wrapper.setProps({ isLoading: true });
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should throw an error if children is not function', () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<DelayedLoader isLoading={false}>
|
||||
<div />
|
||||
</DelayedLoader>
|
||||
);
|
||||
}).toThrow('Children should be function');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<DelayedLoader/> renders 1`] = `
|
||||
<div
|
||||
style="opacity:1"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`<DelayedLoader/> should not show loader if data fetching is finished before threshold 1`] = `
|
||||
<DelayedLoader
|
||||
isLoading={false}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"opacity": "1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</DelayedLoader>
|
||||
`;
|
||||
|
||||
exports[`<DelayedLoader/> should set Timer for 1 seconds if initial loading is true 1`] = `
|
||||
<DelayedLoader
|
||||
isLoading={true}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"opacity": "1",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</DelayedLoader>
|
||||
`;
|
||||
|
||||
exports[`<DelayedLoader/> should show loader if data fetching takes more than threshold 1`] = `
|
||||
<DelayedLoader
|
||||
isLoading={true}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"opacity": "0.2",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</DelayedLoader>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import DelayedLoader from './DelayedLoader';
|
||||
|
||||
export default DelayedLoader;
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiFlyoutFooter } from '@elastic/eui';
|
||||
|
||||
import Flyouts from './flyouts';
|
||||
|
||||
const getFlyoutData = ({ type, payload }) => {
|
||||
const flyout = Flyouts[type];
|
||||
if (!flyout || typeof flyout !== 'function') return null;
|
||||
return flyout(payload);
|
||||
};
|
||||
|
||||
const propTypes = {
|
||||
flyout: PropTypes.object,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const Flyout = ({ flyout, onClose }) => {
|
||||
if (!flyout) return null;
|
||||
const flyoutData = getFlyoutData(flyout);
|
||||
if (!flyoutData) return null;
|
||||
const {
|
||||
flyoutProps = {},
|
||||
headerProps = {},
|
||||
bodyProps = {},
|
||||
footerProps = {},
|
||||
header = null,
|
||||
body = null,
|
||||
footer = null,
|
||||
} = flyoutData;
|
||||
|
||||
const flyoutHeader = header && <EuiFlyoutHeader {...headerProps}>{header}</EuiFlyoutHeader>;
|
||||
const flyoutBody = body && <EuiFlyoutBody {...bodyProps}>{body}</EuiFlyoutBody>;
|
||||
const flyoutFooter = footer && <EuiFlyoutFooter {...footerProps}>{footer}</EuiFlyoutFooter>;
|
||||
|
||||
return (
|
||||
<EuiFlyout onClose={onClose} {...flyoutProps}>
|
||||
{flyoutHeader}
|
||||
{flyoutBody}
|
||||
{flyoutFooter}
|
||||
</EuiFlyout>
|
||||
);
|
||||
};
|
||||
|
||||
Flyout.propTypes = propTypes;
|
||||
|
||||
export default Flyout;
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Flyout from './Flyout';
|
||||
import Flyouts from './flyouts';
|
||||
jest.unmock('./flyouts');
|
||||
|
||||
describe('Flyout', () => {
|
||||
test('renders', () => {
|
||||
const wrapper = shallow(
|
||||
<Flyout flyout={{ type: 'message', payload: null }} onClose={jest.fn()} />
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders null if no flyout', () => {
|
||||
const wrapper = shallow(
|
||||
<Flyout flyout={{ type: 'definitely no flyout', payload: null }} onClose={jest.fn()} />
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('defaults if bad flyout data', () => {
|
||||
Flyouts.message = jest.fn(() => ({}));
|
||||
const wrapper = shallow(
|
||||
<Flyout flyout={{ type: 'message', payload: null }} onClose={jest.fn()} />
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Flyout defaults if bad flyout data 1`] = `
|
||||
<EuiFlyout
|
||||
onClose={[MockFunction]}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`Flyout renders 1`] = `
|
||||
<EuiFlyout
|
||||
aria-labelledby="messageFlyout"
|
||||
maxWidth={500}
|
||||
onClose={[MockFunction]}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "25px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<h2>
|
||||
<strong>
|
||||
Message
|
||||
</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "14px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<p>
|
||||
You have access to a "ctx" variable in your painless scripts and action mustache templates.
|
||||
</p>
|
||||
<h3>
|
||||
Learn More
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<EuiLink
|
||||
href="https://mustache.github.io/mustache.5.html"
|
||||
target="_blank"
|
||||
>
|
||||
HTML Templates with Mustache.js
|
||||
</EuiLink>
|
||||
</li>
|
||||
</ul>
|
||||
</EuiText>
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
`;
|
||||
|
||||
exports[`Flyout renders null if no flyout 1`] = `""`;
|
|
@ -0,0 +1,182 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Flyouts.message generates message JSON 1`] = `
|
||||
Object {
|
||||
"body": <EuiText
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "14px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<p>
|
||||
You have access to a "ctx" variable in your painless scripts and action mustache templates.
|
||||
</p>
|
||||
<h3>
|
||||
Learn More
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<EuiLink
|
||||
href="https://mustache.github.io/mustache.5.html"
|
||||
target="_blank"
|
||||
>
|
||||
HTML Templates with Mustache.js
|
||||
</EuiLink>
|
||||
</li>
|
||||
</ul>
|
||||
</EuiText>,
|
||||
"flyoutProps": Object {
|
||||
"aria-labelledby": "messageFlyout",
|
||||
"maxWidth": 500,
|
||||
"size": "m",
|
||||
},
|
||||
"header": <EuiTitle
|
||||
size="m"
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "25px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<h2>
|
||||
<strong>
|
||||
Message
|
||||
</strong>
|
||||
</h2>
|
||||
</EuiTitle>,
|
||||
"headerProps": Object {
|
||||
"hasBorder": true,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Flyouts.messageFrequency generates message JSON 1`] = `
|
||||
Object {
|
||||
"body": <EuiText
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "14px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<p>
|
||||
Specify message frequency to limit the number of notifications you receive within a given span of time. This setting is especially useful for low severity trigger conditions.
|
||||
</p>
|
||||
<p>
|
||||
Consider the following example:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
A trigger condition is met.
|
||||
</li>
|
||||
<li>
|
||||
The monitor sends a message
|
||||
</li>
|
||||
<li>
|
||||
Message frequency is set to 10 minutes.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
For the next 10 minutes, even if a trigger condition is met dozens of times, the monitor sends no additional messages. If the trigger condition is met 11 minutes later, the monitor sends another message.
|
||||
</p>
|
||||
</EuiText>,
|
||||
"flyoutProps": Object {
|
||||
"aria-labelledby": "messageFrequencyFlyout",
|
||||
"maxWidth": 500,
|
||||
"size": "m",
|
||||
},
|
||||
"header": <EuiTitle
|
||||
size="m"
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "25px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<h2>
|
||||
<strong>
|
||||
Message frequency
|
||||
</strong>
|
||||
</h2>
|
||||
</EuiTitle>,
|
||||
"headerProps": Object {
|
||||
"hasBorder": true,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Flyouts.triggerCondition generates message JSON 1`] = `
|
||||
Object {
|
||||
"body": <div>
|
||||
<EuiText
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "14px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<p>
|
||||
You have access to a "ctx" variable in your painless scripts
|
||||
</p>
|
||||
<p>
|
||||
Below shows a quick JSON example of what's available under the "ctx" variable along with the actual results (where possible) for you to reference.
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiCodeBlock
|
||||
language="json"
|
||||
>
|
||||
{
|
||||
"monitor": "...",
|
||||
"trigger": "...",
|
||||
"results": "...",
|
||||
"periodStart": "...",
|
||||
"periodEnd": "...",
|
||||
"alert": "...",
|
||||
"error": "..."
|
||||
}
|
||||
</EuiCodeBlock>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiCodeEditor
|
||||
height="700px"
|
||||
mode="json"
|
||||
readOnly={true}
|
||||
setOptions={
|
||||
Object {
|
||||
"fontSize": "12px",
|
||||
}
|
||||
}
|
||||
theme="github"
|
||||
value="{}"
|
||||
width="100%"
|
||||
/>
|
||||
</div>,
|
||||
"flyoutProps": Object {
|
||||
"aria-labelledby": "triggerConditionFlyout",
|
||||
"maxWidth": 500,
|
||||
"size": "m",
|
||||
},
|
||||
"header": <EuiTitle
|
||||
size="m"
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "25px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<h2>
|
||||
<strong>
|
||||
Trigger condition
|
||||
</strong>
|
||||
</h2>
|
||||
</EuiTitle>,
|
||||
"headerProps": Object {
|
||||
"hasBorder": true,
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import Flyouts from './index';
|
||||
|
||||
describe('Flyouts.message', () => {
|
||||
test('generates message JSON', () => {
|
||||
const json = Flyouts.message();
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flyouts.messageFrequency', () => {
|
||||
test('generates message JSON', () => {
|
||||
const json = Flyouts.messageFrequency();
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flyouts.triggerCondition', () => {
|
||||
test('generates message JSON', () => {
|
||||
const json = Flyouts.triggerCondition({});
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import message from './message';
|
||||
import messageFrequency from './messageFrequency';
|
||||
import triggerCondition from './triggerCondition';
|
||||
|
||||
const Flyouts = {
|
||||
messageFrequency,
|
||||
message,
|
||||
triggerCondition,
|
||||
};
|
||||
|
||||
export default Flyouts;
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiLink, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { URL } from '../../../utils/constants';
|
||||
|
||||
const message = () => ({
|
||||
flyoutProps: {
|
||||
'aria-labelledby': 'messageFlyout',
|
||||
maxWidth: 500,
|
||||
size: 'm',
|
||||
},
|
||||
headerProps: { hasBorder: true },
|
||||
header: (
|
||||
<EuiTitle size="m" style={{ fontSize: '25px' }}>
|
||||
<h2>
|
||||
<strong>Message</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
),
|
||||
body: (
|
||||
<EuiText style={{ fontSize: '14px' }}>
|
||||
<p>
|
||||
{`You have access to a "ctx" variable in your painless scripts and action mustache templates.`}
|
||||
</p>
|
||||
<h3>Learn More</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<EuiLink target="_blank" href={URL.MUSTACHE}>
|
||||
HTML Templates with Mustache.js
|
||||
</EuiLink>
|
||||
</li>
|
||||
</ul>
|
||||
</EuiText>
|
||||
),
|
||||
});
|
||||
|
||||
export default message;
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiText, EuiTitle } from '@elastic/eui';
|
||||
|
||||
const messageFrequency = () => ({
|
||||
flyoutProps: {
|
||||
'aria-labelledby': 'messageFrequencyFlyout',
|
||||
maxWidth: 500,
|
||||
size: 'm',
|
||||
},
|
||||
headerProps: { hasBorder: true },
|
||||
header: (
|
||||
<EuiTitle size="m" style={{ fontSize: '25px' }}>
|
||||
<h2>
|
||||
<strong>Message frequency</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
),
|
||||
body: (
|
||||
<EuiText style={{ fontSize: '14px' }}>
|
||||
<p>
|
||||
Specify message frequency to limit the number of notifications you receive within a given
|
||||
span of time. This setting is especially useful for low severity trigger conditions.
|
||||
</p>
|
||||
<p>Consider the following example:</p>
|
||||
<ul>
|
||||
<li>A trigger condition is met.</li>
|
||||
<li>The monitor sends a message</li>
|
||||
<li>Message frequency is set to 10 minutes.</li>
|
||||
</ul>
|
||||
<p>
|
||||
For the next 10 minutes, even if a trigger condition is met dozens of times, the monitor
|
||||
sends no additional messages. If the trigger condition is met 11 minutes later, the monitor
|
||||
sends another message.
|
||||
</p>
|
||||
</EuiText>
|
||||
),
|
||||
});
|
||||
|
||||
export default messageFrequency;
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiCodeBlock, EuiCodeEditor, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
|
||||
const CONTEXT_VARIABLES = JSON.stringify(
|
||||
{
|
||||
monitor: '...',
|
||||
trigger: '...',
|
||||
results: '...',
|
||||
periodStart: '...',
|
||||
periodEnd: '...',
|
||||
alert: '...',
|
||||
error: '...',
|
||||
},
|
||||
null,
|
||||
4
|
||||
);
|
||||
|
||||
const triggerCondition = context => ({
|
||||
flyoutProps: {
|
||||
'aria-labelledby': 'triggerConditionFlyout',
|
||||
maxWidth: 500,
|
||||
size: 'm',
|
||||
},
|
||||
headerProps: { hasBorder: true },
|
||||
header: (
|
||||
<EuiTitle size="m" style={{ fontSize: '25px' }}>
|
||||
<h2>
|
||||
<strong>Trigger condition</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
),
|
||||
body: (
|
||||
<div>
|
||||
<EuiText style={{ fontSize: '14px' }}>
|
||||
<p>You have access to a "ctx" variable in your painless scripts</p>
|
||||
<p>
|
||||
Below shows a quick JSON example of what's available under the "ctx" variable along with
|
||||
the actual results (where possible) for you to reference.
|
||||
</p>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiCodeBlock language="json">{CONTEXT_VARIABLES}</EuiCodeBlock>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
theme="github"
|
||||
width="100%"
|
||||
height="700px"
|
||||
readOnly
|
||||
value={JSON.stringify(context, null, 4)}
|
||||
setOptions={{ fontSize: '12px' }}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
||||
export default triggerCondition;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import Flyout from './Flyout';
|
||||
|
||||
export default Flyout;
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiCheckbox } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikCheckbox = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<FieldCheckbox name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<FieldCheckbox name={name} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldCheckbox = ({ name, field: { value, ...rest }, inputProps }) => (
|
||||
<EuiCheckbox name={name} id={name} checked={value} {...inputProps} {...rest} />
|
||||
);
|
||||
|
||||
FormikCheckbox.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
FieldCheckbox.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikCheckbox;
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikCheckbox from './FormikCheckbox';
|
||||
|
||||
describe('FormikCheckbox', () => {
|
||||
test('render formRow', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikCheckbox name="testing" formRow />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('render', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikCheckbox name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikCheckbox render 1`] = `
|
||||
<div
|
||||
class="euiCheckbox euiCheckbox--noLabel"
|
||||
>
|
||||
<input
|
||||
class="euiCheckbox__input"
|
||||
id="testing"
|
||||
name="testing"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="euiCheckbox__square"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`FormikCheckbox render formRow 1`] = `
|
||||
<div
|
||||
class="euiFormRow"
|
||||
id="testing-form-row-row"
|
||||
>
|
||||
<div
|
||||
class="euiFormRow__fieldWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiCheckbox euiCheckbox--noLabel"
|
||||
>
|
||||
<input
|
||||
class="euiCheckbox__input"
|
||||
id="testing"
|
||||
name="testing"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="euiCheckbox__square"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikCheckbox from './FormikCheckbox';
|
||||
|
||||
export default FormikCheckbox;
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiCodeEditor } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikCodeEditor = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<CodeEditor name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<CodeEditor name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const CodeEditor = ({ name, form, field, inputProps: { onBlur, onChange, ...rest } }) => (
|
||||
<EuiCodeEditor
|
||||
id={name}
|
||||
onChange={string => {
|
||||
onChange(string, field, form);
|
||||
}}
|
||||
onBlur={e => {
|
||||
onBlur(e, field, form);
|
||||
}}
|
||||
value={field.value}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikCodeEditor.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
CodeEditor.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikCodeEditor;
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikCodeEditor from './FormikCodeEditor';
|
||||
|
||||
// FIXME: This has an issue where EuiCodeEditor is generating a random HTML id and failing snapshot test
|
||||
|
||||
describe.skip('FormikCodeEditor', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikCodeEditor name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikCodeEditor from './FormikCodeEditor';
|
||||
|
||||
export default FormikCodeEditor;
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiComboBox } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikComboBox = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<ComboBox name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<ComboBox name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const ComboBox = ({
|
||||
name,
|
||||
form,
|
||||
field,
|
||||
inputProps: { onBlur, onChange, onCreateOption, ...rest },
|
||||
}) => (
|
||||
<EuiComboBox
|
||||
name={name}
|
||||
id={name}
|
||||
onChange={
|
||||
typeof onChange === 'function'
|
||||
? options => {
|
||||
onChange(options, field, form);
|
||||
}
|
||||
: onChange
|
||||
}
|
||||
onCreateOption={
|
||||
typeof onCreateOption === 'function'
|
||||
? value => {
|
||||
onCreateOption(value, field, form);
|
||||
}
|
||||
: onCreateOption
|
||||
}
|
||||
onBlur={
|
||||
typeof onBlur === 'function'
|
||||
? e => {
|
||||
onBlur(e, field, form);
|
||||
}
|
||||
: onBlur
|
||||
}
|
||||
selectedOptions={field.value}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikComboBox.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
ComboBox.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikComboBox;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikComboBox from './FormikComboBox';
|
||||
|
||||
describe.skip('FormikComboBox', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikComboBox name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikComboBox from './FormikComboBox';
|
||||
|
||||
export default FormikComboBox;
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFieldNumber } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikFieldNumber = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<FieldNumber name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<FieldNumber name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldNumber = ({ name, form, field, inputProps: { onChange, isInvalid, ...rest } }) => (
|
||||
<EuiFieldNumber
|
||||
{...field}
|
||||
{...rest}
|
||||
onChange={e => (typeof onChange === 'function' ? onChange(e, field, form) : field.onChange(e))}
|
||||
isInvalid={typeof isInvalid === 'function' ? isInvalid(name, form) : isInvalid}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikFieldNumber.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
FieldNumber.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikFieldNumber;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikFieldNumber from './FormikFieldNumber';
|
||||
|
||||
describe('FormikFieldNumber', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikFieldNumber name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikFieldNumber renders 1`] = `
|
||||
<div
|
||||
class="euiFormControlLayout"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<input
|
||||
class="euiFieldNumber"
|
||||
name="testing"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikFieldNumber from './FormikFieldNumber';
|
||||
|
||||
export default FormikFieldNumber;
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFieldPassword } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikFieldPassword = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<FieldPassword name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<FieldPassword name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldPassword = ({
|
||||
name,
|
||||
form,
|
||||
field,
|
||||
inputProps: { onChange, isInvalid, onFocus, ...rest },
|
||||
}) => (
|
||||
<EuiFieldPassword
|
||||
{...field}
|
||||
{...rest}
|
||||
onChange={e => (typeof onChange === 'function' ? onChange(e, field, form) : field.onChange(e))}
|
||||
onFocus={typeof onFocus === 'function' ? e => onFocus(e, field, form) : onFocus}
|
||||
isInvalid={typeof isInvalid === 'function' ? isInvalid(name, form) : isInvalid}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikFieldPassword.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
FieldPassword.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikFieldPassword;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikFieldPassword from './FormikFieldPassword';
|
||||
|
||||
describe('FormikFieldPassword', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikFieldPassword name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikFieldPassword renders 1`] = `
|
||||
<div
|
||||
class="euiFormControlLayout"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<input
|
||||
class="euiFieldPassword"
|
||||
name="testing"
|
||||
type="password"
|
||||
/>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons"
|
||||
>
|
||||
<span
|
||||
class="euiFormControlLayoutCustomIcon"
|
||||
>
|
||||
<div>
|
||||
EuiIconMock
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikFieldPassword from './FormikFieldPassword';
|
||||
|
||||
export default FormikFieldPassword;
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiRadio } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikFieldRadio = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<FieldRadio name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<FieldRadio name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldRadio = ({ name, form, field, inputProps: { onChange, ...rest } }) => (
|
||||
<EuiRadio
|
||||
{...field}
|
||||
{...rest}
|
||||
onChange={e => (typeof onChange === 'function' ? onChange(e, field, form) : field.onChange(e))}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikFieldRadio.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
FieldRadio.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikFieldRadio;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikFieldRadio from './FormikFieldRadio';
|
||||
|
||||
export default FormikFieldRadio;
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFieldText } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikFieldText = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<FieldText name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<FieldText name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldText = ({
|
||||
name,
|
||||
form,
|
||||
field,
|
||||
inputProps: { onChange, isInvalid, onFocus, ...rest },
|
||||
}) => (
|
||||
<EuiFieldText
|
||||
{...field}
|
||||
{...rest}
|
||||
onChange={e => (typeof onChange === 'function' ? onChange(e, field, form) : field.onChange(e))}
|
||||
onFocus={typeof onFocus === 'function' ? e => onFocus(e, field, form) : onFocus}
|
||||
isInvalid={typeof isInvalid === 'function' ? isInvalid(name, form) : isInvalid}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikFieldText.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
FieldText.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikFieldText;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikFieldText from './FormikFieldText';
|
||||
|
||||
describe('FormikFieldText', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikFieldText name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikFieldText renders 1`] = `
|
||||
<div
|
||||
class="euiFormControlLayout"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<input
|
||||
class="euiFieldText"
|
||||
name="testing"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikFieldText from './FormikFieldText';
|
||||
|
||||
export default FormikFieldText;
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiFormRow } from '@elastic/eui';
|
||||
|
||||
const FormikFormRow = ({ children, form, name, rowProps: { isInvalid, error, ...rest } }) => (
|
||||
<EuiFormRow
|
||||
id={`${name}-form-row`}
|
||||
isInvalid={typeof isInvalid === 'function' ? isInvalid(name, form) : isInvalid}
|
||||
error={typeof error === 'function' ? error(name, form) : error}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</EuiFormRow>
|
||||
);
|
||||
|
||||
FormikFormRow.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
rowProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default FormikFormRow;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
|
||||
import FormikFormRow from './FormikFormRow';
|
||||
|
||||
describe('FormikFormRow', () => {
|
||||
const Child = () => <div id="mine">child</div>;
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<FormikFormRow name="testing" rowProps={{}} form={{}}>
|
||||
<Child />
|
||||
</FormikFormRow>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikFormRow renders 1`] = `
|
||||
<div
|
||||
class="euiFormRow"
|
||||
id="testing-form-row-row"
|
||||
>
|
||||
<div
|
||||
class="euiFormRow__fieldWrapper"
|
||||
>
|
||||
<div
|
||||
id="mine"
|
||||
>
|
||||
child
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikFormRow from './FormikFormRow';
|
||||
|
||||
export default FormikFormRow;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field } from 'formik';
|
||||
|
||||
const FormikInputWrapper = ({ name, fieldProps, render }) => (
|
||||
<Field name={name} {...fieldProps}>
|
||||
{({ field, form }) => render({ field, form })}
|
||||
</Field>
|
||||
);
|
||||
|
||||
FormikInputWrapper.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
fieldProps: PropTypes.object.isRequired,
|
||||
render: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FormikInputWrapper;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikInputWrapper from './FormikInputWrapper';
|
||||
|
||||
describe('FormikInputWrapper', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikInputWrapper name="testing" fieldProps={{}} render={() => <div>test</div>} />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikInputWrapper renders 1`] = `
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikInputWrapper from './FormikInputWrapper';
|
||||
|
||||
export default FormikInputWrapper;
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiSelect } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikSelect = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<FieldSelect name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<FieldSelect name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldSelect = ({ name, field, form, inputProps: { onChange, isInvalid, ...rest } }) => (
|
||||
<EuiSelect
|
||||
name={name}
|
||||
id={name}
|
||||
{...field}
|
||||
{...rest}
|
||||
onChange={e => (typeof onChange === 'function' ? onChange(e, field, form) : field.onChange(e))}
|
||||
isInvalid={typeof isInvalid === 'function' ? isInvalid(name, form) : isInvalid}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikSelect.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
FieldSelect.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikSelect;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikSelect from './FormikSelect';
|
||||
|
||||
describe('FormikSelect', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikSelect name="testing" inputProps={{ options: [{ value: 'test', text: 'test' }] }} />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikSelect renders 1`] = `
|
||||
<div
|
||||
class="euiFormControlLayout"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<select
|
||||
class="euiSelect"
|
||||
id="testing"
|
||||
name="testing"
|
||||
>
|
||||
<option
|
||||
value="test"
|
||||
>
|
||||
test
|
||||
</option>
|
||||
</select>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<span
|
||||
class="euiFormControlLayoutCustomIcon"
|
||||
>
|
||||
<div>
|
||||
EuiIconMock
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikSelect from './FormikSelect';
|
||||
|
||||
export default FormikSelect;
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiSwitch } from '@elastic/eui';
|
||||
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikSwitch = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<FieldSwitch name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<FieldSwitch name={name} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldSwitch = ({ name, field: { value, ...rest }, inputProps }) => (
|
||||
<EuiSwitch name={name} id={name} checked={value} {...inputProps} {...rest} />
|
||||
);
|
||||
|
||||
FormikSwitch.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
FieldSwitch.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikSwitch;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikSwitch from './FormikSwitch';
|
||||
|
||||
describe('FormikSwitch', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikSwitch name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikSwitch renders 1`] = `
|
||||
<div
|
||||
class="euiSwitch"
|
||||
>
|
||||
<button
|
||||
aria-checked="false"
|
||||
aria-labelledby="generated-id"
|
||||
class="euiSwitch__button"
|
||||
id="testing"
|
||||
name="testing"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiSwitch__body"
|
||||
>
|
||||
<span
|
||||
class="euiSwitch__thumb"
|
||||
/>
|
||||
<span
|
||||
class="euiSwitch__track"
|
||||
>
|
||||
<div>
|
||||
EuiIconMock
|
||||
</div>
|
||||
<div>
|
||||
EuiIconMock
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="euiSwitch__label"
|
||||
id="generated-id"
|
||||
/>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikSwitch from './FormikSwitch';
|
||||
|
||||
export default FormikSwitch;
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiTextArea } from '@elastic/eui';
|
||||
import FormikInputWrapper from '../FormikInputWrapper';
|
||||
import FormikFormRow from '../FormikFormRow';
|
||||
|
||||
const FormikTextArea = ({
|
||||
name,
|
||||
formRow = false,
|
||||
fieldProps = {},
|
||||
rowProps = {},
|
||||
inputProps = {},
|
||||
}) => (
|
||||
<FormikInputWrapper
|
||||
name={name}
|
||||
fieldProps={fieldProps}
|
||||
render={({ field, form }) =>
|
||||
formRow ? (
|
||||
<FormikFormRow name={name} form={form} rowProps={rowProps}>
|
||||
<TextArea name={name} form={form} field={field} inputProps={inputProps} />
|
||||
</FormikFormRow>
|
||||
) : (
|
||||
<TextArea name={name} form={form} field={field} inputProps={inputProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const TextArea = ({ name, form, field, inputProps: { isInvalid, ...rest } }) => (
|
||||
<EuiTextArea
|
||||
isInvalid={typeof isInvalid === 'function' ? isInvalid(name, form) : isInvalid}
|
||||
{...field}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
FormikTextArea.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
formRow: PropTypes.bool,
|
||||
fieldProps: PropTypes.object,
|
||||
rowProps: PropTypes.object,
|
||||
inputProps: PropTypes.object,
|
||||
};
|
||||
|
||||
TextArea.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
inputProps: PropTypes.object.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
field: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default FormikTextArea;
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import FormikTextArea from './FormikTextArea';
|
||||
|
||||
describe('FormikTextArea', () => {
|
||||
test('renders', () => {
|
||||
const component = (
|
||||
<Formik>
|
||||
<FormikTextArea name="testing" />
|
||||
</Formik>
|
||||
);
|
||||
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormikTextArea renders 1`] = `
|
||||
<textarea
|
||||
class="euiTextArea euiTextArea--resizeVertical"
|
||||
name="testing"
|
||||
rows="6"
|
||||
/>
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikTextArea from './FormikTextArea';
|
||||
|
||||
export default FormikTextArea;
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import FormikFieldText from './FormikFieldText';
|
||||
import FormikFormRow from './FormikFormRow';
|
||||
import FormikInputWrapper from './FormikInputWrapper';
|
||||
import FormikTextArea from './FormikTextArea';
|
||||
import FormikSwitch from './FormikSwitch';
|
||||
import FormikCheckbox from './FormikCheckbox';
|
||||
import FormikSelect from './FormikSelect';
|
||||
import FormikFieldNumber from './FormikFieldNumber';
|
||||
import FormikCodeEditor from './FormikCodeEditor';
|
||||
import FormikComboBox from './FormikComboBox';
|
||||
import FormikFieldPassword from './FormikFieldPassword';
|
||||
import FormikFieldRadio from './FormikFieldRadio';
|
||||
|
||||
export {
|
||||
FormikComboBox,
|
||||
FormikSwitch,
|
||||
FormikCheckbox,
|
||||
FormikSelect,
|
||||
FormikFieldNumber,
|
||||
FormikFieldText,
|
||||
FormikFormRow,
|
||||
FormikInputWrapper,
|
||||
FormikTextArea,
|
||||
FormikCodeEditor,
|
||||
FormikFieldPassword,
|
||||
FormikFieldRadio,
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiHorizontalRule, EuiText, EuiTitle } from '@elastic/eui';
|
||||
|
||||
const DEFAULT_PROPS = { size: 'xs', style: { paddingLeft: '10px' } };
|
||||
const SubHeader = ({
|
||||
description,
|
||||
descriptionProps = DEFAULT_PROPS,
|
||||
horizontalRuleMargin = 'xs',
|
||||
title,
|
||||
titleProps = DEFAULT_PROPS,
|
||||
}) => (
|
||||
<Fragment>
|
||||
<EuiTitle {...titleProps}>{title}</EuiTitle>
|
||||
<EuiHorizontalRule margin={horizontalRuleMargin} />
|
||||
<EuiText {...descriptionProps}>{description}</EuiText>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
SubHeader.propTypes = {
|
||||
description: PropTypes.node.isRequired,
|
||||
descriptionProps: PropTypes.object,
|
||||
horizontalRuleMargin: PropTypes.string,
|
||||
title: PropTypes.node.isRequired,
|
||||
titleProps: PropTypes.object,
|
||||
};
|
||||
|
||||
export default SubHeader;
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
|
||||
import SubHeader from './SubHeader';
|
||||
|
||||
describe('SubHeader', () => {
|
||||
test('renders', () => {
|
||||
const component = <SubHeader description={<div>description</div>} title={<div>title</div>} />;
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SubHeader renders 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="euiTitle euiTitle--xsmall"
|
||||
style="padding-left:10px"
|
||||
>
|
||||
title
|
||||
</div>,
|
||||
<hr
|
||||
class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginXSmall"
|
||||
/>,
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
style="padding-left:10px"
|
||||
>
|
||||
<div>
|
||||
description
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import SubHeader from './SubHeader';
|
||||
|
||||
export default SubHeader;
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
|
||||
<title>Alerting</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Alerting" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group-2" transform="translate(4.000000, 0.500000)">
|
||||
<g id="Group-13">
|
||||
<path d="M9.49121094,20.5 C9.49121094,19.1192881 8.37192281,18 6.99121094,18 C5.61049906,18 4.49121094,19.1192881 4.49121094,20.5" id="Oval-Copy-80" stroke="#007D73" transform="translate(6.991211, 19.250000) scale(1, -1) translate(-6.991211, -19.250000) "></path>
|
||||
<path d="M12.9912109,9.1 C12.9912109,6.0072054 10.3049194,3.5 6.99121094,3.5 C3.67750244,3.5 0.991210938,6.0072054 0.991210938,9.1 C0.991210938,12.8807292 0.660807292,15.6807292 0,17.5 L13.9875,17.5 C12.9912109,14.7215104 12.9912109,12.1927946 12.9912109,9.1 Z" id="Oval" stroke="#333741"></path>
|
||||
<circle id="Oval-Copy-67" stroke="#333741" cx="6.99121094" cy="2" r="1.5"></circle>
|
||||
</g>
|
||||
<path d="M2.5,14.5 L11.5,14.5" id="Line-9" stroke="#007D73" stroke-linecap="square"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,61 @@
|
|||
import Main from './pages/Main';
|
||||
import { CoreContext } from './utils/CoreContext';
|
||||
import {Fetch} from '../../components/kibana/core/public/http/fetch';
|
||||
import {Router} from 'react-router-dom';
|
||||
import {useMemo} from 'react';
|
||||
import {ScopedHistory} from '../../components/kibana/core/public/application/scoped_history';
|
||||
import {notification} from 'antd';
|
||||
import {connect} from 'dva'
|
||||
|
||||
const httpClient = new Fetch({
|
||||
basePath:{
|
||||
get: () => '',
|
||||
prepend: (url) => url,
|
||||
remove: (url) => url,
|
||||
serverBasePath: '/elasticsearch',
|
||||
}
|
||||
});
|
||||
const notifications = {
|
||||
toasts: {
|
||||
addDanger: ({title, text, toastLifeTimeMs})=>{
|
||||
notification.warning({
|
||||
message: title,
|
||||
description: text,
|
||||
duration: toastLifeTimeMs/1000,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AlertingUI = (props)=>{
|
||||
if(!props.selectedCluster.id){
|
||||
return null;
|
||||
}
|
||||
useMemo(()=>{
|
||||
httpClient.getServerBasePath = ()=>{
|
||||
return '/api/elasticsearch/'+ props.selectedCluster.id;
|
||||
}
|
||||
}, [props.selectedCluster]);
|
||||
const isDarkMode = false;
|
||||
const history = useMemo(()=>{
|
||||
return new ScopedHistory(props.history, '/alerting');
|
||||
}, [props.history])
|
||||
|
||||
return (
|
||||
<CoreContext.Provider
|
||||
value={{ http: httpClient, isDarkMode, notifications: notifications }}
|
||||
>
|
||||
<Router history={history}>
|
||||
<div style={{background:'#fff'}}>
|
||||
<Main title="Alerting" {...props} />
|
||||
</div>
|
||||
</Router>
|
||||
</CoreContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default connect(({
|
||||
global
|
||||
})=>({
|
||||
selectedCluster: global.selectedCluster,
|
||||
}))(AlertingUI)
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
.container {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.accordion-horizontal-rule {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.accordion-action {
|
||||
padding: 10px 0px;
|
||||
margin-bottom: 10px;
|
||||
.euiAccordion__button {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.accordion-action:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.accordion-separator {
|
||||
background-image: linear-gradient(#D3DAE6, #D3DAE6);
|
||||
background-size: 100% 1px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.modal-manage-email {
|
||||
min-width: 800px;
|
||||
height: 75vh;
|
||||
max-height: 1000px;
|
||||
}
|
||||
|
||||
.euiTextArea.read-only-text-area {
|
||||
padding-left: 12px;
|
||||
border-color: initial;
|
||||
box-shadow: 0 1px 1px -1px rgba(153, 153, 153, 0.2), 0 3px 2px -2px rgba(153, 153, 153, 0.2),
|
||||
inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Kibana itself has an old version of EUI stylesheets that is being applied to our upgraded EUI library.
|
||||
* Our own EUI dependency is overwriting the Kibana EUI dependency stylesheets, but any CSS properties that aren't
|
||||
* included in the local dependency aren't overwritten, so some extra styles are sometimes applied such as this case:
|
||||
* .euiSwitch .euiSwitch__thumb has a width/height set at 24px in older version which breaks the styling of the newer version.
|
||||
* */
|
||||
|
||||
.euiFormRow.euiFormRow--hasEmptyLabelSpace {
|
||||
padding-top: initial;
|
||||
}
|
||||
|
||||
.euiModal--confirmation {
|
||||
width: initial;
|
||||
}
|
||||
|
||||
.euiModal {
|
||||
padding: initial;
|
||||
}
|
||||
|
||||
.euiRangePicker--popper {
|
||||
left: unset !important;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.euiDatePickerRange__icon {
|
||||
flex: 0 !important;
|
||||
}
|
||||
|
||||
.euiPaginationButton-isActive:focus {
|
||||
color: #0079a5 !important;
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import {
|
||||
Chart,
|
||||
Axis,
|
||||
LineSeries,
|
||||
niceTimeFormatter,
|
||||
Settings,
|
||||
Position,
|
||||
LineAnnotation,
|
||||
} from '@elastic/charts';
|
||||
import { ChartContainer } from '../../../../../components/ChartContainer/ChartContainer';
|
||||
import DelayedLoader from '../../../../../components/DelayedLoader';
|
||||
|
||||
export const MAX_DATA_POINTS = 1000;
|
||||
|
||||
const getAxisTitle = (displayGrade, displayConfidence) => {
|
||||
if (displayGrade && displayConfidence) {
|
||||
return 'Anomaly grade / confidence';
|
||||
}
|
||||
return displayGrade ? 'Anomaly grade' : 'Anomaly confidence';
|
||||
};
|
||||
|
||||
/**
|
||||
* In case of too many anomalies (e.g., high-cardinality detectors), we only keep the max anomalies within
|
||||
* allowed range. Otherwise, return data as they are.
|
||||
* @param {any[]} data The original anomaly result from preview
|
||||
* @returns {any[]} anomalies within allowed range
|
||||
*/
|
||||
export const prepareDataForChart = (data) => {
|
||||
if (data && data.length > MAX_DATA_POINTS) {
|
||||
return sampleMaxAnomalyGrade(data);
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Caclulate the stride between each step
|
||||
* @param {number} total Total number of preview results
|
||||
* @returns {number} The stride
|
||||
*/
|
||||
const calculateStep = (total) => {
|
||||
return Math.ceil(total / MAX_DATA_POINTS);
|
||||
};
|
||||
|
||||
/**
|
||||
* Pick the elememtn with the max anomaly grade within the input array
|
||||
* @param {any[]} anomalies Input array with preview results
|
||||
* @returns The elememtn with the max anomaly grade
|
||||
*/
|
||||
const findAnomalyWithMaxAnomalyGrade = (anomalies) => {
|
||||
let anomalyWithMaxGrade = anomalies[0];
|
||||
for (let i = 1; i < anomalies.length; i++) {
|
||||
if (anomalies[i].anomalyGrade > anomalyWithMaxGrade.anomalyGrade) {
|
||||
anomalyWithMaxGrade = anomalies[i];
|
||||
}
|
||||
}
|
||||
return anomalyWithMaxGrade;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sample max anomalies within allowed range
|
||||
* @param {any[]} data The original results from preview
|
||||
* @return {any[]} sampled anomalies
|
||||
*/
|
||||
const sampleMaxAnomalyGrade = (data) => {
|
||||
let sortedData = data.sort((a, b) => (a.plotTime > b.plotTime ? 1 : -1));
|
||||
const step = calculateStep(sortedData.length);
|
||||
let index = 0;
|
||||
const sampledAnomalies = [];
|
||||
while (index < sortedData.length) {
|
||||
const arr = sortedData.slice(index, index + step);
|
||||
sampledAnomalies.push(findAnomalyWithMaxAnomalyGrade(arr));
|
||||
index = index + step;
|
||||
}
|
||||
return sampledAnomalies;
|
||||
};
|
||||
|
||||
const AnomaliesChart = (props) => {
|
||||
const timeFormatter = niceTimeFormatter([props.startDateTime, props.endDateTime]);
|
||||
const preparedAnomalies = prepareDataForChart(props.anomalies);
|
||||
|
||||
return (
|
||||
<DelayedLoader isLoading={props.isLoading}>
|
||||
{(showLoader) => (
|
||||
<React.Fragment>
|
||||
{props.showTitle ? (
|
||||
<EuiText size="xs">
|
||||
<strong>{props.title}</strong>
|
||||
</EuiText>
|
||||
) : null}
|
||||
<EuiSpacer size="s" />
|
||||
<div>
|
||||
<ChartContainer
|
||||
style={{ height: '300px', width: '100%', opacity: showLoader ? 0.2 : 1 }}
|
||||
>
|
||||
<Chart>
|
||||
{props.showSettings ? (
|
||||
<Settings
|
||||
showLegend
|
||||
legendPosition={Position.Bottom}
|
||||
showLegendDisplayValue={false}
|
||||
/>
|
||||
) : null}
|
||||
<Axis id="bottom" position="bottom" tickFormat={timeFormatter} />
|
||||
<Axis
|
||||
id="left"
|
||||
title={getAxisTitle(props.displayGrade, props.displayConfidence)}
|
||||
position="left"
|
||||
domain={{ min: 0, max: 1 }}
|
||||
/>
|
||||
{props.annotationData ? (
|
||||
<LineAnnotation
|
||||
annotationId="anomalyAnnotation"
|
||||
domainType="yDomain"
|
||||
dataValues={props.annotationData}
|
||||
style={{
|
||||
line: {
|
||||
strokeWidth: 1.5,
|
||||
stroke: '#f00',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{props.displayGrade ? (
|
||||
<LineSeries
|
||||
id="Anomaly grade"
|
||||
xScaleType="time"
|
||||
yScaleType="linear"
|
||||
xAccessor={'plotTime'}
|
||||
yAccessors={['anomalyGrade']}
|
||||
data={preparedAnomalies}
|
||||
/>
|
||||
) : null}
|
||||
{props.displayConfidence ? (
|
||||
<LineSeries
|
||||
id="Confidence"
|
||||
xScaleType="time"
|
||||
yScaleType="linear"
|
||||
xAccessor={'plotTime'}
|
||||
yAccessors={['confidence']}
|
||||
data={preparedAnomalies}
|
||||
/>
|
||||
) : null}
|
||||
</Chart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</DelayedLoader>
|
||||
);
|
||||
};
|
||||
|
||||
AnomaliesChart.propTypes = {
|
||||
startDateTime: PropTypes.number,
|
||||
endDateTime: PropTypes.number,
|
||||
endDate: PropTypes.number,
|
||||
isLoading: PropTypes.bool,
|
||||
showTitle: PropTypes.bool,
|
||||
showSettings: PropTypes.bool,
|
||||
annotationData: PropTypes.array,
|
||||
anomalies: PropTypes.array.isRequired,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
|
||||
AnomaliesChart.defaultProps = {
|
||||
isLoading: false,
|
||||
showTitle: true,
|
||||
showSettings: true,
|
||||
anomalies: undefined,
|
||||
title: 'Sample preview for anomaly score',
|
||||
};
|
||||
|
||||
export { AnomaliesChart };
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import { AnomaliesChart, MAX_DATA_POINTS, prepareDataForChart } from './AnomaliesChart';
|
||||
|
||||
const startTime = moment('2018-10-25T09:30:00').valueOf();
|
||||
const endTime = moment('2018-10-29T09:30:00').valueOf();
|
||||
|
||||
const getRandomArbitrary = (min, max) => {
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate preview results
|
||||
* @param {Number} startTime Preview start time in epoch milliseconds
|
||||
* @param {Number} endTime Preview end time in epoch milliseconds
|
||||
* @param {Number} count Number of results
|
||||
* @returns {any[]} Generated results
|
||||
*/
|
||||
const createTestData = (startTime, endTime, count) => {
|
||||
const data = [];
|
||||
const interval = 60000;
|
||||
const midInterval = interval / 2;
|
||||
for (let i = 0; i < count - 3; i++) {
|
||||
let startGenerated = getRandomArbitrary(startTime, endTime);
|
||||
data.push({
|
||||
anomalyGrade: 0,
|
||||
confidence: 0,
|
||||
dataEndTime: startGenerated + interval,
|
||||
dataStartTime: startGenerated,
|
||||
detectorId: 'nxEuT3YBdrEXnzbxJ7XZ',
|
||||
plotTime: startGenerated + midInterval,
|
||||
schemaVersion: 0,
|
||||
});
|
||||
}
|
||||
// injected 3 anomalies: the beginning, the end, and the middle.
|
||||
data.push({
|
||||
anomalyGrade: 1,
|
||||
confidence: 0.7,
|
||||
dataEndTime: startTime + interval,
|
||||
dataStartTime: startTime,
|
||||
detectorId: 'nxEuT3YBdrEXnzbxJ7XZ',
|
||||
plotTime: startTime + midInterval,
|
||||
schemaVersion: 0,
|
||||
});
|
||||
|
||||
data.push({
|
||||
anomalyGrade: 0.9,
|
||||
confidence: 0.8,
|
||||
dataEndTime: endTime,
|
||||
dataStartTime: endTime - interval,
|
||||
detectorId: 'nxEuT3YBdrEXnzbxJ7XZ',
|
||||
plotTime: endTime - interval + midInterval,
|
||||
schemaVersion: 0,
|
||||
});
|
||||
|
||||
let mid = startTime + (endTime - startTime) / 2;
|
||||
data.push({
|
||||
anomalyGrade: 0.7,
|
||||
confidence: 0.9,
|
||||
dataEndTime: mid + interval,
|
||||
dataStartTime: mid,
|
||||
detectorId: 'nxEuT3YBdrEXnzbxJ7XZ',
|
||||
plotTime: mid + midInterval,
|
||||
schemaVersion: 0,
|
||||
});
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
describe('AnomaliesChart', () => {
|
||||
test('renders ', () => {
|
||||
const sampleData = [
|
||||
{
|
||||
anomalyGrade: 0.9983687181753063,
|
||||
anomalyScore: 0.8381447468893426,
|
||||
confidence: 0.42865659282252266,
|
||||
detectorId: 'temp',
|
||||
endTime: 1569097677667,
|
||||
plotTime: 1569097377667,
|
||||
startTime: 1569097077667,
|
||||
},
|
||||
];
|
||||
const component = (
|
||||
<AnomaliesChart
|
||||
anomalies={sampleData}
|
||||
startDateTime={startTime}
|
||||
endDateTime={endTime}
|
||||
isLoading={false}
|
||||
displayGrade
|
||||
displayConfidence
|
||||
showTitle
|
||||
/>
|
||||
);
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('hc detector trigger definition', () => {
|
||||
let startTime = 1608327992253;
|
||||
let endTime = 1608759992253;
|
||||
const preparedAnomalies = prepareDataForChart(
|
||||
createTestData(startTime, endTime, MAX_DATA_POINTS * 30)
|
||||
);
|
||||
|
||||
expect(preparedAnomalies.length).toBeCloseTo(MAX_DATA_POINTS);
|
||||
|
||||
expect(preparedAnomalies[MAX_DATA_POINTS - 1].anomalyGrade).toBeCloseTo(0.9);
|
||||
expect(preparedAnomalies[MAX_DATA_POINTS - 1].confidence).toBeCloseTo(0.8);
|
||||
|
||||
var anomalyNumber = 0;
|
||||
for (let i = 0; i < MAX_DATA_POINTS; i++) {
|
||||
if (preparedAnomalies[i].anomalyGrade > 0) {
|
||||
anomalyNumber++;
|
||||
// we injected an anomaly in the middle. Due to randomness, we cannot predict which one it is.
|
||||
if (i > 0 && i < MAX_DATA_POINTS - 1) {
|
||||
expect(preparedAnomalies[i].anomalyGrade).toBeCloseTo(0.7);
|
||||
expect(preparedAnomalies[i].confidence).toBeCloseTo(0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
// injected 3 anomalies
|
||||
expect(anomalyNumber).toBe(3);
|
||||
});
|
||||
|
||||
test('single-stream detector trigger definition', () => {
|
||||
let startTime = 1608327992253;
|
||||
let endTime = 1608759992253;
|
||||
|
||||
let originalPreviewResults = createTestData(startTime, endTime, MAX_DATA_POINTS);
|
||||
// we only consolidate and reduce original data when the input data size is larger than MAX_DATA_POINTS
|
||||
expect(prepareDataForChart(originalPreviewResults)).toBe(originalPreviewResults);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AnomaliesChart renders 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
>
|
||||
<strong>
|
||||
Sample preview for anomaly score
|
||||
</strong>
|
||||
</div>,
|
||||
<div
|
||||
class="euiSpacer euiSpacer--s"
|
||||
/>,
|
||||
<div>
|
||||
<div
|
||||
style="border-radius:5px;padding:10px;border:1px solid #D9D9D9;height:300px;width:100%;opacity:1"
|
||||
>
|
||||
<div
|
||||
class="echChart"
|
||||
>
|
||||
<div
|
||||
class="echChartBackground"
|
||||
style="background-color:transparent"
|
||||
/>
|
||||
<div
|
||||
class="echChartStatus"
|
||||
data-ech-render-complete="false"
|
||||
data-ech-render-count="0"
|
||||
/>
|
||||
<div
|
||||
class="echChartResizer"
|
||||
/>
|
||||
<div
|
||||
class="echContainer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
export { AnomaliesChart } from './AnomaliesChart';
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiEmptyPrompt, EuiButton, EuiText, EuiLoadingChart } from '@elastic/eui';
|
||||
import { KIBANA_AD_PLUGIN } from '../../../../../utils/constants';
|
||||
|
||||
const EmptyFeaturesMessage = props => (
|
||||
<div
|
||||
style={{
|
||||
borderRadius: '5px',
|
||||
padding: '10px',
|
||||
border: '1px solid #D9D9D9',
|
||||
height: '250px',
|
||||
width: '100%',
|
||||
...props.containerStyle,
|
||||
}}
|
||||
>
|
||||
{props.isLoading ? (
|
||||
<EuiEmptyPrompt style={{ maxWidth: '45em' }} body={<EuiLoadingChart size="xl" />} />
|
||||
) : (
|
||||
<EuiEmptyPrompt
|
||||
style={{ maxWidth: '45em' }}
|
||||
body={
|
||||
<EuiText>
|
||||
No features have been added to this anomaly detector. A feature is a metric that is used
|
||||
for anomaly detection. A detector can discover anomalies across one or more features.
|
||||
</EuiText>
|
||||
}
|
||||
actions={[
|
||||
<EuiButton
|
||||
data-test-subj="createButton"
|
||||
href={`${KIBANA_AD_PLUGIN}#/detectors/${props.detectorId}/features`}
|
||||
target="_blank"
|
||||
>
|
||||
Add Feature
|
||||
</EuiButton>,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
EmptyFeaturesMessage.propTypes = {
|
||||
detectorId: PropTypes.string,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
containerStyle: PropTypes.object,
|
||||
};
|
||||
EmptyFeaturesMessage.defaultProps = {
|
||||
detectorId: '',
|
||||
containerStyle: {},
|
||||
};
|
||||
|
||||
export { EmptyFeaturesMessage };
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
import { EmptyFeaturesMessage } from './EmptyFeaturesMessage';
|
||||
|
||||
describe('EmptyFeaturesMessage', () => {
|
||||
test('renders ', () => {
|
||||
const component = <EmptyFeaturesMessage detectorId="tempId" />;
|
||||
expect(render(component)).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EmptyFeaturesMessage renders 1`] = `
|
||||
<div
|
||||
style="border-radius:5px;padding:10px;border:1px solid #D9D9D9;height:250px;width:100%"
|
||||
>
|
||||
<div
|
||||
class="euiEmptyPrompt"
|
||||
style="max-width:45em"
|
||||
>
|
||||
<span
|
||||
class="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--medium"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--medium"
|
||||
>
|
||||
No features have been added to this anomaly detector. A feature is a metric that is used for anomaly detection. A detector can discover anomalies across one or more features.
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--l"
|
||||
/>
|
||||
<div
|
||||
class="euiSpacer euiSpacer--s"
|
||||
/>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterMedium euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentCenter euiFlexGroup--directionColumn euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<a
|
||||
class="euiButton euiButton--primary"
|
||||
data-test-subj="createButton"
|
||||
href="opendistro-anomaly-detection-kibana#/detectors/tempId/features"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Add Feature
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
* A copy of the License is located at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* or in the "license" file accompanying this file. This file is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||||
* express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Chart, Axis, LineSeries, RectAnnotation, niceTimeFormatter } from '@elastic/charts';
|
||||
import { EuiPagination, EuiText, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import DelayedLoader from '../../../../../components/DelayedLoader';
|
||||
import { ChartContainer } from '../../../../../components/ChartContainer/ChartContainer';
|
||||
|
||||
class FeatureChart extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activePage: 0,
|
||||
};
|
||||
this.goToPage = this.goToPage.bind(this);
|
||||
}
|
||||
goToPage(pageNumber) {
|
||||
this.setState({
|
||||
activePage: pageNumber,
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const { isLoading, startDateTime, endDateTime, featureData, annotations, title } = this.props;
|
||||
const currentFeature = featureData[this.state.activePage];
|
||||
const timeFormatter = niceTimeFormatter([startDateTime, endDateTime]);
|
||||
return (
|
||||
<DelayedLoader isLoading={isLoading}>
|
||||
{showLoader => {
|
||||
return currentFeature ? (
|
||||
<React.Fragment>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
<strong>{title}</strong>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPagination
|
||||
pageCount={featureData.length}
|
||||
activePage={this.state.activePage}
|
||||
onPageClick={this.goToPage}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<ChartContainer style={showLoader ? { opacity: 0.2 } : {}}>
|
||||
<Chart>
|
||||
<RectAnnotation
|
||||
dataValues={annotations || []}
|
||||
annotationId="react"
|
||||
style={{
|
||||
stroke: '#FCAAAA',
|
||||
strokeWidth: 1.5,
|
||||
fill: '#FCAAAA',
|
||||
}}
|
||||
/>
|
||||
<Axis id="left" title={currentFeature.featureName} position="left" />
|
||||
<Axis id="bottom" position="bottom" tickFormat={timeFormatter} />
|
||||
<LineSeries
|
||||
id="lines"
|
||||
xScaleType="time"
|
||||
yScaleType="linear"
|
||||
xAccessor={'startTime'}
|
||||
yAccessors={['data']}
|
||||
data={currentFeature.data}
|
||||
/>
|
||||
</Chart>
|
||||
</ChartContainer>
|
||||
</React.Fragment>
|
||||
) : null;
|
||||
}}
|
||||
</DelayedLoader>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FeatureChart.propTypes = {
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
startDateTime: PropTypes.number.isRequired,
|
||||
endDateTime: PropTypes.number.isRequired,
|
||||
featureData: PropTypes.array.isRequired,
|
||||
annotations: PropTypes.array,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
|
||||
FeatureChart.defaultProps = {
|
||||
title: 'Sample feature data',
|
||||
};
|
||||
|
||||
export { FeatureChart };
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue