mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge remote-tracking branch 'upstream/master' into EUIfication/data_table
This commit is contained in:
commit
a057da9dfd
350 changed files with 6478 additions and 3418 deletions
|
@ -19,5 +19,6 @@ export interface RouteConfigOptions<Method extends RouteMethod>
|
|||
| [authRequired](./kibana-plugin-core-server.routeconfigoptions.authrequired.md) | <code>boolean | 'optional'</code> | Defines authentication mode for a route: - true. A user has to have valid credentials to access a resource - false. A user can access a resource without any credentials. - 'optional'. A user can access a resource if has valid credentials or no credentials at all. Can be useful when we grant access to a resource but want to identify a user if possible.<!-- -->Defaults to <code>true</code> if an auth mechanism is registered. |
|
||||
| [body](./kibana-plugin-core-server.routeconfigoptions.body.md) | <code>Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody</code> | Additional body options [RouteConfigOptionsBody](./kibana-plugin-core-server.routeconfigoptionsbody.md)<!-- -->. |
|
||||
| [tags](./kibana-plugin-core-server.routeconfigoptions.tags.md) | <code>readonly string[]</code> | Additional metadata tag strings to attach to the route. |
|
||||
| [timeout](./kibana-plugin-core-server.routeconfigoptions.timeout.md) | <code>number</code> | Timeouts for processing durations. Response timeout is in milliseconds. Default value: 2 minutes |
|
||||
| [xsrfRequired](./kibana-plugin-core-server.routeconfigoptions.xsrfrequired.md) | <code>Method extends 'get' ? never : boolean</code> | Defines xsrf protection requirements for a route: - true. Requires an incoming POST/PUT/DELETE request to contain <code>kbn-xsrf</code> header. - false. Disables xsrf protection.<!-- -->Set to true by default |
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [RouteConfigOptions](./kibana-plugin-core-server.routeconfigoptions.md) > [timeout](./kibana-plugin-core-server.routeconfigoptions.timeout.md)
|
||||
|
||||
## RouteConfigOptions.timeout property
|
||||
|
||||
Timeouts for processing durations. Response timeout is in milliseconds. Default value: 2 minutes
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
timeout?: number;
|
||||
```
|
|
@ -9,9 +9,10 @@ Helper to setup two-way syncing of global data and a state container
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, queryString, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'queryString' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
time?: boolean;
|
||||
refreshInterval?: boolean;
|
||||
filters?: FilterStateStore | boolean;
|
||||
query?: boolean;
|
||||
}) => () => void
|
||||
```
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface QueryState
|
|||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [filters](./kibana-plugin-plugins-data-public.querystate.filters.md) | <code>Filter[]</code> | |
|
||||
| [query](./kibana-plugin-plugins-data-public.querystate.query.md) | <code>Query</code> | |
|
||||
| [refreshInterval](./kibana-plugin-plugins-data-public.querystate.refreshinterval.md) | <code>RefreshInterval</code> | |
|
||||
| [time](./kibana-plugin-plugins-data-public.querystate.time.md) | <code>TimeRange</code> | |
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryState](./kibana-plugin-plugins-data-public.querystate.md) > [query](./kibana-plugin-plugins-data-public.querystate.query.md)
|
||||
|
||||
## QueryState.query property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
query?: Query;
|
||||
```
|
|
@ -9,7 +9,7 @@ Helper to setup syncing of global data with the URL
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'queryString' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
stop: () => void;
|
||||
hasInheritedQueryFromUrl: boolean;
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ These can be used to automatically update the list of hosts as a cluster is resi
|
|||
Kibana has a default maximum memory limit of 1.4 GB, and in most cases, we recommend leaving this unconfigured. In some scenarios, such as large reporting jobs,
|
||||
it may make sense to tweak limits to meet more specific requirements.
|
||||
|
||||
You can modify this limit by setting `--max-old-space-size` in the `node.options` config file that can be found inside `kibana/config` folder or any other configured with the environment variable `KIBANA_PATH_CONF` (for example in debian based system would be `/etc/kibana`).
|
||||
You can modify this limit by setting `--max-old-space-size` in the `node.options` config file that can be found inside `kibana/config` folder or any other configured with the environment variable `KBN_PATH_CONF` (for example in debian based system would be `/etc/kibana`).
|
||||
|
||||
The option accepts a limit in MB:
|
||||
--------
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { History } from 'history';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
|
||||
import { Router } from 'react-router-dom';
|
||||
|
@ -85,16 +85,9 @@ const App = ({ navigation, data, history, kbnUrlStateStorage }: StateDemoAppDeps
|
|||
useGlobalStateSyncing(data.query, kbnUrlStateStorage);
|
||||
useAppStateSyncing(appStateContainer, data.query, kbnUrlStateStorage);
|
||||
|
||||
const onQuerySubmit = useCallback(
|
||||
({ query }) => {
|
||||
appStateContainer.set({ ...appState, query });
|
||||
},
|
||||
[appStateContainer, appState]
|
||||
);
|
||||
|
||||
const indexPattern = useIndexPattern(data);
|
||||
if (!indexPattern)
|
||||
return <div>No index pattern found. Please create an intex patter before loading...</div>;
|
||||
return <div>No index pattern found. Please create an index patter before loading...</div>;
|
||||
|
||||
// Render the application DOM.
|
||||
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
|
||||
|
@ -107,8 +100,6 @@ const App = ({ navigation, data, history, kbnUrlStateStorage }: StateDemoAppDeps
|
|||
showSearchBar={true}
|
||||
indexPatterns={[indexPattern]}
|
||||
useDefaultBehaviors={true}
|
||||
onQuerySubmit={onQuerySubmit}
|
||||
query={appState.query}
|
||||
showSaveQuery={true}
|
||||
/>
|
||||
<EuiPage restrictWidth="1000px">
|
||||
|
@ -200,7 +191,7 @@ function useAppStateSyncing<AppState extends QueryState>(
|
|||
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(
|
||||
query,
|
||||
appStateContainer,
|
||||
{ filters: esFilters.FilterStateStore.APP_STATE }
|
||||
{ filters: esFilters.FilterStateStore.APP_STATE, query: true }
|
||||
);
|
||||
|
||||
// sets up syncing app state container with url
|
||||
|
|
|
@ -459,7 +459,7 @@
|
|||
"jest-cli": "^25.5.4",
|
||||
"jest-environment-jsdom-thirteen": "^1.0.1",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jimp": "^0.9.6",
|
||||
"jimp": "^0.14.0",
|
||||
"json5": "^1.0.1",
|
||||
"license-checker": "^16.0.0",
|
||||
"listr": "^0.14.1",
|
||||
|
|
|
@ -112,7 +112,7 @@ export class DocLinksService {
|
|||
kibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/index.html`,
|
||||
siem: {
|
||||
guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
|
||||
gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/install-siem.html`,
|
||||
gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
|
||||
},
|
||||
query: {
|
||||
luceneQuerySyntax: `${ELASTICSEARCH_DOCS}query-dsl-query-string-query.html#query-string-syntax`,
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('core deprecations', () => {
|
|||
const { messages } = applyCoreDeprecations();
|
||||
expect(messages).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Environment variable CONFIG_PATH is deprecated. It has been replaced with KIBANA_PATH_CONF pointing to a config folder",
|
||||
"Environment variable CONFIG_PATH is deprecated. It has been replaced with KBN_PATH_CONF pointing to a config folder",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ConfigDeprecationProvider, ConfigDeprecation } from './types';
|
|||
const configPathDeprecation: ConfigDeprecation = (settings, fromPath, log) => {
|
||||
if (has(process.env, 'CONFIG_PATH')) {
|
||||
log(
|
||||
`Environment variable CONFIG_PATH is deprecated. It has been replaced with KIBANA_PATH_CONF pointing to a config folder`
|
||||
`Environment variable CONFIG_PATH is deprecated. It has been replaced with KBN_PATH_CONF pointing to a config folder`
|
||||
);
|
||||
}
|
||||
return settings;
|
||||
|
|
|
@ -992,6 +992,133 @@ describe('body options', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('timeout options', () => {
|
||||
test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a POST', async () => {
|
||||
const { registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
const router = new Router('', logger, enhanceWithContext);
|
||||
router.post(
|
||||
{
|
||||
path: '/',
|
||||
validate: false,
|
||||
options: { timeout: 300000 },
|
||||
},
|
||||
(context, req, res) => {
|
||||
try {
|
||||
return res.ok({ body: { timeout: req.route.options.timeout } });
|
||||
} catch (err) {
|
||||
return res.internalError({ body: err.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
registerRouter(router);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).post('/').send({ test: 1 }).expect(200, {
|
||||
timeout: 300000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a GET', async () => {
|
||||
const { registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
const router = new Router('', logger, enhanceWithContext);
|
||||
router.get(
|
||||
{
|
||||
path: '/',
|
||||
validate: false,
|
||||
options: { timeout: 300000 },
|
||||
},
|
||||
(context, req, res) => {
|
||||
try {
|
||||
return res.ok({ body: { timeout: req.route.options.timeout } });
|
||||
} catch (err) {
|
||||
return res.internalError({ body: err.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
registerRouter(router);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).get('/').expect(200, {
|
||||
timeout: 300000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a DELETE', async () => {
|
||||
const { registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
const router = new Router('', logger, enhanceWithContext);
|
||||
router.delete(
|
||||
{
|
||||
path: '/',
|
||||
validate: false,
|
||||
options: { timeout: 300000 },
|
||||
},
|
||||
(context, req, res) => {
|
||||
try {
|
||||
return res.ok({ body: { timeout: req.route.options.timeout } });
|
||||
} catch (err) {
|
||||
return res.internalError({ body: err.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
registerRouter(router);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).delete('/').expect(200, {
|
||||
timeout: 300000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a PUT', async () => {
|
||||
const { registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
const router = new Router('', logger, enhanceWithContext);
|
||||
router.put(
|
||||
{
|
||||
path: '/',
|
||||
validate: false,
|
||||
options: { timeout: 300000 },
|
||||
},
|
||||
(context, req, res) => {
|
||||
try {
|
||||
return res.ok({ body: { timeout: req.route.options.timeout } });
|
||||
} catch (err) {
|
||||
return res.internalError({ body: err.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
registerRouter(router);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).put('/').expect(200, {
|
||||
timeout: 300000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should accept a socket "timeout" which is 3 minutes in milliseconds, "300000" for a PATCH', async () => {
|
||||
const { registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
const router = new Router('', logger, enhanceWithContext);
|
||||
router.patch(
|
||||
{
|
||||
path: '/',
|
||||
validate: false,
|
||||
options: { timeout: 300000 },
|
||||
},
|
||||
(context, req, res) => {
|
||||
try {
|
||||
return res.ok({ body: { timeout: req.route.options.timeout } });
|
||||
} catch (err) {
|
||||
return res.internalError({ body: err.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
registerRouter(router);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).patch('/').expect(200, {
|
||||
timeout: 300000,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should return a stream in the body', async () => {
|
||||
const { registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
|
|
|
@ -161,8 +161,10 @@ export class HttpServer {
|
|||
this.log.debug(`registering route handler for [${route.path}]`);
|
||||
// Hapi does not allow payload validation to be specified for 'head' or 'get' requests
|
||||
const validate = isSafeMethod(route.method) ? undefined : { payload: true };
|
||||
const { authRequired, tags, body = {} } = route.options;
|
||||
const { authRequired, tags, body = {}, timeout } = route.options;
|
||||
const { accepts: allow, maxBytes, output, parse } = body;
|
||||
// Hapi does not allow timeouts on payloads to be specified for 'head' or 'get' requests
|
||||
const payloadTimeout = isSafeMethod(route.method) || timeout == null ? undefined : timeout;
|
||||
|
||||
const kibanaRouteState: KibanaRouteState = {
|
||||
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
|
||||
|
@ -181,9 +183,23 @@ export class HttpServer {
|
|||
// validation applied in ./http_tools#getServerOptions
|
||||
// (All NP routes are already required to specify their own validation in order to access the payload)
|
||||
validate,
|
||||
payload: [allow, maxBytes, output, parse].some((v) => typeof v !== 'undefined')
|
||||
? { allow, maxBytes, output, parse }
|
||||
payload: [allow, maxBytes, output, parse, payloadTimeout].some(
|
||||
(v) => typeof v !== 'undefined'
|
||||
)
|
||||
? {
|
||||
allow,
|
||||
maxBytes,
|
||||
output,
|
||||
parse,
|
||||
timeout: payloadTimeout,
|
||||
}
|
||||
: undefined,
|
||||
timeout:
|
||||
timeout != null
|
||||
? {
|
||||
socket: timeout + 1, // Hapi server requires the socket to be greater than payload settings so we add 1 millisecond
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -302,6 +302,130 @@ describe('Options', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('timeout', () => {
|
||||
it('should timeout if configured with a small timeout value for a POST', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.post(
|
||||
{ path: '/a', validate: false, options: { timeout: 1000 } },
|
||||
async (context, req, res) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
return res.ok({});
|
||||
}
|
||||
);
|
||||
router.post({ path: '/b', validate: false }, (context, req, res) => res.ok({}));
|
||||
await server.start();
|
||||
expect(supertest(innerServer.listener).post('/a')).rejects.toThrow('socket hang up');
|
||||
await supertest(innerServer.listener).post('/b').expect(200, {});
|
||||
});
|
||||
|
||||
it('should timeout if configured with a small timeout value for a PUT', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.put(
|
||||
{ path: '/a', validate: false, options: { timeout: 1000 } },
|
||||
async (context, req, res) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
return res.ok({});
|
||||
}
|
||||
);
|
||||
router.put({ path: '/b', validate: false }, (context, req, res) => res.ok({}));
|
||||
await server.start();
|
||||
|
||||
expect(supertest(innerServer.listener).put('/a')).rejects.toThrow('socket hang up');
|
||||
await supertest(innerServer.listener).put('/b').expect(200, {});
|
||||
});
|
||||
|
||||
it('should timeout if configured with a small timeout value for a DELETE', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.delete(
|
||||
{ path: '/a', validate: false, options: { timeout: 1000 } },
|
||||
async (context, req, res) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
return res.ok({});
|
||||
}
|
||||
);
|
||||
router.delete({ path: '/b', validate: false }, (context, req, res) => res.ok({}));
|
||||
await server.start();
|
||||
expect(supertest(innerServer.listener).delete('/a')).rejects.toThrow('socket hang up');
|
||||
await supertest(innerServer.listener).delete('/b').expect(200, {});
|
||||
});
|
||||
|
||||
it('should timeout if configured with a small timeout value for a GET', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.get(
|
||||
// Note: There is a bug within Hapi Server where it cannot set the payload timeout for a GET call but it also cannot configure a timeout less than the payload body
|
||||
// so the least amount of possible time to configure the timeout is 10 seconds.
|
||||
{ path: '/a', validate: false, options: { timeout: 100000 } },
|
||||
async (context, req, res) => {
|
||||
// Cause a wait of 20 seconds to cause the socket hangup
|
||||
await new Promise((resolve) => setTimeout(resolve, 200000));
|
||||
return res.ok({});
|
||||
}
|
||||
);
|
||||
router.get({ path: '/b', validate: false }, (context, req, res) => res.ok({}));
|
||||
await server.start();
|
||||
|
||||
expect(supertest(innerServer.listener).get('/a')).rejects.toThrow('socket hang up');
|
||||
await supertest(innerServer.listener).get('/b').expect(200, {});
|
||||
});
|
||||
|
||||
it('should not timeout if configured with a 5 minute timeout value for a POST', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.post(
|
||||
{ path: '/a', validate: false, options: { timeout: 300000 } },
|
||||
async (context, req, res) => res.ok({})
|
||||
);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).post('/a').expect(200, {});
|
||||
});
|
||||
|
||||
it('should not timeout if configured with a 5 minute timeout value for a PUT', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.put(
|
||||
{ path: '/a', validate: false, options: { timeout: 300000 } },
|
||||
async (context, req, res) => res.ok({})
|
||||
);
|
||||
await server.start();
|
||||
|
||||
await supertest(innerServer.listener).put('/a').expect(200, {});
|
||||
});
|
||||
|
||||
it('should not timeout if configured with a 5 minute timeout value for a DELETE', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.delete(
|
||||
{ path: '/a', validate: false, options: { timeout: 300000 } },
|
||||
async (context, req, res) => res.ok({})
|
||||
);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).delete('/a').expect(200, {});
|
||||
});
|
||||
|
||||
it('should not timeout if configured with a 5 minute timeout value for a GET', async () => {
|
||||
const { server: innerServer, createRouter } = await server.setup(setupDeps);
|
||||
const router = createRouter('/');
|
||||
|
||||
router.get(
|
||||
{ path: '/a', validate: false, options: { timeout: 300000 } },
|
||||
async (context, req, res) => res.ok({})
|
||||
);
|
||||
await server.start();
|
||||
await supertest(innerServer.listener).get('/a').expect(200, {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cache-Control', () => {
|
||||
|
|
|
@ -197,12 +197,14 @@ export class KibanaRequest<
|
|||
private getRouteInfo(request: Request): KibanaRequestRoute<Method> {
|
||||
const method = request.method as Method;
|
||||
const { parse, maxBytes, allow, output } = request.route.settings.payload || {};
|
||||
const timeout = request.route.settings.timeout?.socket;
|
||||
|
||||
const options = ({
|
||||
authRequired: this.getAuthRequired(request),
|
||||
// some places in LP call KibanaRequest.from(request) manually. remove fallback to true before v8
|
||||
xsrfRequired: (request.route.settings.app as KibanaRouteState)?.xsrfRequired ?? true,
|
||||
tags: request.route.settings.tags || [],
|
||||
timeout: typeof timeout === 'number' ? timeout - 1 : undefined, // We are forced to have the timeout be 1 millisecond greater than the server and payload so we subtract one here to give the user consist settings
|
||||
body: isSafeMethod(method)
|
||||
? undefined
|
||||
: {
|
||||
|
|
|
@ -144,6 +144,12 @@ export interface RouteConfigOptions<Method extends RouteMethod> {
|
|||
* Additional body options {@link RouteConfigOptionsBody}.
|
||||
*/
|
||||
body?: Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody;
|
||||
|
||||
/**
|
||||
* Timeouts for processing durations. Response timeout is in milliseconds.
|
||||
* Default value: 2 minutes
|
||||
*/
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,14 +25,18 @@ import { fromRoot } from '../utils';
|
|||
const isString = (v: any): v is string => typeof v === 'string';
|
||||
|
||||
const CONFIG_PATHS = [
|
||||
process.env.KBN_PATH_CONF && join(process.env.KBN_PATH_CONF, 'kibana.yml'),
|
||||
process.env.KIBANA_PATH_CONF && join(process.env.KIBANA_PATH_CONF, 'kibana.yml'),
|
||||
process.env.CONFIG_PATH, // deprecated
|
||||
fromRoot('config/kibana.yml'),
|
||||
].filter(isString);
|
||||
|
||||
const CONFIG_DIRECTORIES = [process.env.KIBANA_PATH_CONF, fromRoot('config'), '/etc/kibana'].filter(
|
||||
isString
|
||||
);
|
||||
const CONFIG_DIRECTORIES = [
|
||||
process.env.KBN_PATH_CONF,
|
||||
process.env.KIBANA_PATH_CONF,
|
||||
fromRoot('config'),
|
||||
'/etc/kibana',
|
||||
].filter(isString);
|
||||
|
||||
const DATA_PATHS = [
|
||||
process.env.DATA_PATH, // deprecated
|
||||
|
|
|
@ -1859,6 +1859,7 @@ export interface RouteConfigOptions<Method extends RouteMethod> {
|
|||
authRequired?: boolean | 'optional';
|
||||
body?: Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody;
|
||||
tags?: readonly string[];
|
||||
timeout?: number;
|
||||
xsrfRequired?: Method extends 'get' ? never : boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ while [ -h "$SCRIPT" ] ; do
|
|||
done
|
||||
|
||||
DIR="$(dirname "${SCRIPT}")/.."
|
||||
CONFIG_DIR=${KIBANA_PATH_CONF:-"$DIR/config"}
|
||||
CONFIG_DIR=${KBN_PATH_CONF:-"$DIR/config"}
|
||||
NODE="${DIR}/node/bin/node"
|
||||
test -x "$NODE"
|
||||
if [ ! -x "$NODE" ]; then
|
||||
|
|
|
@ -12,8 +12,8 @@ If Not Exist "%NODE%" (
|
|||
Exit /B 1
|
||||
)
|
||||
|
||||
set CONFIG_DIR=%KIBANA_PATH_CONF%
|
||||
If [%KIBANA_PATH_CONF%] == [] (
|
||||
set CONFIG_DIR=%KBN_PATH_CONF%
|
||||
If [%KBN_PATH_CONF%] == [] (
|
||||
set CONFIG_DIR=%DIR%\config
|
||||
)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ while [ -h "$SCRIPT" ] ; do
|
|||
done
|
||||
|
||||
DIR="$(dirname "${SCRIPT}")/.."
|
||||
CONFIG_DIR=${KIBANA_PATH_CONF:-"$DIR/config"}
|
||||
CONFIG_DIR=${KBN_PATH_CONF:-"$DIR/config"}
|
||||
NODE="${DIR}/node/bin/node"
|
||||
test -x "$NODE"
|
||||
if [ ! -x "$NODE" ]; then
|
||||
|
|
|
@ -13,8 +13,8 @@ If Not Exist "%NODE%" (
|
|||
Exit /B 1
|
||||
)
|
||||
|
||||
set CONFIG_DIR=%KIBANA_PATH_CONF%
|
||||
If [%KIBANA_PATH_CONF%] == [] (
|
||||
set CONFIG_DIR=%KBN_PATH_CONF%
|
||||
If [%KBN_PATH_CONF%] == [] (
|
||||
set CONFIG_DIR=%DIR%\config
|
||||
)
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ If Not Exist "%NODE%" (
|
|||
Exit /B 1
|
||||
)
|
||||
|
||||
set CONFIG_DIR=%KIBANA_PATH_CONF%
|
||||
If [%KIBANA_PATH_CONF%] == [] (
|
||||
set CONFIG_DIR=%KBN_PATH_CONF%
|
||||
If [%KBN_PATH_CONF%] == [] (
|
||||
set CONFIG_DIR=%DIR%\config
|
||||
)
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ case $1 in
|
|||
--ingroup "<%= group %>" --shell /bin/false "<%= user %>"
|
||||
fi
|
||||
|
||||
if [ -n "$2" ]; then
|
||||
IS_UPGRADE=true
|
||||
fi
|
||||
|
||||
set_access
|
||||
;;
|
||||
abort-deconfigure|abort-upgrade|abort-remove)
|
||||
|
@ -47,6 +51,10 @@ case $1 in
|
|||
-c "kibana service user" "<%= user %>"
|
||||
fi
|
||||
|
||||
if [ "$1" = "2" ]; then
|
||||
IS_UPGRADE=true
|
||||
fi
|
||||
|
||||
set_access
|
||||
;;
|
||||
|
||||
|
@ -55,3 +63,9 @@ case $1 in
|
|||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$IS_UPGRADE" = "true" ]; then
|
||||
if command -v systemctl >/dev/null; then
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -12,4 +12,4 @@ KILL_ON_STOP_TIMEOUT=0
|
|||
|
||||
BABEL_CACHE_PATH="/var/lib/kibana/optimize/.babel_register_cache.json"
|
||||
|
||||
KIBANA_PATH_CONF="/etc/kibana"
|
||||
KBN_PATH_CONF="/etc/kibana"
|
||||
|
|
|
@ -108,6 +108,7 @@ run(
|
|||
const reporter = new ErrorReporter();
|
||||
const messages: Map<string, { message: string }> = new Map();
|
||||
await list.run({ messages, reporter });
|
||||
process.exitCode = 0;
|
||||
} catch (error) {
|
||||
process.exitCode = 1;
|
||||
if (error instanceof ErrorReporter) {
|
||||
|
@ -117,6 +118,7 @@ run(
|
|||
log.error(error);
|
||||
}
|
||||
}
|
||||
process.exit();
|
||||
},
|
||||
{
|
||||
flags: {
|
||||
|
|
|
@ -52,7 +52,10 @@ export interface DashboardAppScope extends ng.IScope {
|
|||
expandedPanel?: string;
|
||||
getShouldShowEditHelp: () => boolean;
|
||||
getShouldShowViewHelp: () => boolean;
|
||||
updateQueryAndFetch: ({ query, dateRange }: { query: Query; dateRange?: TimeRange }) => void;
|
||||
handleRefresh: (
|
||||
{ query, dateRange }: { query?: Query; dateRange: TimeRange },
|
||||
isUpdate?: boolean
|
||||
) => void;
|
||||
topNavMenu: any;
|
||||
showAddPanel: any;
|
||||
showSaveQuery: boolean;
|
||||
|
|
|
@ -25,12 +25,11 @@ import React, { useState, ReactElement } from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import angular from 'angular';
|
||||
|
||||
import { Observable, pipe, Subscription } from 'rxjs';
|
||||
import { filter, map, mapTo, startWith, switchMap } from 'rxjs/operators';
|
||||
import { Observable, pipe, Subscription, merge } from 'rxjs';
|
||||
import { filter, map, debounceTime, mapTo, startWith, switchMap } from 'rxjs/operators';
|
||||
import { History } from 'history';
|
||||
import { SavedObjectSaveOpts } from 'src/plugins/saved_objects/public';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public';
|
||||
import { TimeRange } from 'src/plugins/data/public';
|
||||
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
|
||||
|
||||
import {
|
||||
|
@ -38,11 +37,9 @@ import {
|
|||
esFilters,
|
||||
IndexPattern,
|
||||
IndexPatternsContract,
|
||||
Query,
|
||||
QueryState,
|
||||
SavedQuery,
|
||||
syncQueryStateWithUrl,
|
||||
UI_SETTINGS,
|
||||
} from '../../../data/public';
|
||||
import { getSavedObjectFinder, SaveResult, showSaveModal } from '../../../saved_objects/public';
|
||||
|
||||
|
@ -81,8 +78,8 @@ import {
|
|||
addFatalError,
|
||||
AngularHttpError,
|
||||
KibanaLegacyStart,
|
||||
migrateLegacyQuery,
|
||||
subscribeWithScope,
|
||||
migrateLegacyQuery,
|
||||
} from '../../../kibana_legacy/public';
|
||||
|
||||
export interface DashboardAppControllerDependencies extends RenderDeps {
|
||||
|
@ -127,7 +124,6 @@ export class DashboardAppController {
|
|||
$route,
|
||||
$routeParams,
|
||||
dashboardConfig,
|
||||
localStorage,
|
||||
indexPatterns,
|
||||
savedQueryService,
|
||||
embeddable,
|
||||
|
@ -153,8 +149,8 @@ export class DashboardAppController {
|
|||
navigation,
|
||||
}: DashboardAppControllerDependencies) {
|
||||
const filterManager = queryService.filterManager;
|
||||
const queryFilter = filterManager;
|
||||
const timefilter = queryService.timefilter.timefilter;
|
||||
const queryStringManager = queryService.queryString;
|
||||
const isEmbeddedExternally = Boolean($routeParams.embed);
|
||||
|
||||
// url param rules should only apply when embedded (e.g. url?embed=true)
|
||||
|
@ -172,6 +168,10 @@ export class DashboardAppController {
|
|||
chrome.docTitle.change(dash.title);
|
||||
}
|
||||
|
||||
const incomingEmbeddable = embeddable
|
||||
.getStateTransfer(scopedHistory())
|
||||
.getIncomingEmbeddablePackage();
|
||||
|
||||
const dashboardStateManager = new DashboardStateManager({
|
||||
savedDashboard: dash,
|
||||
hideWriteControls: dashboardConfig.getHideWriteControls(),
|
||||
|
@ -184,20 +184,30 @@ export class DashboardAppController {
|
|||
// sync initial app filters from state to filterManager
|
||||
// if there is an existing similar global filter, then leave it as global
|
||||
filterManager.setAppFilters(_.cloneDeep(dashboardStateManager.appState.filters));
|
||||
queryStringManager.setQuery(migrateLegacyQuery(dashboardStateManager.appState.query));
|
||||
|
||||
// setup syncing of app filters between appState and filterManager
|
||||
const stopSyncingAppFilters = connectToQueryState(
|
||||
queryService,
|
||||
{
|
||||
set: ({ filters }) => dashboardStateManager.setFilters(filters || []),
|
||||
get: () => ({ filters: dashboardStateManager.appState.filters }),
|
||||
set: ({ filters, query }) => {
|
||||
dashboardStateManager.setFilters(filters || []);
|
||||
dashboardStateManager.setQuery(query || queryStringManager.getDefaultQuery());
|
||||
},
|
||||
get: () => ({
|
||||
filters: dashboardStateManager.appState.filters,
|
||||
query: dashboardStateManager.getQuery(),
|
||||
}),
|
||||
state$: dashboardStateManager.appState$.pipe(
|
||||
map((state) => ({
|
||||
filters: state.filters,
|
||||
query: queryStringManager.formatQuery(state.query),
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
filters: esFilters.FilterStateStore.APP_STATE,
|
||||
query: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -327,7 +337,7 @@ export class DashboardAppController {
|
|||
const isEmptyInReadonlyMode = shouldShowUnauthorizedEmptyState();
|
||||
return {
|
||||
id: dashboardStateManager.savedDashboard.id || '',
|
||||
filters: queryFilter.getFilters(),
|
||||
filters: filterManager.getFilters(),
|
||||
hidePanelTitles: dashboardStateManager.getHidePanelTitles(),
|
||||
query: $scope.model.query,
|
||||
timeRange: {
|
||||
|
@ -352,7 +362,7 @@ export class DashboardAppController {
|
|||
// https://github.com/angular/angular.js/wiki/Understanding-Scopes
|
||||
$scope.model = {
|
||||
query: dashboardStateManager.getQuery(),
|
||||
filters: queryFilter.getFilters(),
|
||||
filters: filterManager.getFilters(),
|
||||
timeRestore: dashboardStateManager.getTimeRestore(),
|
||||
title: dashboardStateManager.getTitle(),
|
||||
description: dashboardStateManager.getDescription(),
|
||||
|
@ -416,12 +426,12 @@ export class DashboardAppController {
|
|||
if (
|
||||
!esFilters.compareFilters(
|
||||
container.getInput().filters,
|
||||
queryFilter.getFilters(),
|
||||
filterManager.getFilters(),
|
||||
esFilters.COMPARE_ALL_OPTIONS
|
||||
)
|
||||
) {
|
||||
// Add filters modifies the object passed to it, hence the clone deep.
|
||||
queryFilter.addFilters(_.cloneDeep(container.getInput().filters));
|
||||
filterManager.addFilters(_.cloneDeep(container.getInput().filters));
|
||||
|
||||
dashboardStateManager.applyFilters(
|
||||
$scope.model.query,
|
||||
|
@ -444,21 +454,24 @@ export class DashboardAppController {
|
|||
refreshDashboardContainer();
|
||||
});
|
||||
|
||||
const incomingState = embeddable
|
||||
.getStateTransfer(scopedHistory())
|
||||
.getIncomingEmbeddablePackage();
|
||||
if (incomingState) {
|
||||
if ('id' in incomingState) {
|
||||
container.addOrUpdateEmbeddable<SavedObjectEmbeddableInput>(incomingState.type, {
|
||||
savedObjectId: incomingState.id,
|
||||
});
|
||||
} else if ('input' in incomingState) {
|
||||
const input = incomingState.input;
|
||||
if (incomingEmbeddable) {
|
||||
if ('id' in incomingEmbeddable) {
|
||||
container.addOrUpdateEmbeddable<SavedObjectEmbeddableInput>(
|
||||
incomingEmbeddable.type,
|
||||
{
|
||||
savedObjectId: incomingEmbeddable.id,
|
||||
}
|
||||
);
|
||||
} else if ('input' in incomingEmbeddable) {
|
||||
const input = incomingEmbeddable.input;
|
||||
delete input.id;
|
||||
const explicitInput = {
|
||||
savedVis: input,
|
||||
};
|
||||
container.addOrUpdateEmbeddable<EmbeddableInput>(incomingState.type, explicitInput);
|
||||
container.addOrUpdateEmbeddable<EmbeddableInput>(
|
||||
incomingEmbeddable.type,
|
||||
explicitInput
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -480,13 +493,8 @@ export class DashboardAppController {
|
|||
});
|
||||
|
||||
dashboardStateManager.applyFilters(
|
||||
dashboardStateManager.getQuery() || {
|
||||
query: '',
|
||||
language:
|
||||
localStorage.get('kibana.userQueryLanguage') ||
|
||||
uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
},
|
||||
queryFilter.getFilters()
|
||||
dashboardStateManager.getQuery() || queryStringManager.getDefaultQuery(),
|
||||
filterManager.getFilters()
|
||||
);
|
||||
|
||||
timefilter.disableTimeRangeSelector();
|
||||
|
@ -560,21 +568,13 @@ export class DashboardAppController {
|
|||
}
|
||||
};
|
||||
|
||||
$scope.updateQueryAndFetch = function ({ query, dateRange }) {
|
||||
if (dateRange) {
|
||||
timefilter.setTime(dateRange);
|
||||
}
|
||||
|
||||
const oldQuery = $scope.model.query;
|
||||
if (_.isEqual(oldQuery, query)) {
|
||||
$scope.handleRefresh = function (_payload, isUpdate) {
|
||||
if (isUpdate === false) {
|
||||
// The user can still request a reload in the query bar, even if the
|
||||
// query is the same, and in that case, we have to explicitly ask for
|
||||
// a reload, since no state changes will cause it.
|
||||
lastReloadRequestTime = new Date().getTime();
|
||||
refreshDashboardContainer();
|
||||
} else {
|
||||
$scope.model.query = query;
|
||||
dashboardStateManager.applyFilters($scope.model.query, $scope.model.filters);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -593,7 +593,7 @@ export class DashboardAppController {
|
|||
// Making this method sync broke the updates.
|
||||
// Temporary fix, until we fix the complex state in this file.
|
||||
setTimeout(() => {
|
||||
queryFilter.setFilters(allFilters);
|
||||
filterManager.setFilters(allFilters);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
|
@ -626,11 +626,6 @@ export class DashboardAppController {
|
|||
|
||||
$scope.indexPatterns = [];
|
||||
|
||||
$scope.$watch('model.query', (newQuery: Query) => {
|
||||
const query = migrateLegacyQuery(newQuery) as Query;
|
||||
$scope.updateQueryAndFetch({ query });
|
||||
});
|
||||
|
||||
$scope.$watch(
|
||||
() => dashboardCapabilities.saveQuery,
|
||||
(newCapability) => {
|
||||
|
@ -671,18 +666,11 @@ export class DashboardAppController {
|
|||
showFilterBar,
|
||||
indexPatterns: $scope.indexPatterns,
|
||||
showSaveQuery: $scope.showSaveQuery,
|
||||
query: $scope.model.query,
|
||||
savedQuery: $scope.savedQuery,
|
||||
onSavedQueryIdChange,
|
||||
savedQueryId: dashboardStateManager.getSavedQueryId(),
|
||||
useDefaultBehaviors: true,
|
||||
onQuerySubmit: (payload: { dateRange: TimeRange; query?: Query }): void => {
|
||||
if (!payload.query) {
|
||||
$scope.updateQueryAndFetch({ query: $scope.model.query, dateRange: payload.dateRange });
|
||||
} else {
|
||||
$scope.updateQueryAndFetch({ query: payload.query, dateRange: payload.dateRange });
|
||||
}
|
||||
},
|
||||
onQuerySubmit: $scope.handleRefresh,
|
||||
};
|
||||
};
|
||||
const dashboardNavBar = document.getElementById('dashboardChrome');
|
||||
|
@ -697,25 +685,11 @@ export class DashboardAppController {
|
|||
};
|
||||
|
||||
$scope.timefilterSubscriptions$ = new Subscription();
|
||||
|
||||
const timeChanges$ = merge(timefilter.getRefreshIntervalUpdate$(), timefilter.getTimeUpdate$());
|
||||
$scope.timefilterSubscriptions$.add(
|
||||
subscribeWithScope(
|
||||
$scope,
|
||||
timefilter.getRefreshIntervalUpdate$(),
|
||||
{
|
||||
next: () => {
|
||||
updateState();
|
||||
refreshDashboardContainer();
|
||||
},
|
||||
},
|
||||
(error: AngularHttpError | Error | string) => addFatalError(fatalErrors, error)
|
||||
)
|
||||
);
|
||||
|
||||
$scope.timefilterSubscriptions$.add(
|
||||
subscribeWithScope(
|
||||
$scope,
|
||||
timefilter.getTimeUpdate$(),
|
||||
timeChanges$,
|
||||
{
|
||||
next: () => {
|
||||
updateState();
|
||||
|
@ -1088,13 +1062,21 @@ export class DashboardAppController {
|
|||
|
||||
updateViewMode(dashboardStateManager.getViewMode());
|
||||
|
||||
const filterChanges = merge(filterManager.getUpdates$(), queryStringManager.getUpdates$()).pipe(
|
||||
debounceTime(100)
|
||||
);
|
||||
|
||||
// update root source when filters update
|
||||
const updateSubscription = queryFilter.getUpdates$().subscribe({
|
||||
const updateSubscription = filterChanges.subscribe({
|
||||
next: () => {
|
||||
$scope.model.filters = queryFilter.getFilters();
|
||||
$scope.model.filters = filterManager.getFilters();
|
||||
$scope.model.query = queryStringManager.getQuery();
|
||||
dashboardStateManager.applyFilters($scope.model.query, $scope.model.filters);
|
||||
if (dashboardContainer) {
|
||||
dashboardContainer.updateInput({ filters: $scope.model.filters });
|
||||
dashboardContainer.updateInput({
|
||||
filters: $scope.model.filters,
|
||||
query: $scope.model.query,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -199,10 +199,11 @@ export const castEsToKbnFieldTypeName: (esType: ES_FIELD_TYPES | string) => KBN_
|
|||
// Warning: (ae-missing-release-tag) "connectToQueryState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export const connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
export const connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, queryString, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'queryString' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
time?: boolean;
|
||||
refreshInterval?: boolean;
|
||||
filters?: FilterStateStore | boolean;
|
||||
query?: boolean;
|
||||
}) => () => void;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "createSavedQueryService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
@ -1388,6 +1389,8 @@ export interface QueryState {
|
|||
// (undocumented)
|
||||
filters?: Filter[];
|
||||
// (undocumented)
|
||||
query?: Query;
|
||||
// (undocumented)
|
||||
refreshInterval?: RefreshInterval;
|
||||
// (undocumented)
|
||||
time?: TimeRange;
|
||||
|
@ -1762,7 +1765,7 @@ export type StatefulSearchBarProps = SearchBarOwnProps & {
|
|||
// Warning: (ae-missing-release-tag) "syncQueryStateWithUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export const syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
export const syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'queryString' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
stop: () => void;
|
||||
hasInheritedQueryFromUrl: boolean;
|
||||
};
|
||||
|
@ -1911,7 +1914,7 @@ export const UI_SETTINGS: {
|
|||
// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:41:60 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:45:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/types.ts:54:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/types.ts:55:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/types.ts:63:5 - (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts
|
||||
|
|
|
@ -21,6 +21,7 @@ import { Observable } from 'rxjs';
|
|||
import { QueryService, QuerySetup, QueryStart } from '.';
|
||||
import { timefilterServiceMock } from './timefilter/timefilter_service.mock';
|
||||
import { createFilterManagerMock } from './filter_manager/filter_manager.mock';
|
||||
import { queryStringManagerMock } from './query_string/query_string_manager.mock';
|
||||
|
||||
type QueryServiceClientContract = PublicMethodsOf<QueryService>;
|
||||
|
||||
|
@ -28,6 +29,7 @@ const createSetupContractMock = () => {
|
|||
const setupContract: jest.Mocked<QuerySetup> = {
|
||||
filterManager: createFilterManagerMock(),
|
||||
timefilter: timefilterServiceMock.createSetupContract(),
|
||||
queryString: queryStringManagerMock.createSetupContract(),
|
||||
state$: new Observable(),
|
||||
};
|
||||
|
||||
|
@ -38,6 +40,7 @@ const createStartContractMock = () => {
|
|||
const startContract: jest.Mocked<QueryStart> = {
|
||||
addToQueryLog: jest.fn(),
|
||||
filterManager: createFilterManagerMock(),
|
||||
queryString: queryStringManagerMock.createStartContract(),
|
||||
savedQueries: jest.fn() as any,
|
||||
state$: new Observable(),
|
||||
timefilter: timefilterServiceMock.createStartContract(),
|
||||
|
|
|
@ -25,6 +25,7 @@ import { createAddToQueryLog } from './lib';
|
|||
import { TimefilterService, TimefilterSetup } from './timefilter';
|
||||
import { createSavedQueryService } from './saved_query/saved_query_service';
|
||||
import { createQueryStateObservable } from './state_sync/create_global_query_observable';
|
||||
import { QueryStringManager, QueryStringContract } from './query_string';
|
||||
|
||||
/**
|
||||
* Query Service
|
||||
|
@ -45,6 +46,7 @@ interface QueryServiceStartDependencies {
|
|||
export class QueryService {
|
||||
filterManager!: FilterManager;
|
||||
timefilter!: TimefilterSetup;
|
||||
queryStringManager!: QueryStringContract;
|
||||
|
||||
state$!: ReturnType<typeof createQueryStateObservable>;
|
||||
|
||||
|
@ -57,14 +59,18 @@ export class QueryService {
|
|||
storage,
|
||||
});
|
||||
|
||||
this.queryStringManager = new QueryStringManager(storage, uiSettings);
|
||||
|
||||
this.state$ = createQueryStateObservable({
|
||||
filterManager: this.filterManager,
|
||||
timefilter: this.timefilter,
|
||||
queryString: this.queryStringManager,
|
||||
}).pipe(share());
|
||||
|
||||
return {
|
||||
filterManager: this.filterManager,
|
||||
timefilter: this.timefilter,
|
||||
queryString: this.queryStringManager,
|
||||
state$: this.state$,
|
||||
};
|
||||
}
|
||||
|
@ -76,6 +82,7 @@ export class QueryService {
|
|||
uiSettings,
|
||||
}),
|
||||
filterManager: this.filterManager,
|
||||
queryString: this.queryStringManager,
|
||||
savedQueries: createSavedQueryService(savedObjectsClient),
|
||||
state$: this.state$,
|
||||
timefilter: this.timefilter,
|
||||
|
|
20
src/plugins/data/public/query/query_string/index.ts
Normal file
20
src/plugins/data/public/query/query_string/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License 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 { QueryStringContract, QueryStringManager } from './query_string_manager';
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License 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 { QueryStringContract } from '.';
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const queryStringManagerMock: jest.Mocked<QueryStringContract> = {
|
||||
getQuery: jest.fn(),
|
||||
setQuery: jest.fn(),
|
||||
getUpdates$: jest.fn(),
|
||||
getDefaultQuery: jest.fn(),
|
||||
formatQuery: jest.fn(),
|
||||
clearQuery: jest.fn(),
|
||||
};
|
||||
return queryStringManagerMock;
|
||||
};
|
||||
|
||||
export const queryStringManagerMock = {
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createSetupContractMock,
|
||||
};
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License 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 _ from 'lodash';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
|
||||
import { Query, UI_SETTINGS } from '../../../common';
|
||||
|
||||
export class QueryStringManager {
|
||||
private query$: BehaviorSubject<Query>;
|
||||
|
||||
constructor(
|
||||
private readonly storage: IStorageWrapper,
|
||||
private readonly uiSettings: CoreStart['uiSettings']
|
||||
) {
|
||||
this.query$ = new BehaviorSubject<Query>(this.getDefaultQuery());
|
||||
}
|
||||
|
||||
private getDefaultLanguage() {
|
||||
return (
|
||||
this.storage.get('kibana.userQueryLanguage') ||
|
||||
this.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE)
|
||||
);
|
||||
}
|
||||
|
||||
public getDefaultQuery() {
|
||||
return {
|
||||
query: '',
|
||||
language: this.getDefaultLanguage(),
|
||||
};
|
||||
}
|
||||
|
||||
public formatQuery(query: Query | string | undefined): Query {
|
||||
if (!query) {
|
||||
return this.getDefaultQuery();
|
||||
} else if (typeof query === 'string') {
|
||||
return {
|
||||
query,
|
||||
language: this.getDefaultLanguage(),
|
||||
};
|
||||
} else {
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
public getUpdates$ = () => {
|
||||
return this.query$.asObservable();
|
||||
};
|
||||
|
||||
public getQuery = (): Query => {
|
||||
return this.query$.getValue();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the query.
|
||||
* @param {Query} query
|
||||
*/
|
||||
public setQuery = (query: Query) => {
|
||||
const curQuery = this.query$.getValue();
|
||||
if (query?.language !== curQuery.language || query?.query !== curQuery.query) {
|
||||
this.query$.next(query);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets the query to the default one.
|
||||
*/
|
||||
public clearQuery = () => {
|
||||
this.setQuery(this.getDefaultQuery());
|
||||
};
|
||||
}
|
||||
|
||||
export type QueryStringContract = PublicMethodsOf<QueryStringManager>;
|
|
@ -48,6 +48,8 @@ setupMock.uiSettings.get.mockImplementation((key: string) => {
|
|||
switch (key) {
|
||||
case UI_SETTINGS.FILTERS_PINNED_BY_DEFAULT:
|
||||
return true;
|
||||
case UI_SETTINGS.SEARCH_QUERY_LANGUAGE:
|
||||
return 'kuery';
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now' };
|
||||
case UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS:
|
||||
|
|
|
@ -35,15 +35,24 @@ export const connectToQueryState = <S extends QueryState>(
|
|||
{
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
state$,
|
||||
}: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'state$'>,
|
||||
}: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'queryString' | 'state$'>,
|
||||
stateContainer: BaseStateContainer<S>,
|
||||
syncConfig: { time?: boolean; refreshInterval?: boolean; filters?: FilterStateStore | boolean }
|
||||
syncConfig: {
|
||||
time?: boolean;
|
||||
refreshInterval?: boolean;
|
||||
filters?: FilterStateStore | boolean;
|
||||
query?: boolean;
|
||||
}
|
||||
) => {
|
||||
const syncKeys: Array<keyof QueryStateChange> = [];
|
||||
if (syncConfig.time) {
|
||||
syncKeys.push('time');
|
||||
}
|
||||
if (syncConfig.query) {
|
||||
syncKeys.push('query');
|
||||
}
|
||||
if (syncConfig.refreshInterval) {
|
||||
syncKeys.push('refreshInterval');
|
||||
}
|
||||
|
@ -133,6 +142,9 @@ export const connectToQueryState = <S extends QueryState>(
|
|||
if (syncConfig.time && changes.time) {
|
||||
newState.time = timefilter.getTime();
|
||||
}
|
||||
if (syncConfig.query && changes.query) {
|
||||
newState.query = queryString.getQuery();
|
||||
}
|
||||
if (syncConfig.refreshInterval && changes.refreshInterval) {
|
||||
newState.refreshInterval = timefilter.getRefreshInterval();
|
||||
}
|
||||
|
@ -173,6 +185,13 @@ export const connectToQueryState = <S extends QueryState>(
|
|||
}
|
||||
}
|
||||
|
||||
if (syncConfig.query) {
|
||||
const curQuery = state.query || queryString.getQuery();
|
||||
if (!_.isEqual(curQuery, queryString.getQuery())) {
|
||||
queryString.setQuery(_.cloneDeep(curQuery));
|
||||
}
|
||||
}
|
||||
|
||||
if (syncConfig.filters) {
|
||||
const filters = state.filters || [];
|
||||
if (syncConfig.filters === true) {
|
||||
|
|
|
@ -24,23 +24,31 @@ import { FilterManager } from '../filter_manager';
|
|||
import { QueryState, QueryStateChange } from './index';
|
||||
import { createStateContainer } from '../../../../kibana_utils/public';
|
||||
import { isFilterPinned, compareFilters, COMPARE_ALL_OPTIONS } from '../../../common';
|
||||
import { QueryStringContract } from '../query_string';
|
||||
|
||||
export function createQueryStateObservable({
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
}: {
|
||||
timefilter: TimefilterSetup;
|
||||
filterManager: FilterManager;
|
||||
queryString: QueryStringContract;
|
||||
}): Observable<{ changes: QueryStateChange; state: QueryState }> {
|
||||
return new Observable((subscriber) => {
|
||||
const state = createStateContainer<QueryState>({
|
||||
time: timefilter.getTime(),
|
||||
refreshInterval: timefilter.getRefreshInterval(),
|
||||
filters: filterManager.getFilters(),
|
||||
query: queryString.getQuery(),
|
||||
});
|
||||
|
||||
let currentChange: QueryStateChange = {};
|
||||
const subs: Subscription[] = [
|
||||
queryString.getUpdates$().subscribe(() => {
|
||||
currentChange.query = true;
|
||||
state.set({ ...state.get(), query: queryString.getQuery() });
|
||||
}),
|
||||
timefilter.getTimeUpdate$().subscribe(() => {
|
||||
currentChange.time = true;
|
||||
state.set({ ...state.get(), time: timefilter.getTime() });
|
||||
|
|
|
@ -43,6 +43,8 @@ setupMock.uiSettings.get.mockImplementation((key: string) => {
|
|||
return true;
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now' };
|
||||
case 'search:queryLanguage':
|
||||
return 'kuery';
|
||||
case UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS:
|
||||
return { pause: false, value: 0 };
|
||||
default:
|
||||
|
|
|
@ -35,7 +35,7 @@ const GLOBAL_STATE_STORAGE_KEY = '_g';
|
|||
* @param kbnUrlStateStorage to use for syncing
|
||||
*/
|
||||
export const syncQueryStateWithUrl = (
|
||||
query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'state$'>,
|
||||
query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'queryString' | 'state$'>,
|
||||
kbnUrlStateStorage: IKbnUrlStateStorage
|
||||
) => {
|
||||
const {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Filter, RefreshInterval, TimeRange } from '../../../common';
|
||||
import { Filter, RefreshInterval, TimeRange, Query } from '../../../common';
|
||||
|
||||
/**
|
||||
* All query state service state
|
||||
|
@ -26,6 +26,7 @@ export interface QueryState {
|
|||
time?: TimeRange;
|
||||
refreshInterval?: RefreshInterval;
|
||||
filters?: Filter[];
|
||||
query?: Query;
|
||||
}
|
||||
|
||||
type QueryStateChangePartial = {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
|
||||
import { KibanaContextProvider } from '../../../../kibana_react/public';
|
||||
|
@ -28,7 +28,8 @@ import { useFilterManager } from './lib/use_filter_manager';
|
|||
import { useTimefilter } from './lib/use_timefilter';
|
||||
import { useSavedQuery } from './lib/use_saved_query';
|
||||
import { DataPublicPluginStart } from '../../types';
|
||||
import { Filter, Query, TimeRange, UI_SETTINGS } from '../../../common';
|
||||
import { Filter, Query, TimeRange } from '../../../common';
|
||||
import { useQueryStringManager } from './lib/use_query_string_manager';
|
||||
|
||||
interface StatefulSearchBarDeps {
|
||||
core: CoreStart;
|
||||
|
@ -65,8 +66,7 @@ const defaultOnRefreshChange = (queryService: QueryStart) => {
|
|||
const defaultOnQuerySubmit = (
|
||||
props: StatefulSearchBarProps,
|
||||
queryService: QueryStart,
|
||||
currentQuery: Query,
|
||||
setQueryStringState: Function
|
||||
currentQuery: Query
|
||||
) => {
|
||||
if (!props.useDefaultBehaviors) return props.onQuerySubmit;
|
||||
|
||||
|
@ -78,7 +78,11 @@ const defaultOnQuerySubmit = (
|
|||
!_.isEqual(payload.query, currentQuery);
|
||||
if (isUpdate) {
|
||||
timefilter.setTime(payload.dateRange);
|
||||
setQueryStringState(payload.query);
|
||||
if (payload.query) {
|
||||
queryService.queryString.setQuery(payload.query);
|
||||
} else {
|
||||
queryService.queryString.clearQuery();
|
||||
}
|
||||
} else {
|
||||
// Refresh button triggered for an update
|
||||
if (props.onQuerySubmit)
|
||||
|
@ -121,30 +125,7 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
return (props: StatefulSearchBarProps) => {
|
||||
const { useDefaultBehaviors } = props;
|
||||
// Handle queries
|
||||
const queryRef = useRef(props.query);
|
||||
const onQuerySubmitRef = useRef(props.onQuerySubmit);
|
||||
const defaultQuery = {
|
||||
query: '',
|
||||
language:
|
||||
storage.get('kibana.userQueryLanguage') ||
|
||||
core.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
};
|
||||
const [query, setQuery] = useState<Query>(props.query || defaultQuery);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.query !== queryRef.current) {
|
||||
queryRef.current = props.query;
|
||||
setQuery(props.query || defaultQuery);
|
||||
}
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
}, [defaultQuery, props.query]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.onQuerySubmit !== onQuerySubmitRef.current) {
|
||||
onQuerySubmitRef.current = props.onQuerySubmit;
|
||||
}
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
}, [props.onQuerySubmit]);
|
||||
|
||||
// handle service state updates.
|
||||
// i.e. filters being added from a visualization directly to filterManager.
|
||||
|
@ -152,6 +133,10 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
filters: props.filters,
|
||||
filterManager: data.query.filterManager,
|
||||
});
|
||||
const { query } = useQueryStringManager({
|
||||
query: props.query,
|
||||
queryStringManager: data.query.queryString,
|
||||
});
|
||||
const { timeRange, refreshInterval } = useTimefilter({
|
||||
dateRangeFrom: props.dateRangeFrom,
|
||||
dateRangeTo: props.dateRangeTo,
|
||||
|
@ -163,10 +148,8 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
// Fetch and update UI from saved query
|
||||
const { savedQuery, setSavedQuery, clearSavedQuery } = useSavedQuery({
|
||||
queryService: data.query,
|
||||
setQuery,
|
||||
savedQueryId: props.savedQueryId,
|
||||
notifications: core.notifications,
|
||||
defaultLanguage: defaultQuery.language,
|
||||
});
|
||||
|
||||
// Fire onQuerySubmit on query or timerange change
|
||||
|
@ -210,7 +193,7 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
onFiltersUpdated={defaultFiltersUpdated(data.query)}
|
||||
onRefreshChange={defaultOnRefreshChange(data.query)}
|
||||
savedQuery={savedQuery}
|
||||
onQuerySubmit={defaultOnQuerySubmit(props, data.query, query, setQuery)}
|
||||
onQuerySubmit={defaultOnQuerySubmit(props, data.query, query)}
|
||||
onClearSavedQuery={defaultOnClearSavedQuery(props, clearSavedQuery)}
|
||||
onSavedQueryUpdated={defaultOnSavedQueryUpdated(props, setSavedQuery)}
|
||||
onSaved={defaultOnSavedQueryUpdated(props, setSavedQuery)}
|
||||
|
|
|
@ -21,10 +21,8 @@ import { clearStateFromSavedQuery } from './clear_saved_query';
|
|||
|
||||
import { dataPluginMock } from '../../../mocks';
|
||||
import { DataPublicPluginStart } from '../../../types';
|
||||
import { Query } from '../../..';
|
||||
|
||||
describe('clearStateFromSavedQuery', () => {
|
||||
const DEFAULT_LANGUAGE = 'banana';
|
||||
let dataMock: jest.Mocked<DataPublicPluginStart>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -32,19 +30,9 @@ describe('clearStateFromSavedQuery', () => {
|
|||
});
|
||||
|
||||
it('should clear filters and query', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
dataMock.query.filterManager.removeAll = jest.fn();
|
||||
clearStateFromSavedQuery(dataMock.query, setQueryState, DEFAULT_LANGUAGE);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.removeAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use search:queryLanguage', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
dataMock.query.filterManager.removeAll = jest.fn();
|
||||
clearStateFromSavedQuery(dataMock.query, setQueryState, DEFAULT_LANGUAGE);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
expect((setQueryState.mock.calls[0][0] as Query).language).toBe(DEFAULT_LANGUAGE);
|
||||
clearStateFromSavedQuery(dataMock.query);
|
||||
expect(dataMock.query.queryString.clearQuery).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.removeAll).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,14 +18,7 @@
|
|||
*/
|
||||
import { QueryStart } from '../../../query';
|
||||
|
||||
export const clearStateFromSavedQuery = (
|
||||
queryService: QueryStart,
|
||||
setQueryStringState: Function,
|
||||
defaultLanguage: string
|
||||
) => {
|
||||
export const clearStateFromSavedQuery = (queryService: QueryStart) => {
|
||||
queryService.filterManager.removeAll();
|
||||
setQueryStringState({
|
||||
query: '',
|
||||
language: defaultLanguage,
|
||||
});
|
||||
queryService.queryString.clearQuery();
|
||||
};
|
||||
|
|
|
@ -47,37 +47,34 @@ describe('populateStateFromSavedQuery', () => {
|
|||
});
|
||||
|
||||
it('should set query', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
const savedQuery: SavedQuery = {
|
||||
...baseSavedQuery,
|
||||
};
|
||||
populateStateFromSavedQuery(dataMock.query, setQueryState, savedQuery);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
expect(dataMock.query.queryString.setQuery).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set filters', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
const savedQuery: SavedQuery = {
|
||||
...baseSavedQuery,
|
||||
};
|
||||
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
|
||||
savedQuery.attributes.filters = [f1];
|
||||
populateStateFromSavedQuery(dataMock.query, setQueryState, savedQuery);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
expect(dataMock.query.queryString.setQuery).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.setFilters).toHaveBeenCalledWith([f1]);
|
||||
});
|
||||
|
||||
it('should preserve global filters', async () => {
|
||||
const globalFilter = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
|
||||
dataMock.query.filterManager.getGlobalFilters = jest.fn().mockReturnValue([globalFilter]);
|
||||
const setQueryState = jest.fn();
|
||||
const savedQuery: SavedQuery = {
|
||||
...baseSavedQuery,
|
||||
};
|
||||
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
|
||||
savedQuery.attributes.filters = [f1];
|
||||
populateStateFromSavedQuery(dataMock.query, setQueryState, savedQuery);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
expect(dataMock.query.queryString.setQuery).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.setFilters).toHaveBeenCalledWith([globalFilter, f1]);
|
||||
});
|
||||
|
||||
|
@ -97,7 +94,7 @@ describe('populateStateFromSavedQuery', () => {
|
|||
dataMock.query.timefilter.timefilter.setTime = jest.fn();
|
||||
dataMock.query.timefilter.timefilter.setRefreshInterval = jest.fn();
|
||||
|
||||
populateStateFromSavedQuery(dataMock.query, jest.fn(), savedQuery);
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
|
||||
expect(dataMock.query.timefilter.timefilter.setTime).toHaveBeenCalledWith({
|
||||
from: savedQuery.attributes.timefilter.from,
|
||||
|
|
|
@ -19,14 +19,11 @@
|
|||
|
||||
import { QueryStart, SavedQuery } from '../../../query';
|
||||
|
||||
export const populateStateFromSavedQuery = (
|
||||
queryService: QueryStart,
|
||||
setQueryStringState: Function,
|
||||
savedQuery: SavedQuery
|
||||
) => {
|
||||
export const populateStateFromSavedQuery = (queryService: QueryStart, savedQuery: SavedQuery) => {
|
||||
const {
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
} = queryService;
|
||||
// timefilter
|
||||
if (savedQuery.attributes.timefilter) {
|
||||
|
@ -40,7 +37,7 @@ export const populateStateFromSavedQuery = (
|
|||
}
|
||||
|
||||
// query string
|
||||
setQueryStringState(savedQuery.attributes.query);
|
||||
queryString.setQuery(savedQuery.attributes.query);
|
||||
|
||||
// filters
|
||||
const savedQueryFilters = savedQuery.attributes.filters || [];
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License 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 { useState, useEffect } from 'react';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Query } from '../../..';
|
||||
import { QueryStringContract } from '../../../query/query_string';
|
||||
|
||||
interface UseQueryStringProps {
|
||||
query?: Query;
|
||||
queryStringManager: QueryStringContract;
|
||||
}
|
||||
|
||||
export const useQueryStringManager = (props: UseQueryStringProps) => {
|
||||
// Filters should be either what's passed in the initial state or the current state of the filter manager
|
||||
const [query, setQuery] = useState<Query>(props.query || props.queryStringManager.getQuery());
|
||||
useEffect(() => {
|
||||
const subscriptions = new Subscription();
|
||||
|
||||
subscriptions.add(
|
||||
props.queryStringManager.getUpdates$().subscribe({
|
||||
next: () => {
|
||||
const newQuery = props.queryStringManager.getQuery();
|
||||
setQuery(newQuery);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return () => {
|
||||
subscriptions.unsubscribe();
|
||||
};
|
||||
}, [props.queryStringManager]);
|
||||
|
||||
return { query };
|
||||
};
|
|
@ -27,10 +27,8 @@ import { clearStateFromSavedQuery } from './clear_saved_query';
|
|||
|
||||
interface UseSavedQueriesProps {
|
||||
queryService: DataPublicPluginStart['query'];
|
||||
setQuery: Function;
|
||||
notifications: CoreStart['notifications'];
|
||||
savedQueryId?: string;
|
||||
defaultLanguage: string;
|
||||
}
|
||||
|
||||
interface UseSavedQueriesReturn {
|
||||
|
@ -41,7 +39,6 @@ interface UseSavedQueriesReturn {
|
|||
|
||||
export const useSavedQuery = (props: UseSavedQueriesProps): UseSavedQueriesReturn => {
|
||||
// Handle saved queries
|
||||
const defaultLanguage = props.defaultLanguage;
|
||||
const [savedQuery, setSavedQuery] = useState<SavedQuery | undefined>();
|
||||
|
||||
// Effect is used to convert a saved query id into an object
|
||||
|
@ -53,12 +50,12 @@ export const useSavedQuery = (props: UseSavedQueriesProps): UseSavedQueriesRetur
|
|||
// Make sure we set the saved query to the most recent one
|
||||
if (newSavedQuery && newSavedQuery.id === savedQueryId) {
|
||||
setSavedQuery(newSavedQuery);
|
||||
populateStateFromSavedQuery(props.queryService, props.setQuery, newSavedQuery);
|
||||
populateStateFromSavedQuery(props.queryService, newSavedQuery);
|
||||
}
|
||||
} catch (error) {
|
||||
// Clear saved query
|
||||
setSavedQuery(undefined);
|
||||
clearStateFromSavedQuery(props.queryService, props.setQuery, defaultLanguage);
|
||||
clearStateFromSavedQuery(props.queryService);
|
||||
// notify of saving error
|
||||
props.notifications.toasts.addWarning({
|
||||
title: i18n.translate('data.search.unableToGetSavedQueryToastTitle', {
|
||||
|
@ -73,23 +70,21 @@ export const useSavedQuery = (props: UseSavedQueriesProps): UseSavedQueriesRetur
|
|||
if (props.savedQueryId) fetchSavedQuery(props.savedQueryId);
|
||||
else setSavedQuery(undefined);
|
||||
}, [
|
||||
defaultLanguage,
|
||||
props.notifications.toasts,
|
||||
props.queryService,
|
||||
props.queryService.savedQueries,
|
||||
props.savedQueryId,
|
||||
props.setQuery,
|
||||
]);
|
||||
|
||||
return {
|
||||
savedQuery,
|
||||
setSavedQuery: (q: SavedQuery) => {
|
||||
setSavedQuery(q);
|
||||
populateStateFromSavedQuery(props.queryService, props.setQuery, q);
|
||||
populateStateFromSavedQuery(props.queryService, q);
|
||||
},
|
||||
clearSavedQuery: () => {
|
||||
setSavedQuery(undefined);
|
||||
clearStateFromSavedQuery(props.queryService, props.setQuery, defaultLanguage);
|
||||
clearStateFromSavedQuery(props.queryService);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
app-name="'discover'"
|
||||
config="topNavMenu"
|
||||
index-patterns="[indexPattern]"
|
||||
on-query-submit="updateQuery"
|
||||
on-query-submit="handleRefresh"
|
||||
on-saved-query-id-change="updateSavedQueryId"
|
||||
query="state.query"
|
||||
saved-query-id="state.savedQuery"
|
||||
screen-title="screenTitle"
|
||||
show-date-picker="indexPattern.isTimeBased()"
|
||||
|
|
|
@ -70,9 +70,7 @@ import {
|
|||
indexPatterns as indexPatternsUtils,
|
||||
connectToQueryState,
|
||||
syncQueryStateWithUrl,
|
||||
getDefaultQuery,
|
||||
search,
|
||||
UI_SETTINGS,
|
||||
} from '../../../../data/public';
|
||||
import { getIndexPatternId } from '../helpers/get_index_pattern_id';
|
||||
import { addFatalError } from '../../../../kibana_legacy/public';
|
||||
|
@ -191,16 +189,7 @@ app.directive('discoverApp', function () {
|
|||
};
|
||||
});
|
||||
|
||||
function discoverController(
|
||||
$element,
|
||||
$route,
|
||||
$scope,
|
||||
$timeout,
|
||||
$window,
|
||||
Promise,
|
||||
localStorage,
|
||||
uiCapabilities
|
||||
) {
|
||||
function discoverController($element, $route, $scope, $timeout, $window, Promise, uiCapabilities) {
|
||||
const { isDefault: isDefaultType } = indexPatternsUtils;
|
||||
const subscriptions = new Subscription();
|
||||
const $fetchObservable = new Subject();
|
||||
|
@ -246,11 +235,15 @@ function discoverController(
|
|||
|
||||
// sync initial app filters from state to filterManager
|
||||
filterManager.setAppFilters(_.cloneDeep(appStateContainer.getState().filters));
|
||||
data.query.queryString.setQuery(appStateContainer.getState().query);
|
||||
|
||||
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(
|
||||
data.query,
|
||||
appStateContainer,
|
||||
{ filters: esFilters.FilterStateStore.APP_STATE }
|
||||
{
|
||||
filters: esFilters.FilterStateStore.APP_STATE,
|
||||
query: true,
|
||||
}
|
||||
);
|
||||
|
||||
const appStateUnsubscribe = appStateContainer.subscribe(async (newState) => {
|
||||
|
@ -262,7 +255,7 @@ function discoverController(
|
|||
$scope.state = { ...newState };
|
||||
|
||||
// detect changes that should trigger fetching of new data
|
||||
const changes = ['interval', 'sort', 'query'].filter(
|
||||
const changes = ['interval', 'sort'].filter(
|
||||
(prop) => !_.isEqual(newStatePartial[prop], oldStatePartial[prop])
|
||||
);
|
||||
|
||||
|
@ -593,12 +586,7 @@ function discoverController(
|
|||
};
|
||||
|
||||
function getStateDefaults() {
|
||||
const query =
|
||||
$scope.searchSource.getField('query') ||
|
||||
getDefaultQuery(
|
||||
localStorage.get('kibana.userQueryLanguage') ||
|
||||
config.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE)
|
||||
);
|
||||
const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery();
|
||||
return {
|
||||
query,
|
||||
sort: getSortArray(savedSearch.sort, $scope.indexPattern),
|
||||
|
@ -635,12 +623,7 @@ function discoverController(
|
|||
|
||||
const init = _.once(() => {
|
||||
$scope.updateDataSource().then(async () => {
|
||||
const searchBarChanges = merge(
|
||||
timefilter.getAutoRefreshFetch$(),
|
||||
timefilter.getFetch$(),
|
||||
filterManager.getFetches$(),
|
||||
$fetchObservable
|
||||
).pipe(debounceTime(100));
|
||||
const searchBarChanges = merge(data.query.state$, $fetchObservable).pipe(debounceTime(100));
|
||||
|
||||
subscriptions.add(
|
||||
subscribeWithScope(
|
||||
|
@ -824,9 +807,8 @@ function discoverController(
|
|||
});
|
||||
};
|
||||
|
||||
$scope.updateQuery = function ({ query }, isUpdate = true) {
|
||||
if (!_.isEqual(query, appStateContainer.getState().query) || isUpdate === false) {
|
||||
setAppState({ query });
|
||||
$scope.handleRefresh = function (_payload, isUpdate) {
|
||||
if (isUpdate === false) {
|
||||
$fetchObservable.next();
|
||||
}
|
||||
};
|
||||
|
@ -976,7 +958,7 @@ function discoverController(
|
|||
config.get(SORT_DEFAULT_ORDER_SETTING)
|
||||
)
|
||||
)
|
||||
.setField('query', $scope.state.query || null)
|
||||
.setField('query', data.query.queryString.getQuery() || null)
|
||||
.setField('filter', filterManager.getFilters());
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
|
|
@ -35,12 +35,7 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import {
|
||||
esQuery,
|
||||
IndexPattern,
|
||||
Query,
|
||||
UI_SETTINGS,
|
||||
} from '../../../../../../../plugins/data/public';
|
||||
import { esQuery, IndexPattern, Query } from '../../../../../../../plugins/data/public';
|
||||
import { context as contextType } from '../../../../../../kibana_react/public';
|
||||
import { IndexPatternManagmentContextValue } from '../../../../types';
|
||||
import { ExecuteScript } from '../../types';
|
||||
|
@ -248,10 +243,7 @@ export class TestScript extends Component<TestScriptProps, TestScriptState> {
|
|||
showFilterBar={false}
|
||||
showDatePicker={false}
|
||||
showQueryInput={true}
|
||||
query={{
|
||||
language: this.context.services.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
query: '',
|
||||
}}
|
||||
query={this.context.services.data.query.queryString.getDefaultQuery()}
|
||||
onQuerySubmit={this.previewScript}
|
||||
indexPatterns={[this.props.indexPattern]}
|
||||
customSubmitButton={
|
||||
|
|
|
@ -23,7 +23,7 @@ import { htmlIdGenerator, EuiButton, EuiSpacer } from '@elastic/eui';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { useMount } from 'react-use';
|
||||
|
||||
import { Query, UI_SETTINGS } from '../../../../data/public';
|
||||
import { Query } from '../../../../data/public';
|
||||
import { useKibana } from '../../../../kibana_react/public';
|
||||
import { FilterRow } from './filter';
|
||||
import { AggParamEditorProps } from '../agg_param_props';
|
||||
|
@ -70,7 +70,7 @@ function FiltersParamEditor({ agg, value = [], setValue }: AggParamEditorProps<F
|
|||
updateFilters([
|
||||
...filters,
|
||||
{
|
||||
input: { query: '', language: services.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE) },
|
||||
input: services.data.query.queryString.getDefaultQuery(),
|
||||
label: '',
|
||||
id: generateId(),
|
||||
},
|
||||
|
|
|
@ -157,7 +157,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
|
|||
if (!adapters) return;
|
||||
|
||||
return this.deps.start().plugins.inspector.open(adapters, {
|
||||
title: this.getTitle() || '',
|
||||
title: this.getTitle() || this.title || '',
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -215,8 +215,17 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
// propagate the title to the output embeddable
|
||||
// but only when the visualization is in edit/Visualize mode
|
||||
if (!this.parent && this.vis.title !== this.output.title) {
|
||||
this.updateOutput({ title: this.vis.title });
|
||||
}
|
||||
|
||||
// Keep title depending on the output Embeddable to decouple the
|
||||
// visual appearance of the title and the actual title content (useful in Dashboard)
|
||||
if (this.output.title !== this.title) {
|
||||
this.title = this.output.title;
|
||||
|
||||
if (this.domNode) {
|
||||
this.domNode.setAttribute('data-title', this.title || '');
|
||||
}
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
*/
|
||||
|
||||
import React, { memo, useCallback, useMemo, useState, useEffect } from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { OverlayRef } from 'kibana/public';
|
||||
import { Query } from 'src/plugins/data/public';
|
||||
import { useKibana } from '../../../../kibana_react/public';
|
||||
import {
|
||||
VisualizeServices,
|
||||
|
@ -68,15 +66,13 @@ const TopNav = ({
|
|||
setInspectorSession(session);
|
||||
}, [embeddableHandler]);
|
||||
|
||||
const updateQuery = useCallback(
|
||||
({ query }: { query?: Query }) => {
|
||||
if (!isEqual(currentAppState.query, query)) {
|
||||
stateContainer.transitions.set('query', query || currentAppState.query);
|
||||
} else {
|
||||
const handleRefresh = useCallback(
|
||||
(_payload: any, isUpdate?: boolean) => {
|
||||
if (isUpdate === false) {
|
||||
savedVisInstance.embeddableHandler.reload();
|
||||
}
|
||||
},
|
||||
[currentAppState.query, savedVisInstance.embeddableHandler, stateContainer.transitions]
|
||||
[savedVisInstance.embeddableHandler]
|
||||
);
|
||||
|
||||
const config = useMemo(() => {
|
||||
|
@ -149,8 +145,7 @@ const TopNav = ({
|
|||
<TopNavMenu
|
||||
appName={APP_NAME}
|
||||
config={config}
|
||||
query={currentAppState.query}
|
||||
onQuerySubmit={updateQuery}
|
||||
onQuerySubmit={handleRefresh}
|
||||
savedQueryId={currentAppState.savedQuery}
|
||||
onSavedQueryIdChange={stateContainer.transitions.updateSavedQuery}
|
||||
indexPatterns={indexPattern ? [indexPattern] : undefined}
|
||||
|
|
|
@ -105,10 +105,16 @@ describe('useEditorUpdates', () => {
|
|||
to: 'now',
|
||||
};
|
||||
mockFilters = ['mockFilters'];
|
||||
const mockQuery = {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
};
|
||||
// @ts-expect-error
|
||||
mockServices.data.query.timefilter.timefilter.getTime.mockImplementation(() => timeRange);
|
||||
// @ts-expect-error
|
||||
mockServices.data.query.filterManager.getFilters.mockImplementation(() => mockFilters);
|
||||
// @ts-expect-error
|
||||
mockServices.data.query.queryString.getQuery.mockImplementation(() => mockQuery);
|
||||
});
|
||||
|
||||
test('should set up current app state and render the editor', () => {
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
import { EventEmitter } from 'events';
|
||||
import { merge } from 'rxjs';
|
||||
|
||||
import { migrateLegacyQuery } from '../../../../../kibana_legacy/public';
|
||||
import {
|
||||
VisualizeServices,
|
||||
VisualizeAppState,
|
||||
|
@ -47,6 +45,8 @@ export const useEditorUpdates = (
|
|||
const {
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
state$,
|
||||
} = services.data.query;
|
||||
const { embeddableHandler, savedVis, savedSearch, vis } = savedVisInstance;
|
||||
const initialState = appState.getState();
|
||||
|
@ -60,7 +60,7 @@ export const useEditorUpdates = (
|
|||
uiState: vis.uiState,
|
||||
timeRange: timefilter.getTime(),
|
||||
filters: filterManager.getFilters(),
|
||||
query: appState.getState().query,
|
||||
query: queryString.getQuery(),
|
||||
linked: !!vis.data.savedSearchId,
|
||||
savedSearch,
|
||||
});
|
||||
|
@ -68,17 +68,12 @@ export const useEditorUpdates = (
|
|||
embeddableHandler.updateInput({
|
||||
timeRange: timefilter.getTime(),
|
||||
filters: filterManager.getFilters(),
|
||||
query: appState.getState().query,
|
||||
query: queryString.getQuery(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const subscriptions = merge(
|
||||
timefilter.getTimeUpdate$(),
|
||||
timefilter.getAutoRefreshFetch$(),
|
||||
timefilter.getFetch$(),
|
||||
filterManager.getFetches$()
|
||||
).subscribe({
|
||||
const subscriptions = state$.subscribe({
|
||||
next: reloadVisualization,
|
||||
error: services.fatalErrors.add,
|
||||
});
|
||||
|
@ -116,10 +111,6 @@ export const useEditorUpdates = (
|
|||
// and initializing different visualizations
|
||||
return;
|
||||
}
|
||||
const newQuery = migrateLegacyQuery(state.query);
|
||||
if (!isEqual(state.query, newQuery)) {
|
||||
appState.transitions.set('query', newQuery);
|
||||
}
|
||||
|
||||
if (!isEqual(state.uiState, vis.uiState.getChanges())) {
|
||||
vis.uiState.set(state.uiState);
|
||||
|
|
|
@ -96,6 +96,7 @@ describe('useVisualizeAppState', () => {
|
|||
);
|
||||
expect(connectToQueryState).toHaveBeenCalledWith(mockServices.data.query, expect.any(Object), {
|
||||
filters: 'appState',
|
||||
query: true,
|
||||
});
|
||||
expect(result.current).toEqual({
|
||||
appState: stateContainer,
|
||||
|
|
|
@ -24,6 +24,7 @@ import { EventEmitter } from 'events';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { MarkdownSimple, toMountPoint } from '../../../../../kibana_react/public';
|
||||
import { migrateLegacyQuery } from '../../../../../kibana_legacy/public';
|
||||
import { esFilters, connectToQueryState } from '../../../../../data/public';
|
||||
import { VisualizeServices, VisualizeAppStateContainer, SavedVisInstance } from '../../types';
|
||||
import { visStateToEditorState } from '../utils';
|
||||
|
@ -61,19 +62,35 @@ export const useVisualizeAppState = (
|
|||
|
||||
eventEmitter.on('dirtyStateChange', onDirtyStateChange);
|
||||
|
||||
const { filterManager } = services.data.query;
|
||||
// sync initial app filters from state to filterManager
|
||||
const { filterManager, queryString } = services.data.query;
|
||||
// sync initial app state from state to managers
|
||||
filterManager.setAppFilters(cloneDeep(stateContainer.getState().filters));
|
||||
// setup syncing of app filters between appState and filterManager
|
||||
queryString.setQuery(migrateLegacyQuery(stateContainer.getState().query));
|
||||
|
||||
// setup syncing of app filters between appState and query services
|
||||
const stopSyncingAppFilters = connectToQueryState(
|
||||
services.data.query,
|
||||
{
|
||||
set: ({ filters }) => stateContainer.transitions.set('filters', filters),
|
||||
get: () => ({ filters: stateContainer.getState().filters }),
|
||||
state$: stateContainer.state$.pipe(map((state) => ({ filters: state.filters }))),
|
||||
set: ({ filters, query }) => {
|
||||
stateContainer.transitions.set('filters', filters);
|
||||
stateContainer.transitions.set('query', query);
|
||||
},
|
||||
get: () => {
|
||||
return {
|
||||
filters: stateContainer.getState().filters,
|
||||
query: stateContainer.getState().query,
|
||||
};
|
||||
},
|
||||
state$: stateContainer.state$.pipe(
|
||||
map((state) => ({
|
||||
filters: state.filters,
|
||||
query: state.query,
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
filters: esFilters.FilterStateStore.APP_STATE,
|
||||
query: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ChromeStart, DocLinksStart } from 'kibana/public';
|
||||
import { Filter, UI_SETTINGS } from '../../../../data/public';
|
||||
import { Filter } from '../../../../data/public';
|
||||
import { VisualizeServices, SavedVisInstance } from '../types';
|
||||
|
||||
export const addHelpMenuToAppChrome = (chrome: ChromeStart, docLinks: DocLinksStart) => {
|
||||
|
@ -49,12 +49,9 @@ export const addBadgeToAppChrome = (chrome: ChromeStart) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const getDefaultQuery = ({ localStorage, uiSettings }: VisualizeServices) => ({
|
||||
query: '',
|
||||
language:
|
||||
localStorage.get('kibana.userQueryLanguage') ||
|
||||
uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
});
|
||||
export const getDefaultQuery = ({ data }: VisualizeServices) => {
|
||||
return data.query.queryString.getDefaultQuery();
|
||||
};
|
||||
|
||||
export const visStateToEditorState = (
|
||||
{ vis, savedVis }: SavedVisInstance,
|
||||
|
|
|
@ -563,6 +563,10 @@ export default function ({ getService, getPageObjects }) {
|
|||
|
||||
it('should display updated scaled label text after time range is changed', async () => {
|
||||
await PageObjects.visEditor.setInterval('Millisecond');
|
||||
|
||||
// Apply interval
|
||||
await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton');
|
||||
|
||||
const isHelperScaledLabelExists = await find.existsByCssSelector(
|
||||
'[data-test-subj="currentlyScaledText"]'
|
||||
);
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
|
||||
export const AlignmentGuide = ({ transformMatrix, width, height }) => {
|
||||
const newStyle = {
|
||||
width,
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
background: 'magenta',
|
||||
position: 'absolute',
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className="canvasAlignmentGuide canvasInteractable canvasLayoutAnnotation"
|
||||
style={newStyle}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
AlignmentGuide.propTypes = {
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
.canvasAlignmentGuide {
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { FC, ReactEventHandler } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
EuiDescriptionList,
|
||||
|
@ -12,7 +12,13 @@ import {
|
|||
EuiDescriptionListDescription,
|
||||
} from '@elastic/eui';
|
||||
|
||||
export const ArgAdd = ({ onValueAdd, displayName, help }) => {
|
||||
interface Props {
|
||||
displayName: string;
|
||||
help: string;
|
||||
onValueAdd?: ReactEventHandler;
|
||||
}
|
||||
|
||||
export const ArgAdd: FC<Props> = ({ onValueAdd = () => {}, displayName, help }) => {
|
||||
return (
|
||||
<button className="canvasArg__add" onClick={onValueAdd}>
|
||||
<EuiDescriptionList compressed>
|
||||
|
@ -26,7 +32,7 @@ export const ArgAdd = ({ onValueAdd, displayName, help }) => {
|
|||
};
|
||||
|
||||
ArgAdd.propTypes = {
|
||||
displayName: PropTypes.string,
|
||||
help: PropTypes.string,
|
||||
displayName: PropTypes.string.isRequired,
|
||||
help: PropTypes.string.isRequired,
|
||||
onValueAdd: PropTypes.func,
|
||||
};
|
|
@ -4,8 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { ArgAdd as Component } from './arg_add';
|
||||
|
||||
export const ArgAdd = pure(Component);
|
||||
export { ArgAdd } from './arg_add';
|
|
@ -4,12 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { MouseEventHandler, FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiButtonIcon } from '@elastic/eui';
|
||||
// @ts-expect-error untyped local
|
||||
import { Popover, PopoverChildrenProps } from '../popover';
|
||||
// @ts-expect-error untyped local
|
||||
import { ArgAdd } from '../arg_add';
|
||||
// @ts-expect-error untyped local
|
||||
import { Arg } from '../../expression_types/arg';
|
||||
|
@ -27,8 +26,8 @@ interface Props {
|
|||
options: ArgOptions[];
|
||||
}
|
||||
|
||||
export const ArgAddPopover = ({ options }: Props) => {
|
||||
const button = (handleClick: React.MouseEventHandler<HTMLButtonElement>) => (
|
||||
export const ArgAddPopover: FC<Props> = ({ options }) => {
|
||||
const button = (handleClick: MouseEventHandler<HTMLButtonElement>) => (
|
||||
<EuiButtonIcon
|
||||
iconType="plusInCircle"
|
||||
aria-label={strings.getAddAriaLabel()}
|
||||
|
|
|
@ -4,7 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
import { ArgAddPopover as Component } from './arg_add_popover';
|
||||
|
||||
export const ArgAddPopover = pure(Component);
|
||||
export { ArgAddPopover } from './arg_add_popover';
|
||||
|
|
|
@ -229,545 +229,6 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/Assets/AssetManager redux 1`] = `
|
||||
Array [
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
/>,
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={1}
|
||||
/>,
|
||||
<div
|
||||
data-focus-lock-disabled={false}
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
>
|
||||
<div
|
||||
className="euiModal canvasAssetManager canvasModal--fixedSize"
|
||||
onKeyDown={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"maxWidth": "1000px",
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this modal window"
|
||||
className="euiButtonIcon euiButtonIcon--text euiModal__closeIcon"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="euiModal__flex"
|
||||
>
|
||||
<div
|
||||
className="euiModalHeader canvasAssetManager__modalHeader"
|
||||
>
|
||||
<div
|
||||
className="euiModalHeader__title canvasAssetManager__modalHeaderTitle"
|
||||
>
|
||||
Manage workpad assets
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive canvasAssetManager__fileUploadWrapper"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
className="euiFilePicker euiFilePicker--compressed"
|
||||
>
|
||||
<div
|
||||
className="euiFilePicker__wrap"
|
||||
>
|
||||
<input
|
||||
accept="image/*"
|
||||
aria-describedby="generated-id"
|
||||
className="euiFilePicker__input"
|
||||
multiple={true}
|
||||
onChange={[Function]}
|
||||
onDragLeave={[Function]}
|
||||
onDragOver={[Function]}
|
||||
onDrop={[Function]}
|
||||
type="file"
|
||||
/>
|
||||
<div
|
||||
className="euiFilePicker__prompt"
|
||||
id="generated-id"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiFilePicker__icon"
|
||||
data-euiicon-type="importAction"
|
||||
size="m"
|
||||
/>
|
||||
<div
|
||||
className="euiFilePicker__promptText"
|
||||
>
|
||||
Select or drag and drop images
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiModalBody"
|
||||
>
|
||||
<div
|
||||
className="euiModalBody__overflow"
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--small"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
<p>
|
||||
Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--l"
|
||||
/>
|
||||
<div
|
||||
className="euiFlexGrid euiFlexGrid--gutterLarge euiFlexGrid--fourths euiFlexGrid--responsive"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem"
|
||||
>
|
||||
<div
|
||||
className="euiPanel euiPanel--paddingSmall canvasAsset"
|
||||
>
|
||||
<div
|
||||
className="canvasAsset__thumb canvasCheckered"
|
||||
>
|
||||
<figure
|
||||
className="euiImage canvasAsset__img "
|
||||
>
|
||||
<img
|
||||
alt="Asset thumbnail"
|
||||
className="euiImage__img"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1Ni4zMSA1Ni4zMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7c3Ryb2tlOiMwMDc4YTA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlBsYW5lIEljb248L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNDkuNTEsNDguOTMsNDEuMjYsMjIuNTIsNTMuNzYsMTBhNS4yOSw1LjI5LDAsMCwwLTcuNDgtNy40N2wtMTIuNSwxMi41TDcuMzgsNi43OUEuNy43LDAsMCwwLDYuNjksN0wxLjIsMTIuNDVhLjcuNywwLDAsMCwwLDFMMTkuODUsMjlsLTcuMjQsNy4yNC03Ljc0LS42YS43MS43MSwwLDAsMC0uNTMuMkwxLjIxLDM5YS42Ny42NywwLDAsMCwuMDgsMUw5LjQ1LDQ2bC4wNywwYy4xMS4xMy4yMi4yNi4zNC4zOHMuMjUuMjMuMzguMzRhLjM2LjM2LDAsMCwwLDAsLjA3TDE2LjMzLDU1YS42OC42OCwwLDAsMCwxLC4wN0wyMC40OSw1MmEuNjcuNjcsMCwwLDAsLjE5LS41NGwtLjU5LTcuNzQsNy4yNC03LjI0TDQyLjg1LDU1LjA2YS42OC42OCwwLDAsMCwxLDBsNS41LTUuNUEuNjYuNjYsMCwwLDAsNDkuNTEsNDguOTNaIi8+PC9nPjwvZz48L3N2Zz4="
|
||||
style={Object {}}
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--s"
|
||||
/>
|
||||
<div
|
||||
className="euiText euiText--extraSmall eui-textBreakAll"
|
||||
>
|
||||
<p
|
||||
className="eui-textBreakAll"
|
||||
>
|
||||
<strong>
|
||||
airplane
|
||||
</strong>
|
||||
<br />
|
||||
<span
|
||||
className="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
<small>
|
||||
(
|
||||
1
|
||||
kb)
|
||||
</small>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--s"
|
||||
/>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsBaseline euiFlexGroup--justifyContentCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero asset-create-image"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-label="Create image element"
|
||||
className="euiButtonIcon euiButtonIcon--primary"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="vector"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero asset-download"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<div
|
||||
className="canvasDownload"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<button
|
||||
aria-label="Download"
|
||||
className="euiButtonIcon euiButtonIcon--primary"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="sortDown"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<div
|
||||
className="canvasClipboard"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<button
|
||||
aria-label="Copy id to clipboard"
|
||||
className="euiButtonIcon euiButtonIcon--primary"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="copyClipboard"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-label="Delete"
|
||||
className="euiButtonIcon euiButtonIcon--danger"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="trash"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem"
|
||||
>
|
||||
<div
|
||||
className="euiPanel euiPanel--paddingSmall canvasAsset"
|
||||
>
|
||||
<div
|
||||
className="canvasAsset__thumb canvasCheckered"
|
||||
>
|
||||
<figure
|
||||
className="euiImage canvasAsset__img "
|
||||
>
|
||||
<img
|
||||
alt="Asset thumbnail"
|
||||
className="euiImage__img"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzOC4zOSA1Ny41NyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7c3Ryb2tlOiMwMTliOGY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPkxvY2F0aW9uIEljb248L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMTkuMTksMUExOC4xOSwxOC4xOSwwLDAsMCwyLjk0LDI3LjM2aDBhMTkuNTEsMTkuNTEsMCwwLDAsMSwxLjc4TDE5LjE5LDU1LjU3LDM0LjM4LDI5LjIxQTE4LjE5LDE4LjE5LDAsMCwwLDE5LjE5LDFabTAsMjMuMjlhNS41Myw1LjUzLDAsMSwxLDUuNTMtNS41M0E1LjUzLDUuNTMsMCwwLDEsMTkuMTksMjQuMjlaIi8+PC9nPjwvZz48L3N2Zz4="
|
||||
style={Object {}}
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--s"
|
||||
/>
|
||||
<div
|
||||
className="euiText euiText--extraSmall eui-textBreakAll"
|
||||
>
|
||||
<p
|
||||
className="eui-textBreakAll"
|
||||
>
|
||||
<strong>
|
||||
marker
|
||||
</strong>
|
||||
<br />
|
||||
<span
|
||||
className="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
<small>
|
||||
(
|
||||
1
|
||||
kb)
|
||||
</small>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="euiSpacer euiSpacer--s"
|
||||
/>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsBaseline euiFlexGroup--justifyContentCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero asset-create-image"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-label="Create image element"
|
||||
className="euiButtonIcon euiButtonIcon--primary"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="vector"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero asset-download"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<div
|
||||
className="canvasDownload"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<button
|
||||
aria-label="Download"
|
||||
className="euiButtonIcon euiButtonIcon--primary"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="sortDown"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<div
|
||||
className="canvasClipboard"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<button
|
||||
aria-label="Copy id to clipboard"
|
||||
className="euiButtonIcon euiButtonIcon--primary"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="copyClipboard"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<span
|
||||
className="euiToolTipAnchor"
|
||||
onKeyUp={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<button
|
||||
aria-label="Delete"
|
||||
className="euiButtonIcon euiButtonIcon--danger"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="trash"
|
||||
size="m"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="euiModalFooter canvasAssetManager__modalFooter"
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow canvasAssetManager__meterWrapper"
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem"
|
||||
>
|
||||
<progress
|
||||
aria-labelledby="CanvasAssetManagerLabel"
|
||||
className="euiProgress euiProgress--native euiProgress--s euiProgress--secondary"
|
||||
max={25000}
|
||||
value={2}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero eui-textNoWrap"
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--medium"
|
||||
id="CanvasAssetManagerLabel"
|
||||
>
|
||||
0% space used
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="euiButton euiButton--primary euiButton--small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="euiButton__content"
|
||||
>
|
||||
<span
|
||||
className="euiButton__text"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={0}
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/Assets/AssetManager two assets 1`] = `
|
||||
Array [
|
||||
<div
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
|
||||
export const BorderConnection = ({ transformMatrix, width, height }) => {
|
||||
const newStyle = {
|
||||
width,
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
position: 'absolute',
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
};
|
||||
return <div className="canvasBorder--connection canvasLayoutAnnotation" style={newStyle} />;
|
||||
};
|
||||
|
||||
BorderConnection.propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
.canvasBorder--connection {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
border-top: 1px dashed $euiColorLightShade;
|
||||
border-left: 1px dashed $euiColorLightShade;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
import { BorderConnection as Component } from './border_connection';
|
||||
|
||||
export const BorderConnection = pure(Component);
|
|
@ -1,13 +0,0 @@
|
|||
.canvasBorderResizeHandle {
|
||||
@include euiSlightShadow;
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
margin-left: -4px;
|
||||
margin-top: -4px;
|
||||
background-color: $euiColorEmptyShade;
|
||||
border: 1px solid $euiColorDarkShade;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
import { BorderResizeHandle as Component } from './border_resize_handle';
|
||||
|
||||
export const BorderResizeHandle = pure(Component);
|
|
@ -4,18 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC, ReactNode } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { FunctionComponent, ReactNode } from 'react';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
export interface Props {
|
||||
/** Any valid CSS color. If not a valid CSS string, the dot will be transparent and checkered */
|
||||
value?: string;
|
||||
interface Props {
|
||||
/** Nodes to display within the dot. Should fit within the constraints. */
|
||||
children?: ReactNode;
|
||||
/** Any valid CSS color. If not a valid CSS string, the dot will be transparent and checkered */
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export const ColorDot: FunctionComponent<Props> = ({ value, children }) => {
|
||||
export const ColorDot: FC<Props> = ({ value, children }) => {
|
||||
const tc = tinycolor(value);
|
||||
let style = {};
|
||||
|
||||
|
@ -34,6 +34,6 @@ export const ColorDot: FunctionComponent<Props> = ({ value, children }) => {
|
|||
};
|
||||
|
||||
ColorDot.propTypes = {
|
||||
value: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
|
|
@ -4,9 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { ColorDot as Component } from './color_dot';
|
||||
|
||||
export { Props } from './color_dot';
|
||||
export const ColorDot = pure(Component);
|
||||
export { ColorDot } from './color_dot';
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiButtonIcon, EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { EuiButtonIcon, EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { ColorDot } from '../color_dot/color_dot';
|
||||
|
||||
|
@ -15,17 +15,17 @@ import { ComponentStrings } from '../../../i18n/components';
|
|||
const { ColorManager: strings } = ComponentStrings;
|
||||
|
||||
export interface Props {
|
||||
/**
|
||||
* Determines if the add/remove buttons are displayed.
|
||||
* @default false
|
||||
*/
|
||||
hasButtons?: boolean;
|
||||
/** The function to call when the Add Color button is clicked. The button will be disabled if there is no handler. */
|
||||
onAddColor?: (value: string) => void;
|
||||
/** The function to call when the value is changed */
|
||||
onChange: (value: string) => void;
|
||||
/** The function to call when the Remove Color button is clicked. The button will be disabled if there is no handler. */
|
||||
onRemoveColor?: (value: string) => void;
|
||||
/**
|
||||
* Determines if the add/remove buttons are displayed.
|
||||
* @default false
|
||||
*/
|
||||
hasButtons?: boolean;
|
||||
/**
|
||||
* The value of the color manager. Only honors valid CSS values.
|
||||
* @default ''
|
||||
|
@ -33,12 +33,12 @@ export interface Props {
|
|||
value?: string;
|
||||
}
|
||||
|
||||
export const ColorManager: FunctionComponent<Props> = ({
|
||||
value = '',
|
||||
onAddColor,
|
||||
onRemoveColor,
|
||||
onChange,
|
||||
export const ColorManager: FC<Props> = ({
|
||||
hasButtons = false,
|
||||
onAddColor,
|
||||
onChange,
|
||||
onRemoveColor,
|
||||
value = '',
|
||||
}) => {
|
||||
const tc = tinycolor(value);
|
||||
const validColor = tc.isValid();
|
||||
|
|
|
@ -4,9 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { ColorManager as Component } from './color_manager';
|
||||
|
||||
export { Props } from './color_manager';
|
||||
export const ColorManager = pure(Component);
|
||||
export { ColorManager, Props } from './color_manager';
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiIcon, EuiLink } from '@elastic/eui';
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { EuiIcon, EuiLink } from '@elastic/eui';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { readableColor } from '../../lib/readable_color';
|
||||
import { ColorDot } from '../color_dot';
|
||||
import { ItemGrid } from '../item_grid';
|
||||
|
||||
export interface Props {
|
||||
interface Props {
|
||||
/**
|
||||
* An array of hexadecimal color values. Non-hex will be ignored.
|
||||
* @default []
|
||||
|
@ -32,7 +32,7 @@ export interface Props {
|
|||
value?: string;
|
||||
}
|
||||
|
||||
export const ColorPalette: FunctionComponent<Props> = ({
|
||||
export const ColorPalette: FC<Props> = ({
|
||||
colors = [],
|
||||
colorsPerRow = 6,
|
||||
onChange,
|
||||
|
|
|
@ -4,8 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
import { ColorPalette as Component } from './color_palette';
|
||||
|
||||
export { Props } from './color_palette';
|
||||
export const ColorPalette = pure(Component);
|
||||
export { ColorPalette } from './color_palette';
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
import { ColorManager, Props as ColorManagerProps } from '../color_manager';
|
||||
import { ColorPalette } from '../color_palette';
|
||||
|
||||
|
@ -18,7 +19,7 @@ export interface Props extends ColorManagerProps {
|
|||
colors?: string[];
|
||||
}
|
||||
|
||||
export const ColorPicker: FunctionComponent<Props> = ({
|
||||
export const ColorPicker: FC<Props> = ({
|
||||
colors = [],
|
||||
hasButtons = false,
|
||||
onAddColor,
|
||||
|
|
|
@ -4,9 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { ColorPicker as Component } from './color_picker';
|
||||
|
||||
export { Props } from './color_picker';
|
||||
export const ColorPicker = pure(Component);
|
||||
export { ColorPicker, Props } from './color_picker';
|
||||
|
|
|
@ -4,20 +4,21 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiLink, PopoverAnchorPosition } from '@elastic/eui';
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { EuiLink, PopoverAnchorPosition } from '@elastic/eui';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
import { ColorDot } from '../color_dot';
|
||||
import { ColorPicker, Props as ColorPickerProps } from '../color_picker';
|
||||
import { Popover } from '../popover';
|
||||
|
||||
export interface Props extends ColorPickerProps {
|
||||
anchorPosition: PopoverAnchorPosition;
|
||||
anchorPosition?: PopoverAnchorPosition;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export const ColorPickerPopover: FunctionComponent<Props> = (props: Props) => {
|
||||
export const ColorPickerPopover: FC<Props> = (props: Props) => {
|
||||
const { value, anchorPosition, ariaLabel, ...rest } = props;
|
||||
const button = (handleClick: React.MouseEventHandler<HTMLButtonElement>) => (
|
||||
<EuiLink
|
||||
|
|
|
@ -4,9 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { ColorPickerPopover as Component } from './color_picker_popover';
|
||||
|
||||
export { Props } from './color_picker_popover';
|
||||
export const ColorPickerPopover = pure(Component);
|
||||
export { ColorPickerPopover, Props } from './color_picker_popover';
|
||||
|
|
|
@ -4,13 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiIcon, EuiPagination } from '@elastic/eui';
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiIcon, EuiPagination } from '@elastic/eui';
|
||||
import moment from 'moment';
|
||||
import { Paginate } from '../paginate';
|
||||
import { Datatable as DatatableType, DatatableColumn } from '../../../types';
|
||||
|
||||
const getIcon = (type) => {
|
||||
type IconType = 'string' | 'number' | 'date' | 'boolean' | 'null';
|
||||
|
||||
const getIcon = (type: IconType) => {
|
||||
if (type === null) {
|
||||
return;
|
||||
}
|
||||
|
@ -36,19 +39,31 @@ const getIcon = (type) => {
|
|||
return <EuiIcon type={icon} color="subdued" />;
|
||||
};
|
||||
|
||||
const getColumnName = (col) => (typeof col === 'string' ? col : col.name);
|
||||
const getColumnName = (col: DatatableColumn) => (typeof col === 'string' ? col : col.name);
|
||||
|
||||
const getColumnType = (col) => col.type || null;
|
||||
const getColumnType = (col: DatatableColumn) => col.type || null;
|
||||
|
||||
const getFormattedValue = (val, type) => {
|
||||
const getFormattedValue = (val: any, type: any) => {
|
||||
if (type === 'date') {
|
||||
return moment(val).format();
|
||||
}
|
||||
return String(val);
|
||||
};
|
||||
|
||||
export const Datatable = ({ datatable, perPage, paginate, showHeader }) => (
|
||||
<Paginate rows={datatable.rows} perPage={perPage || 10}>
|
||||
interface Props {
|
||||
datatable: DatatableType;
|
||||
paginate?: boolean;
|
||||
perPage?: number;
|
||||
showHeader?: boolean;
|
||||
}
|
||||
|
||||
export const Datatable: FC<Props> = ({
|
||||
datatable,
|
||||
paginate = false,
|
||||
perPage = 10,
|
||||
showHeader = false,
|
||||
}) => (
|
||||
<Paginate rows={datatable.rows} perPage={perPage}>
|
||||
{({ rows, setPage, pageNumber, totalPages }) => (
|
||||
<div className="canvasDataTable">
|
||||
<div className="canvasDataTable__tableWrapper">
|
||||
|
@ -91,7 +106,7 @@ export const Datatable = ({ datatable, perPage, paginate, showHeader }) => (
|
|||
|
||||
Datatable.propTypes = {
|
||||
datatable: PropTypes.object.isRequired,
|
||||
perPage: PropTypes.number,
|
||||
paginate: PropTypes.bool,
|
||||
perPage: PropTypes.number,
|
||||
showHeader: PropTypes.bool,
|
||||
};
|
|
@ -4,7 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
import { Datatable as Component } from './datatable';
|
||||
|
||||
export const Datatable = pure(Component);
|
||||
export { Datatable } from './datatable';
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
|
||||
export const DragBoxAnnotation = ({ transformMatrix, width, height }) => {
|
||||
const newStyle = {
|
||||
width,
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
};
|
||||
return <div className="canvasDragBoxAnnotation canvasLayoutAnnotation" style={newStyle} />;
|
||||
};
|
||||
|
||||
DragBoxAnnotation.propTypes = {
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
.canvasDragBoxAnnotation {
|
||||
position: absolute;
|
||||
background: none;
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
outline: dashed 1px $euiColorDarkShade;
|
||||
pointer-events: none;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
import { DragBoxAnnotation as Component } from './dragbox_annotation';
|
||||
|
||||
export const DragBoxAnnotation = pure(Component);
|
|
@ -7,8 +7,8 @@
|
|||
import { action } from '@storybook/addon-actions';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
import { americanTypewriter } from '../../../common/lib/fonts';
|
||||
import { FontPicker } from './font_picker';
|
||||
import { americanTypewriter } from '../../../../common/lib/fonts';
|
||||
import { FontPicker } from '../font_picker';
|
||||
|
||||
storiesOf('components/FontPicker', module)
|
||||
.add('default', () => <FontPicker onSelect={action('onSelect')} />)
|
|
@ -4,14 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiSuperSelect } from '@elastic/eui';
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { EuiSuperSelect } from '@elastic/eui';
|
||||
import { fonts, FontValue } from '../../../common/lib/fonts';
|
||||
|
||||
interface DisplayedFont {
|
||||
value: string;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
@ -19,9 +19,7 @@ interface Props {
|
|||
value?: FontValue;
|
||||
}
|
||||
|
||||
export const FontPicker: FunctionComponent<Props> = (props) => {
|
||||
const { value, onSelect } = props;
|
||||
|
||||
export const FontPicker: FC<Props> = ({ value, onSelect }) => {
|
||||
// While fonts are strongly-typed, we also support custom fonts someone might type in.
|
||||
// So let's cast the fonts and allow for additions.
|
||||
const displayedFonts: DisplayedFont[] = fonts;
|
||||
|
@ -46,10 +44,10 @@ export const FontPicker: FunctionComponent<Props> = (props) => {
|
|||
};
|
||||
|
||||
FontPicker.propTypes = {
|
||||
/** Initial value of the Font Picker. */
|
||||
value: PropTypes.string,
|
||||
/** Function to execute when a Font is selected. */
|
||||
onSelect: PropTypes.func,
|
||||
/** Initial value of the Font Picker. */
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
FontPicker.displayName = 'FontPicker';
|
||||
|
|
|
@ -4,8 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { FontPicker as Component } from './font_picker';
|
||||
|
||||
export const FontPicker = pure(Component);
|
||||
export { FontPicker } from './font_picker';
|
|
@ -1,8 +0,0 @@
|
|||
.canvasHoverAnnotation {
|
||||
position: absolute;
|
||||
background: none;
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
outline: solid 1px $euiColorVis0;
|
||||
pointer-events: none;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { pure } from 'recompose';
|
||||
import { HoverAnnotation as Component } from './hover_annotation';
|
||||
|
||||
export const HoverAnnotation = pure(Component);
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
import { TransformMatrix3d } from '../../lib/aeroelastic';
|
||||
|
||||
interface Props {
|
||||
height: number;
|
||||
transformMatrix: TransformMatrix3d;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export const AlignmentGuide: FC<Props> = ({ transformMatrix, width, height }) => (
|
||||
<div
|
||||
className="canvasAlignmentGuide canvasInteractable canvasLayoutAnnotation"
|
||||
style={{
|
||||
background: 'magenta',
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
position: 'absolute',
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
width,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
AlignmentGuide.propTypes = {
|
||||
height: PropTypes.number.isRequired,
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
import { TransformMatrix3d } from '../../lib/aeroelastic';
|
||||
|
||||
interface Props {
|
||||
height: number;
|
||||
transformMatrix: TransformMatrix3d;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export const BorderConnection: FC<Props> = ({ transformMatrix, width, height }) => (
|
||||
<div
|
||||
className="canvasBorderConnection canvasLayoutAnnotation"
|
||||
style={{
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
position: 'absolute',
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
width,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
BorderConnection.propTypes = {
|
||||
height: PropTypes.number.isRequired,
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
};
|
|
@ -4,11 +4,17 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
import { TransformMatrix3d } from '../../lib/aeroelastic';
|
||||
|
||||
export const BorderResizeHandle = ({ transformMatrix, zoomScale }) => (
|
||||
interface Props {
|
||||
transformMatrix: TransformMatrix3d;
|
||||
zoomScale?: number;
|
||||
}
|
||||
|
||||
export const BorderResizeHandle: FC<Props> = ({ transformMatrix, zoomScale = 1 }) => (
|
||||
<div
|
||||
className="canvasBorderResizeHandle canvasLayoutAnnotation"
|
||||
style={{
|
||||
|
@ -19,4 +25,5 @@ export const BorderResizeHandle = ({ transformMatrix, zoomScale }) => (
|
|||
|
||||
BorderResizeHandle.propTypes = {
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
zoomScale: PropTypes.number,
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
import { TransformMatrix3d } from '../../lib/aeroelastic';
|
||||
|
||||
interface Props {
|
||||
height: number;
|
||||
transformMatrix: TransformMatrix3d;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export const DragBoxAnnotation: FC<Props> = ({ transformMatrix, width, height }) => (
|
||||
<div
|
||||
className="canvasDragBoxAnnotation canvasLayoutAnnotation"
|
||||
style={{
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
width,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
DragBoxAnnotation.propTypes = {
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
};
|
|
@ -4,23 +4,32 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { matrixToCSS } from '../../lib/dom';
|
||||
import { TransformMatrix3d } from '../../lib/aeroelastic';
|
||||
|
||||
export const HoverAnnotation = ({ transformMatrix, width, height }) => {
|
||||
const newStyle = {
|
||||
width,
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
};
|
||||
return <div className="canvasHoverAnnotation canvasLayoutAnnotation" style={newStyle} />;
|
||||
};
|
||||
interface Props {
|
||||
height: number;
|
||||
transformMatrix: TransformMatrix3d;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export const HoverAnnotation: FC<Props> = ({ transformMatrix, width, height }) => (
|
||||
<div
|
||||
className="canvasHoverAnnotation canvasLayoutAnnotation"
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
marginLeft: -width / 2,
|
||||
marginTop: -height / 2,
|
||||
transform: matrixToCSS(transformMatrix),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
HoverAnnotation.propTypes = {
|
||||
height: PropTypes.number.isRequired,
|
||||
transformMatrix: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { AlignmentGuide } from './alignment_guide';
|
||||
export { DragBoxAnnotation } from './dragbox_annotation';
|
||||
export { HoverAnnotation } from './hover_annotation';
|
||||
export { TooltipAnnotation } from './tooltip_annotation';
|
||||
export { RotationHandle } from './rotation_handle';
|
||||
export { BorderConnection } from './border_connection';
|
||||
export { BorderResizeHandle } from './border_resize_handle';
|
|
@ -0,0 +1,81 @@
|
|||
.canvasAlignmentGuide {
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.canvasBorderConnection {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
border-top: 1px dashed $euiColorLightShade;
|
||||
border-left: 1px dashed $euiColorLightShade;
|
||||
}
|
||||
|
||||
.canvasBorderResizeHandle {
|
||||
@include euiSlightShadow;
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: $euiSizeS;
|
||||
width: $euiSizeS;
|
||||
margin-left: -4px;
|
||||
margin-top: -4px;
|
||||
background-color: $euiColorEmptyShade;
|
||||
border: 1px solid $euiColorDarkShade;
|
||||
}
|
||||
|
||||
.canvasDragBoxAnnotation {
|
||||
position: absolute;
|
||||
background: none;
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
outline: dashed 1px $euiColorDarkShade;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.canvasHoverAnnotation {
|
||||
position: absolute;
|
||||
background: none;
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
outline: solid 1px $euiColorVis0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.canvasRotationHandle {
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: $euiSizeL;
|
||||
width: 0;
|
||||
margin-left: -1px;
|
||||
margin-top: -12px;
|
||||
border-top: 1px dashed $euiColorLightShade;
|
||||
border-left: 1px dashed $euiColorLightShade;
|
||||
}
|
||||
|
||||
.canvasRotationHandle__handle {
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 9px;
|
||||
width: 9px;
|
||||
margin-left: -5px;
|
||||
margin-top: -6px;
|
||||
border-radius: 50%;
|
||||
background-color: $euiColorMediumShade;
|
||||
}
|
||||
|
||||
.tooltipAnnotation {
|
||||
@include euiToolTipStyle($size: 's');
|
||||
position: absolute;
|
||||
transform-origin: center center; /* the default, only for clarity */
|
||||
transform-style: preserve-3d;
|
||||
outline: none;
|
||||
pointer-events: none;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue