diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.html
index 3cf8932958b6d8..b07e92fccfb4ca 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.html
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.html
@@ -4,13 +4,17 @@
>
-
-
-
-
{{screenTitle}}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx
index c0a0693431295b..389e00efd92047 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app.tsx
@@ -76,6 +76,12 @@ export interface DashboardAppScope extends ng.IScope {
onSavedQueryUpdated: (savedQuery: SavedQuery) => void;
onClearSavedQuery: () => void;
topNavMenu: any;
+ showTopNav: () => boolean;
+ showTopNavMenu: () => boolean;
+ showSearchBar: () => boolean;
+ showQueryBar: () => boolean;
+ showQueryInput: () => boolean;
+ showDatePicker: () => boolean;
showFilterBar: () => boolean;
showAddPanel: any;
showSaveQuery: boolean;
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
index 075516d52bab63..2b32d8d14e0f85 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
@@ -26,6 +26,8 @@ import angular from 'angular';
import { Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { History } from 'history';
+import { parse } from 'url';
+
import { SavedObjectSaveOpts } from 'src/plugins/saved_objects/public';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
@@ -713,8 +715,44 @@ export class DashboardAppController {
});
}
+ const hasUrlParam = (param: string): boolean =>
+ param in parse(location.hash.slice(1), true).query;
+
+ const displayIfUrlParam = (param: string): boolean =>
+ hasUrlParam(param) && !dashboardStateManager.getFullScreenMode();
+
+ const topNavUpdate = (): void => {
+ $scope.topNavMenu = $scope.showTopNavMenu()
+ ? getTopNavConfig(
+ dashboardStateManager.getViewMode(),
+ navActions,
+ dashboardConfig.getHideWriteControls()
+ )
+ : null;
+ };
+
+ $scope.$watch(
+ (): boolean => $scope.showTopNavMenu(),
+ (): void => topNavUpdate()
+ );
+
+ $scope.showTopNav = () => $scope.isVisible || $scope.showSearchBar();
+
+ $scope.showTopNavMenu = () => $scope.isVisible || displayIfUrlParam('show-top-nav-menu');
+
+ $scope.showSearchBar = () =>
+ $scope.isVisible || $scope.showQueryBar() || $scope.showFilterBar();
+
+ $scope.showQueryBar = () =>
+ $scope.isVisible || $scope.showQueryInput() || $scope.showDatePicker();
+
+ $scope.showQueryInput = () => $scope.isVisible || displayIfUrlParam('show-query-input');
+
+ $scope.showDatePicker = () => $scope.isVisible || displayIfUrlParam('show-date-picker');
+
$scope.showFilterBar = () =>
- $scope.model.filters.length > 0 || !dashboardStateManager.getFullScreenMode();
+ ($scope.model.filters.length > 0 || !dashboardStateManager.getFullScreenMode()) &&
+ !hasUrlParam('hide-filter-bar');
$scope.showAddPanel = () => {
dashboardStateManager.setFullScreenMode(false);
@@ -888,14 +926,8 @@ export class DashboardAppController {
});
});
- dashboardStateManager.registerChangeListener(() => {
- // view mode could have changed, so trigger top nav update
- $scope.topNavMenu = getTopNavConfig(
- dashboardStateManager.getViewMode(),
- navActions,
- dashboardConfig.getHideWriteControls()
- );
- });
+ // view mode could have changed, so trigger top nav update
+ dashboardStateManager.registerChangeListener(topNavUpdate);
$scope.$on('$destroy', () => {
updateSubscription.unsubscribe();
diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js
index 12c3ca2acc3cd8..77b5faa7828fa1 100644
--- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js
+++ b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js
@@ -99,12 +99,13 @@ export const createTopNavHelper = ({ TopNavMenu }) => reactDirective => {
// All modifiers default to true.
// Set to false to hide subcomponents.
+ 'showTopNavMenu',
'showSearchBar',
- 'showFilterBar',
'showQueryBar',
'showQueryInput',
- 'showDatePicker',
'showSaveQuery',
+ 'showDatePicker',
+ 'showFilterBar',
'appName',
'screenTitle',
diff --git a/src/plugins/dashboard_embeddable_container/public/plugin.tsx b/src/plugins/dashboard_embeddable_container/public/plugin.tsx
index 44c9dbf2dcc4b6..0a5ec33cb82f9a 100644
--- a/src/plugins/dashboard_embeddable_container/public/plugin.tsx
+++ b/src/plugins/dashboard_embeddable_container/public/plugin.tsx
@@ -70,6 +70,7 @@ export class DashboardEmbeddableContainerPublicPlugin
};
const ExitFullScreenButton: React.FC = props => {
+ // TODO: Only call useHideChrome if chrome is visible
useHideChrome();
return ;
};
diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss
index 1c47c28097454e..731c9f4d7f18d8 100644
--- a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss
+++ b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss
@@ -3,6 +3,10 @@
padding: 0px $euiSizeS $euiSizeS $euiSizeS;
}
+.globalQueryBar:first-child {
+ padding-top: $euiSizeS;
+}
+
.globalQueryBar:not(:empty) {
padding-bottom: $euiSizeS;
}
diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx
index ad9c8401389fac..2a28e8a5c42381 100644
--- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx
+++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx
@@ -348,7 +348,7 @@ function QueryBarTopRowUI(props: Props) {
className={classes}
responsive={!!props.showDatePicker}
gutterSize="s"
- justifyContent="flexEnd"
+ justifyContent="flexStart"
>
{renderQueryInput()}
{renderSharingMetaFields()}
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx
index 8e0e8b3031132b..4009977ff89a4b 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx
@@ -29,6 +29,7 @@ const dataShim = {
};
describe('TopNavMenu', () => {
+ const WRAPPER_SELECTOR = '.kbnTopNavMenu__wrapper';
const TOP_NAV_ITEM_SELECTOR = 'TopNavMenuItem';
const SEARCH_BAR_SELECTOR = 'SearchBar';
const menuItems: TopNavMenuData[] = [
@@ -55,6 +56,13 @@ describe('TopNavMenu', () => {
expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0);
});
+ it('Should not render the wrapper when showTopNavMenu and showSearchBar are both false', () => {
+ const component = shallowWithIntl(
+
+ );
+ expect(component.find(WRAPPER_SELECTOR).length).toBe(0);
+ });
+
it('Should render 1 menu item', () => {
const component = shallowWithIntl();
expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(1);
@@ -67,6 +75,15 @@ describe('TopNavMenu', () => {
expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0);
});
+ it('Should not render menu items when showTopNavMenu is false', () => {
+ const component = shallowWithIntl(
+
+ );
+ expect(component.find(WRAPPER_SELECTOR).length).toBe(0);
+ expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0);
+ expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0);
+ });
+
it('Should render search bar', () => {
const component = shallowWithIntl(
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
index cf39c82eff3ce5..ba55fa00e87eb5 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import React from 'react';
+import React, { ReactElement } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n/react';
@@ -28,6 +28,7 @@ import { StatefulSearchBarProps, DataPublicPluginStart } from '../../../data/pub
export type TopNavMenuProps = StatefulSearchBarProps & {
config?: TopNavMenuData[];
+ showTopNavMenu?: boolean;
showSearchBar?: boolean;
data?: DataPublicPluginStart;
};
@@ -41,8 +42,13 @@ export type TopNavMenuProps = StatefulSearchBarProps & {
*
**/
-export function TopNavMenu(props: TopNavMenuProps) {
- const { config, showSearchBar, ...searchBarProps } = props;
+export function TopNavMenu(props: TopNavMenuProps): ReactElement | null {
+ const { config, showTopNavMenu, showSearchBar, ...searchBarProps } = props;
+
+ if (!showTopNavMenu && (!showSearchBar || !props.data)) {
+ return null;
+ }
+
function renderItems() {
if (!config) return;
return config.map((menuItem: TopNavMenuData, i: number) => {
@@ -54,6 +60,21 @@ export function TopNavMenu(props: TopNavMenuProps) {
});
}
+ function renderMenu() {
+ if (!showTopNavMenu) return;
+ return (
+
+ {renderItems()}
+
+ );
+ }
+
function renderSearchBar() {
// Validate presense of all required fields
if (!showSearchBar || !props.data) return;
@@ -64,15 +85,7 @@ export function TopNavMenu(props: TopNavMenuProps) {
function renderLayout() {
return (
-
- {renderItems()}
-
+ {renderMenu()}
{renderSearchBar()}
);
@@ -82,6 +95,7 @@ export function TopNavMenu(props: TopNavMenuProps) {
}
TopNavMenu.defaultProps = {
+ showTopNavMenu: true,
showSearchBar: false,
showQueryBar: true,
showQueryInput: true,
diff --git a/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap b/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap
index c10ca551308809..0aa3dc61b62aa8 100644
--- a/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap
+++ b/src/plugins/share/public/components/__snapshots__/url_panel_content.test.tsx.snap
@@ -435,3 +435,487 @@ exports[`should hide short url section when allowShortUrl is false 1`] = `
`;
+
+exports[`should not show embedded option switches when permalink 1`] = `
+
+
+
+ }
+ labelType="label"
+ >
+
+
+
+
+
+
+ }
+ position="bottom"
+ />
+
+ ,
+ },
+ Object {
+ "data-test-subj": "exportAsSavedObject",
+ "disabled": false,
+ "id": "savedObject",
+ "label":
+
+
+
+
+
+ }
+ position="bottom"
+ />
+
+ ,
+ },
+ ]
+ }
+ />
+
+
+
+
+
+ }
+ onChange={[Function]}
+ />
+
+
+
+ }
+ position="bottom"
+ />
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`should show embedded option switches when embedded link 1`] = `
+
+
+
+ }
+ labelType="label"
+ >
+
+
+
+
+
+
+ }
+ position="bottom"
+ />
+
+ ,
+ },
+ Object {
+ "data-test-subj": "exportAsSavedObject",
+ "disabled": false,
+ "id": "savedObject",
+ "label":
+
+
+
+
+
+ }
+ position="bottom"
+ />
+
+ ,
+ },
+ ]
+ }
+ />
+
+
+
+
+
+ }
+ onChange={[Function]}
+ />
+
+
+
+ }
+ position="bottom"
+ />
+
+
+
+
+
+
+
+ }
+ onChange={[Function]}
+ />
+
+
+
+ }
+ position="bottom"
+ />
+
+
+
+
+
+
+
+ }
+ onChange={[Function]}
+ />
+
+
+
+ }
+ position="bottom"
+ />
+
+
+
+
+
+
+
+ }
+ onChange={[Function]}
+ />
+
+
+
+ }
+ position="bottom"
+ />
+
+
+
+
+
+
+
+ }
+ onChange={[Function]}
+ />
+
+
+
+ }
+ position="bottom"
+ />
+
+
+
+
+ "
+ >
+
+
+
+
+`;
diff --git a/src/plugins/share/public/components/url_panel_content.test.tsx b/src/plugins/share/public/components/url_panel_content.test.tsx
index 9db8d1ccf2efa2..4c5cd62d62ec6b 100644
--- a/src/plugins/share/public/components/url_panel_content.test.tsx
+++ b/src/plugins/share/public/components/url_panel_content.test.tsx
@@ -24,6 +24,10 @@ import { shallow } from 'enzyme';
import { UrlPanelContent } from './url_panel_content';
+const TOP_NAV_MENU_SWITCH_SELECTOR = '[data-test-subj="topNavMenuSwitch"]';
+const QUERY_INPUT_SWITCH_SELECTOR = '[data-test-subj="queryInputSwitch"]';
+const DATE_PICKER_SWITCH_SELECTOR = '[data-test-subj="datePickerSwitch"]';
+const FILTER_BAR_SWITCH_SELECTOR = '[data-test-subj="filterBarSwitch"]';
const defaultProps = {
allowShortUrl: true,
objectType: 'dashboard',
@@ -47,3 +51,21 @@ test('should hide short url section when allowShortUrl is false', () => {
);
expect(component).toMatchSnapshot();
});
+
+test('should show embedded option switches when embedded link', () => {
+ const component = shallow();
+ expect(component.find(TOP_NAV_MENU_SWITCH_SELECTOR).length).toBe(1);
+ expect(component.find(QUERY_INPUT_SWITCH_SELECTOR).length).toBe(1);
+ expect(component.find(DATE_PICKER_SWITCH_SELECTOR).length).toBe(1);
+ expect(component.find(FILTER_BAR_SWITCH_SELECTOR).length).toBe(1);
+ expect(component).toMatchSnapshot();
+});
+
+test('should not show embedded option switches when permalink', () => {
+ const component = shallow();
+ expect(component.find(TOP_NAV_MENU_SWITCH_SELECTOR).length).toBe(0);
+ expect(component.find(QUERY_INPUT_SWITCH_SELECTOR).length).toBe(0);
+ expect(component.find(DATE_PICKER_SWITCH_SELECTOR).length).toBe(0);
+ expect(component.find(FILTER_BAR_SWITCH_SELECTOR).length).toBe(0);
+ expect(component).toMatchSnapshot();
+});
diff --git a/src/plugins/share/public/components/url_panel_content.tsx b/src/plugins/share/public/components/url_panel_content.tsx
index d0d4ce55dc1ac2..96a23fdfd1cc18 100644
--- a/src/plugins/share/public/components/url_panel_content.tsx
+++ b/src/plugins/share/public/components/url_panel_content.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import React, { Component } from 'react';
+import React, { Component, ReactElement } from 'react';
import {
EuiButton,
@@ -60,11 +60,20 @@ enum ExportUrlAsType {
interface State {
exportUrlAs: ExportUrlAsType;
useShortUrl: boolean;
+ showTopNavMenu: boolean;
+ showQueryInput: boolean;
+ showDatePicker: boolean;
+ showFilterBar: boolean;
isCreatingShortUrl: boolean;
url?: string;
shortUrlErrorMsg?: string;
}
+type UrlParams = Pick<
+ State,
+ 'showTopNavMenu' | 'showQueryInput' | 'showDatePicker' | 'showFilterBar'
+>;
+
export class UrlPanelContent extends Component {
private mounted?: boolean;
private shortUrlCache?: string;
@@ -77,6 +86,10 @@ export class UrlPanelContent extends Component {
this.state = {
exportUrlAs: ExportUrlAsType.EXPORT_URL_AS_SNAPSHOT,
useShortUrl: false,
+ showTopNavMenu: false,
+ showQueryInput: false,
+ showDatePicker: false,
+ showFilterBar: true,
isCreatingShortUrl: false,
url: '',
};
@@ -102,6 +115,10 @@ export class UrlPanelContent extends Component {
{this.renderExportAsRadioGroup()}
{this.renderShortUrlSwitch()}
+ {this.renderTopNavMenuSwitch()}
+ {this.renderQueryInputSwitch()}
+ {this.renderDatePickerSwitch()}
+ {this.renderFilterBarSwitch()}
@@ -186,13 +203,25 @@ export class UrlPanelContent extends Component {
return this.props.shareableUrl || window.location.href;
};
- private makeUrlEmbeddable = (url: string) => {
- const embedQueryParam = '?embed=true';
+ private getEmbedQueryParams = (): string => {
+ return [
+ ['&show-top-nav-menu=true', this.state.showTopNavMenu],
+ ['&show-query-input=true', this.state.showQueryInput],
+ ['&show-date-picker=true', this.state.showDatePicker],
+ ['&hide-filter-bar=true', !this.state.showFilterBar], // Inverted to keep default behaviour for old links
+ ].reduce(
+ (accumulator, [queryParam, include]) => (include ? accumulator + queryParam : accumulator),
+ '?embed=true'
+ );
+ };
+
+ private makeUrlEmbeddable = (url: string): string => {
const urlHasQueryString = url.indexOf('?') !== -1;
+ const embedQueryParams = this.getEmbedQueryParams();
if (urlHasQueryString) {
- return url.replace('?', `${embedQueryParam}&`);
+ return url.replace('?', `${embedQueryParams}&`);
}
- return `${url}${embedQueryParam}`;
+ return `${url}${embedQueryParams}`;
};
private makeIframeTag = (url?: string) => {
@@ -279,6 +308,11 @@ export class UrlPanelContent extends Component {
}
};
+ private handleUrlParamChange = (param: keyof UrlParams) => (evt: EuiSwitchEvent): void => {
+ const stateUpdate: Partial = { [param]: evt.target.checked };
+ this.setState(stateUpdate as State, this.setUrl);
+ };
+
private renderExportUrlAsOptions = () => {
return [
{
@@ -391,4 +425,120 @@ export class UrlPanelContent extends Component {
);
};
+
+ private renderTopNavMenuSwitch = (): ReactElement | void => {
+ if (!this.props.isEmbedded) {
+ return;
+ }
+ const switchLabel = (
+
+ );
+ const switchComponent = (
+
+ );
+ const tipContent = (
+
+ );
+
+ return (
+
+ {this.renderWithIconTip(switchComponent, tipContent)}
+
+ );
+ };
+
+ private renderQueryInputSwitch = (): ReactElement | void => {
+ if (!this.props.isEmbedded) {
+ return;
+ }
+ const switchLabel = (
+
+ );
+ const switchComponent = (
+
+ );
+ const tipContent = (
+
+ );
+
+ return (
+
+ {this.renderWithIconTip(switchComponent, tipContent)}
+
+ );
+ };
+
+ private renderDatePickerSwitch = (): ReactElement | void => {
+ if (!this.props.isEmbedded) {
+ return;
+ }
+ const switchLabel = (
+
+ );
+ const switchComponent = (
+
+ );
+ const tipContent = (
+
+ );
+
+ return (
+
+ {this.renderWithIconTip(switchComponent, tipContent)}
+
+ );
+ };
+
+ private renderFilterBarSwitch = (): ReactElement | void => {
+ if (!this.props.isEmbedded) {
+ return;
+ }
+ const switchLabel = (
+
+ );
+ const switchComponent = (
+
+ );
+ const tipContent = (
+
+ );
+
+ return (
+
+ {this.renderWithIconTip(switchComponent, tipContent)}
+
+ );
+ };
}