Skip to content

Commit

Permalink
Merge branch '7.x' into backport/7.x/pr-58208
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Feb 28, 2020
2 parents 8a80665 + 80349f2 commit 858cc31
Show file tree
Hide file tree
Showing 515 changed files with 5,678 additions and 3,535 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/moc
/x-pack/legacy/plugins/infra/common/graphql/types.ts
/x-pack/legacy/plugins/infra/public/graphql/types.ts
/x-pack/legacy/plugins/infra/server/graphql/types.ts
/x-pack/legacy/plugins/apm/cypress/**/snapshots.js
/x-pack/legacy/plugins/apm/e2e/cypress/**/snapshots.js
/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken
**/graphql/types.ts
**/*.js.snap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
* under the License.
*/

export { PersistedState } from './persisted_state';
export const PLUGIN_ID = 'stateContainersExampleWithDataServices';
export const PLUGIN_NAME = 'State containers example - with data services';
4 changes: 2 additions & 2 deletions examples/state_containers_examples/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["state_containers_examples"],
"server": false,
"server": true,
"ui": true,
"requiredPlugins": [],
"requiredPlugins": ["navigation", "data"],
"optionalPlugins": []
}
23 changes: 19 additions & 4 deletions examples/state_containers_examples/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
*/

import { AppMountParameters, CoreSetup, Plugin } from 'kibana/public';
import { AppPluginDependencies } from './with_data_services/types';
import { PLUGIN_ID, PLUGIN_NAME } from '../common';

export class StateContainersExamplesPlugin implements Plugin {
public setup(core: CoreSetup) {
core.application.register({
id: 'state-containers-example-browser-history',
id: 'stateContainersExampleBrowserHistory',
title: 'State containers example - browser history routing',
async mount(params: AppMountParameters) {
const { renderApp, History } = await import('./app');
const { renderApp, History } = await import('./todo/app');
return renderApp(params, {
appInstanceId: '1',
appTitle: 'Routing with browser history',
Expand All @@ -34,17 +36,30 @@ export class StateContainersExamplesPlugin implements Plugin {
},
});
core.application.register({
id: 'state-containers-example-hash-history',
id: 'stateContainersExampleHashHistory',
title: 'State containers example - hash history routing',
async mount(params: AppMountParameters) {
const { renderApp, History } = await import('./app');
const { renderApp, History } = await import('./todo/app');
return renderApp(params, {
appInstanceId: '2',
appTitle: 'Routing with hash history',
historyType: History.Hash,
});
},
});

core.application.register({
id: PLUGIN_ID,
title: PLUGIN_NAME,
async mount(params: AppMountParameters) {
// Load application bundle
const { renderApp } = await import('./with_data_services/application');
// Get start services as specified in kibana.json
const [coreStart, depsStart] = await core.getStartServices();
// Render the application
return renderApp(coreStart, depsStart as AppPluginDependencies, params);
},
});
}

public start() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ import {
syncStates,
getStateFromKbnUrl,
BaseState,
} from '../../../src/plugins/kibana_utils/public';
import { useUrlTracker } from '../../../src/plugins/kibana_react/public';
} from '../../../../src/plugins/kibana_utils/public';
import { useUrlTracker } from '../../../../src/plugins/kibana_react/public';
import {
defaultState,
pureTransitions,
TodoActions,
TodoState,
} from '../../../src/plugins/kibana_utils/demos/state_containers/todomvc';
} from '../../../../src/plugins/kibana_utils/demos/state_containers/todomvc';

interface GlobalState {
text: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 React from 'react';
import ReactDOM from 'react-dom';
import { createBrowserHistory } from 'history';
import { AppMountParameters, CoreStart } from '../../../../src/core/public';
import { AppPluginDependencies } from './types';
import { StateDemoApp } from './components/app';
import { createKbnUrlStateStorage } from '../../../../src/plugins/kibana_utils/public/';

export const renderApp = (
{ notifications, http }: CoreStart,
{ navigation, data }: AppPluginDependencies,
{ appBasePath, element }: AppMountParameters
) => {
const history = createBrowserHistory({ basename: appBasePath });
const kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: false, history });

ReactDOM.render(
<StateDemoApp
notifications={notifications}
http={http}
navigation={navigation}
data={data}
history={history}
kbnUrlStateStorage={kbnUrlStateStorage}
/>,
element
);

return () => ReactDOM.unmountComponentAtNode(element);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/*
* 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 React, { useEffect, useRef, useState, useCallback } from 'react';
import { History } from 'history';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { Router } from 'react-router-dom';

import {
EuiFieldText,
EuiPage,
EuiPageBody,
EuiPageContent,
EuiPageHeader,
EuiTitle,
} from '@elastic/eui';

import { CoreStart } from '../../../../../src/core/public';
import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public';
import {
connectToQueryState,
syncQueryStateWithUrl,
DataPublicPluginStart,
IIndexPattern,
QueryState,
Filter,
esFilters,
Query,
} from '../../../../../src/plugins/data/public';
import {
BaseState,
BaseStateContainer,
createStateContainer,
createStateContainerReactHelpers,
IKbnUrlStateStorage,
ReduxLikeStateContainer,
syncState,
} from '../../../../../src/plugins/kibana_utils/public';
import { PLUGIN_ID, PLUGIN_NAME } from '../../../common';

interface StateDemoAppDeps {
notifications: CoreStart['notifications'];
http: CoreStart['http'];
navigation: NavigationPublicPluginStart;
data: DataPublicPluginStart;
history: History;
kbnUrlStateStorage: IKbnUrlStateStorage;
}

interface AppState {
name: string;
filters: Filter[];
query?: Query;
}
const defaultAppState: AppState = {
name: '',
filters: [],
};
const {
Provider: AppStateContainerProvider,
useState: useAppState,
useContainer: useAppStateContainer,
} = createStateContainerReactHelpers<ReduxLikeStateContainer<AppState>>();

const App = ({
notifications,
http,
navigation,
data,
history,
kbnUrlStateStorage,
}: StateDemoAppDeps) => {
const appStateContainer = useAppStateContainer();
const appState = useAppState();

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>;

// Render the application DOM.
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
return (
<Router history={history}>
<I18nProvider>
<>
<navigation.ui.TopNavMenu
appName={PLUGIN_ID}
showSearchBar={true}
indexPatterns={[indexPattern]}
useDefaultBehaviors={true}
onQuerySubmit={onQuerySubmit}
query={appState.query}
showSaveQuery={true}
/>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="stateDemo.helloWorldText"
defaultMessage="{name}!"
values={{ name: PLUGIN_NAME }}
/>
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiPageContent>
<EuiFieldText
placeholder="Additional application state: My name is..."
value={appState.name}
onChange={e => appStateContainer.set({ ...appState, name: e.target.value })}
aria-label="My name"
/>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</>
</I18nProvider>
</Router>
);
};

export const StateDemoApp = (props: StateDemoAppDeps) => {
const appStateContainer = useCreateStateContainer(defaultAppState);

return (
<AppStateContainerProvider value={appStateContainer}>
<App {...props} />
</AppStateContainerProvider>
);
};

function useCreateStateContainer<State extends BaseState>(
defaultState: State
): ReduxLikeStateContainer<State> {
const stateContainerRef = useRef<ReduxLikeStateContainer<State> | null>(null);
if (!stateContainerRef.current) {
stateContainerRef.current = createStateContainer(defaultState);
}
return stateContainerRef.current;
}

function useIndexPattern(data: DataPublicPluginStart) {
const [indexPattern, setIndexPattern] = useState<IIndexPattern>();
useEffect(() => {
const fetchIndexPattern = async () => {
const defaultIndexPattern = await data.indexPatterns.getDefault();
if (defaultIndexPattern) {
setIndexPattern(defaultIndexPattern);
}
};
fetchIndexPattern();
}, [data.indexPatterns]);

return indexPattern;
}

function useGlobalStateSyncing(
query: DataPublicPluginStart['query'],
kbnUrlStateStorage: IKbnUrlStateStorage
) {
// setup sync state utils
useEffect(() => {
// sync global filters, time filters, refresh interval from data.query to url '_g'
const { stop } = syncQueryStateWithUrl(query, kbnUrlStateStorage);
return () => {
stop();
};
}, [query, kbnUrlStateStorage]);
}

function useAppStateSyncing<AppState extends QueryState>(
appStateContainer: BaseStateContainer<AppState>,
query: DataPublicPluginStart['query'],
kbnUrlStateStorage: IKbnUrlStateStorage
) {
// setup sync state utils
useEffect(() => {
// sync app filters with app state container from data.query to state container
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(
query,
appStateContainer,
{ filters: esFilters.FilterStateStore.APP_STATE }
);

// sets up syncing app state container with url
const { start: startSyncingAppStateWithUrl, stop: stopSyncingAppStateWithUrl } = syncState({
storageKey: '_a',
stateStorage: kbnUrlStateStorage,
stateContainer: {
...appStateContainer,
// stateSync utils requires explicit handling of default state ("null")
set: state => state && appStateContainer.set(state),
},
});

// merge initial state from app state container and current state in url
const initialAppState: AppState = {
...appStateContainer.get(),
...kbnUrlStateStorage.get<AppState>('_a'),
};
// trigger state update. actually needed in case some data was in url
appStateContainer.set(initialAppState);

// set current url to whatever is in app state container
kbnUrlStateStorage.set<AppState>('_a', initialAppState);

// finally start syncing state containers with url
startSyncingAppStateWithUrl();

return () => {
stopSyncingQueryAppStateWithStateContainer();
stopSyncingAppStateWithUrl();
};
}, [query, kbnUrlStateStorage, appStateContainer]);
}
Loading

0 comments on commit 858cc31

Please sign in to comment.