Skip to content

Commit

Permalink
Add a feature for custom panel titles (#14831) (#15004)
Browse files Browse the repository at this point in the history
* Add a feature for custom panel titles

* Add tests and put back data-test-subjs

* sync with master and add padding to form

* UI/UX cleanup

- add enter on close functionality
- make reset title a link instead of a button
- Push css to visualizations instead of the panel. This means
background colors will be flush to the panel.  Override for tile maps
which apparently need it (yet region maps don’t for some reason??)

* Fix refactor miss from merge

* whoops, put block display back to make link fall to bottom

* Undo accidental delete visualization name change

* Color top pop over arrow correctly

* Use naming Options and Customize Panel

* update jest snapshot

* Use custom panels for data-title attributes
  • Loading branch information
stacey-gammon committed Nov 16, 2017
1 parent 3d68d3f commit 23c6106
Show file tree
Hide file tree
Showing 22 changed files with 310 additions and 112 deletions.
9 changes: 9 additions & 0 deletions src/core_plugins/kibana/public/dashboard/actions/panels.js
Expand Up @@ -3,6 +3,15 @@ import { createAction } from 'redux-actions';
export const deletePanel = createAction('DELETE_PANEL');

export const updatePanel = createAction('UPDATE_PANEL');
export const resetPanelTitle = createAction('RESET_PANEl_TITLE');
export const setPanelTitle = createAction('SET_PANEl_TITLE',
/**
* @param title {string}
* @param panelIndex {string}
*/
(title, panelIndex) => ({ title, panelIndex })
);


function panelArrayToMap(panels) {
const panelsMap = {};
Expand Down
Expand Up @@ -21,7 +21,7 @@ exports[`DashboardPanel matches snapshot 1`] = `
class="kuiMicroButtonGroup"
>
<div
class="kuiPopover kuiPopover--anchorRight dashboardPanelPopOver"
class="kuiPopover kuiPopover--anchorRight dashboardPanelPopOver kuiPopover--withTitle"
>
<span
aria-label="Panel options"
Expand Down
Expand Up @@ -5,7 +5,10 @@ import { DashboardPanel } from './dashboard_panel';
import { DashboardViewMode } from '../dashboard_view_mode';
import { PanelError } from '../panel/panel_error';
import { store } from '../../store';
import { updateViewMode } from '../actions';
import {
updateViewMode,
setPanels,
} from '../actions';
import { Provider } from 'react-redux';
import { getEmbeddableFactoryMock } from '../__tests__/get_embeddable_factories_mock';

Expand All @@ -26,6 +29,7 @@ function getProps(props = {}) {

beforeAll(() => {
store.dispatch(updateViewMode(DashboardViewMode.EDIT));
store.dispatch(setPanels([{ panelIndex: 'foo1' }]));
});

test('DashboardPanel matches snapshot', () => {
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

@@ -1,7 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';

export function PanelHeader({ title, actions }) {
export function PanelHeader({ title, actions, isViewOnlyMode }) {
if (isViewOnlyMode && !title) {
return (
<div className="panel-heading-floater">
<div className="kuiMicroButtonGroup">
{actions}
</div>
</div>
);
}

return (
<div className="panel-heading">
<span
Expand All @@ -21,6 +31,7 @@ export function PanelHeader({ title, actions }) {
}

PanelHeader.propTypes = {
isViewOnlyMode: PropTypes.bool,
title: PropTypes.string,
actions: PropTypes.node,
};
Expand Up @@ -15,16 +15,18 @@ import {

import {
getEmbeddable,
getEmbeddableTitle,
getPanel,
getMaximizedPanelId,
getFullScreenMode,
getViewMode
} from '../../selectors';

const mapStateToProps = ({ dashboard }, { panelId }) => {
const embeddable = getEmbeddable(dashboard, panelId);
const panel = getPanel(dashboard, panelId);
const embeddableTitle = embeddable ? embeddable.title : '';
return {
title: embeddable ? getEmbeddableTitle(dashboard, panelId) : '',
title: panel.title === undefined ? embeddableTitle : panel.title,
isExpanded: getMaximizedPanelId(dashboard) === panelId,
isViewOnlyMode: getFullScreenMode(dashboard) || getViewMode(dashboard) === DashboardViewMode.VIEW,
};
Expand Down Expand Up @@ -57,6 +59,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
return {
title,
actions,
isViewOnlyMode,
};
};

Expand Down
@@ -0,0 +1,59 @@
import React from 'react';
import { Provider } from 'react-redux';
import _ from 'lodash';
import { mount } from 'enzyme';

import { PanelHeaderContainer } from './panel_header_container';
import { DashboardViewMode } from '../../dashboard_view_mode';
import { store } from '../../../store';
import {
updateViewMode,
setPanels,
setPanelTitle,
resetPanelTitle,
embeddableRenderFinished,
} from '../../actions';
import { getEmbeddableFactoryMock } from '../../__tests__/get_embeddable_factories_mock';
import {
TestSubjects,
} from 'ui_framework/src/test';

function getProps(props = {}) {
const defaultTestProps = {
panelId: 'foo1',
embeddableFactory: getEmbeddableFactoryMock(),
};
return _.defaultsDeep(props, defaultTestProps);
}

let component;

beforeAll(() => {
store.dispatch(updateViewMode(DashboardViewMode.EDIT));
store.dispatch(setPanels([{ panelIndex: 'foo1' }]));
store.dispatch(embeddableRenderFinished('foo1', { title: 'my embeddable title', editUrl: 'editme' }));
});

afterAll(() => {
component.unmount();
});

test('Panel header shows embeddable title when nothing is set on the panel', () => {
component = mount(<Provider store={store}><PanelHeaderContainer {...getProps()} /></Provider>);
expect(TestSubjects.getText(component, 'dashboardPanelTitle')).toBe('my embeddable title');
});

test('Panel header shows panel title when it is set on the panel', () => {
store.dispatch(setPanelTitle('my custom panel title', 'foo1'));
expect(TestSubjects.getText(component, 'dashboardPanelTitle')).toBe('my custom panel title');
});

test('Panel header shows no panel title when it is set to an empty string on the panel', () => {
store.dispatch(setPanelTitle('', 'foo1'));
expect(TestSubjects.getText(component, 'dashboardPanelTitle')).toBe('');
});

test('Panel header shows embeddable title when the panel title is reset', () => {
store.dispatch(resetPanelTitle('foo1'));
expect(TestSubjects.getText(component, 'dashboardPanelTitle')).toBe('my embeddable title');
});
Expand Up @@ -2,13 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import {
KuiPopover,
KuiContextMenuPanel,
KuiContextMenu,
KuiKeyboardAccessible,
} from 'ui_framework/components';

import { EditMenuItem } from './edit_menu_item';
import { DeleteMenuItem } from './delete_menu_item';
import { ExpandOrCollapseMenuItem } from './expand_or_collapse_menu_item';
import { PanelOptionsMenuForm } from './panel_options_menu_form';

export class PanelOptionsMenu extends React.Component {
state = {
Expand All @@ -35,23 +33,74 @@ export class PanelOptionsMenu extends React.Component {
this.props.toggleExpandedPanel();
};

renderItems() {
const items = [
<EditMenuItem
key="0"
onEditPanel={this.onEditPanel}
/>,
<ExpandOrCollapseMenuItem
key="2"
onToggleExpand={this.onToggleExpandPanel}
isExpanded={this.props.isExpanded}
/>
buildMainMenuPanel() {
const { isExpanded } = this.props;
const mainPanelMenuItems = [
{
name: 'Edit visualization',
'data-test-subj': 'dashboardPanelEditLink',
icon: <span
aria-hidden="true"
className="kuiButton__icon kuiIcon fa-edit"
/>,
onClick: this.onEditPanel,
},
{
name: 'Customize panel',
'data-test-subj': 'dashboardPanelOptionsSubMenuLink',
icon: <span
aria-hidden="true"
className="kuiButton__icon kuiIcon fa-edit"
/>,
panel: 'panelSubOptionsMenu',
},
{
name: isExpanded ? 'Minimize' : 'Full screen',
'data-test-subj': 'dashboardPanelExpandIcon',
icon: <span
aria-hidden="true"
className={`kuiButton__icon kuiIcon ${isExpanded ? 'fa-compress' : 'fa-expand'}`}
/>,
onClick: this.onToggleExpandPanel,
}
];
if (!this.props.isExpanded) {
items.push(<DeleteMenuItem key="3" onDeletePanel={this.onDeletePanel} />);
mainPanelMenuItems.push({
name: 'Delete from dashboard',
'data-test-subj': 'dashboardPanelRemoveIcon',
icon: <span
aria-hidden="true"
className="kuiButton__icon kuiIcon fa-trash"
/>,
onClick: this.onDeletePanel,
});
}

return items;
return {
title: 'Options',
id: 'mainMenu',
items: mainPanelMenuItems,
};
}

buildPanelOptionsSubMenu() {
return {
title: 'Customize panel',
id: 'panelSubOptionsMenu',
content: <PanelOptionsMenuForm
onReset={this.props.onResetPanelTitle}
onUpdatePanelTitle={this.props.onUpdatePanelTitle}
title={this.props.panelTitle}
onClose={this.closePopover}
/>,
};
}

renderPanels() {
return [
this.buildMainMenuPanel(),
this.buildPanelOptionsSubMenu(),
];
}

render() {
Expand All @@ -74,17 +123,21 @@ export class PanelOptionsMenu extends React.Component {
closePopover={this.closePopover}
panelPaddingSize="none"
anchorPosition="right"
withTitle
>
<KuiContextMenuPanel
onClose={this.closePopover}
items={this.renderItems()}
<KuiContextMenu
initialPanelId="mainMenu"
panels={this.renderPanels()}
/>
</KuiPopover>
);
}
}

PanelOptionsMenu.propTypes = {
panelTitle: PropTypes.string,
onUpdatePanelTitle: PropTypes.func.isRequired,
onResetPanelTitle: PropTypes.func.isRequired,
editUrl: PropTypes.string.isRequired,
toggleExpandedPanel: PropTypes.func.isRequired,
isExpanded: PropTypes.bool.isRequired,
Expand Down

0 comments on commit 23c6106

Please sign in to comment.