Skip to content

Commit

Permalink
[Canvas] Adds doc links and keyboard shortcut cheatsheat to help menu (
Browse files Browse the repository at this point in the history
…#31335) (#31727)

* Added docs link and keyboard shortcuts to global help menu

* Fixed tooltip

* Removed aeroelastic keyboard event handlers

* typography and copy changes

* Removed doc links from workpad manager

* Added input target check to workpad page keyhandler

* Fixed ungrouping

* Displays arrow symbols instead of arrow key word

* Removed tabIndex

* Fix: reintroduce additional call on keyboard event
  • Loading branch information
cqliu1 authored Feb 21, 2019
1 parent efd98d7 commit 4ad671d
Show file tree
Hide file tree
Showing 14 changed files with 342 additions and 131 deletions.
9 changes: 9 additions & 0 deletions x-pack/plugins/canvas/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import 'ui/autoload/all';
import chrome from 'ui/chrome';
import './angular/config';
import './angular/services';
import React from 'react';
import ReactDOM from 'react-dom';
import { CanvasRootController } from './angular/controllers';

// Import the uiExports that the application uses
Expand All @@ -24,5 +26,12 @@ import './lib/load_expression_types';
import './lib/load_transitions';
import 'uiExports/canvas';

import { HelpMenu } from './components/help_menu/help_menu';

// load the application
chrome.setRootController('canvas', CanvasRootController);

// add Canvas docs to help menu in global nav
chrome.helpExtension.set(domNode => {
ReactDOM.render(<HelpMenu />, domNode);
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ export class FullscreenControl extends React.PureComponent {

return (
<span>
<Shortcuts name="EDITOR" handler={keyHandler} targetNodeSelector="body" global isolate />
<Shortcuts
name="PRESENTATION"
handler={keyHandler}
targetNodeSelector="body"
global
isolate
/>
{children({ isFullscreen, toggleFullscreen: this.toggleFullscreen })}
</span>
);
Expand Down
48 changes: 48 additions & 0 deletions x-pack/plugins/canvas/public/components/help_menu/help_menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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, { Fragment, PureComponent } from 'react';
import { EuiButton, EuiHorizontalRule, EuiText, EuiSpacer, EuiPortal } from '@elastic/eui';
import { documentationLinks } from '../../lib/documentation_links';
import { KeyboardShortcutsDoc } from '../keyboard_shortcuts_doc';

export class HelpMenu extends PureComponent {
state = { isFlyoutVisible: false };

showFlyout = () => {
this.setState({ isFlyoutVisible: true });
};

hideFlyout = () => {
this.setState({ isFlyoutVisible: false });
};

render() {
return (
<Fragment>
<EuiHorizontalRule margin="none" />
<EuiSpacer />
<EuiText size="s">
<p>For Canvas specific information</p>
</EuiText>
<EuiSpacer />
<EuiButton fill iconType="popout" href={documentationLinks.canvas} target="_blank">
Canvas documentation
</EuiButton>
<EuiSpacer />
<EuiButton onClick={this.showFlyout} target="_blank">
Keyboard shortcuts
</EuiButton>

{this.state.isFlyoutVisible && (
<EuiPortal>
<KeyboardShortcutsDoc onClose={this.hideFlyout} />
</EuiPortal>
)}
</Fragment>
);
}
}
7 changes: 7 additions & 0 deletions x-pack/plugins/canvas/public/components/help_menu/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 { HelpMenu } from './help_menu';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 { KeyboardShortcutsDoc } from './keyboard_shortcuts_doc';
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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 {
EuiFlyout,
EuiFlyoutHeader,
EuiFlyoutBody,
EuiDescriptionList,
EuiHorizontalRule,
EuiCode,
EuiSpacer,
EuiTitle,
} from '@elastic/eui';
import { keymap } from '../../lib/keymap';
import { getClientPlatform } from '../../lib/get_client_platform';
import { getId } from '../../lib/get_id';

const getPrettyShortcut = shortcut => {
if (!shortcut) {
return '';
}

let result = shortcut.replace(/command/i, '⌘');
result = result.replace(/option/i, '⌥');
result = result.replace(/left/i, '←');
result = result.replace(/right/i, '→');
result = result.replace(/up/i, '↑');
result = result.replace(/down/i, '↓');

return (
<span key={getId('span')}>
{result
.split(/(\+)/g) //splits the array by '+' and keeps the '+'s as elements in the array
.map(key => (key === '+' ? ` ${key} ` : <EuiCode key={getId('shortcut')}>{key}</EuiCode>))}
</span>
);
};

const getDescriptionListItems = shortcuts =>
Object.values(shortcuts).map(shortcutKeyMap => {
const os = getClientPlatform();
const osShortcuts = shortcutKeyMap[os];
return {
title: shortcutKeyMap.help,
description: osShortcuts.reduce((acc, shortcut, i) => {
if (i !== 0) {
acc.push(' or ');
}
acc.push(getPrettyShortcut(shortcut));
return acc;
}, []),
};
});

export const KeyboardShortcutsDoc = props => (
<EuiFlyout closeButtonAriaLabel="Closes keyboard shortcuts reference" size="s" {...props}>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="s">
<h2>Keyboard Shortcuts</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
{Object.values(keymap).map(namespace => {
const { displayName, ...shortcuts } = namespace;
return (
<div key={getId('shortcuts')} className="canvasKeyboardShortcut">
<EuiTitle size="xs">
<h4>{displayName}</h4>
</EuiTitle>
<EuiHorizontalRule margin="s" />
<EuiDescriptionList
textStyle="reverse"
type="column"
listItems={getDescriptionListItems(shortcuts)}
compressed
/>
<EuiSpacer />
</div>
);
})}
</EuiFlyoutBody>
</EuiFlyout>
);

KeyboardShortcutsDoc.propTypes = {
onClose: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const WorkpadHeader = ({
<EuiFlexItem grow={false}>
<FullscreenControl>
{({ toggleFullscreen }) => (
<EuiToolTip position="bottom" content="Toggle fullscreen mode">
<EuiToolTip position="bottom" content="Enter fullscreen mode">
<EuiButtonIcon
iconType="fullScreen"
aria-label="View fullscreen"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiBetaBadge,
EuiLink,
} from '@elastic/eui';
import { WorkpadLoader } from '../workpad_loader';
import { WorkpadTemplates } from '../workpad_templates';
import { documentationLinks } from '../../lib/documentation_links';

export const WorkpadManager = ({ onClose }) => {
const tabs = [
Expand Down Expand Up @@ -57,11 +55,6 @@ export const WorkpadManager = ({ onClose }) => {
tooltipContent="Canvas is still in beta. Please help us improve by reporting issues or bugs in the Kibana repo."
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink href={documentationLinks.canvas} target="_blank">
Docs
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiModalHeader>
<EuiModalBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,70 +124,10 @@ const handleMouseDown = (commit, e, isEditable) => {
);
};

const keyCode = key => (key === 'Meta' ? 'MetaLeft' : 'Key' + key.toUpperCase());

const isTextInput = ({ tagName, type }) => {
// input types that aren't variations of text input
const nonTextInputs = [
'button',
'checkbox',
'color',
'file',
'image',
'radio',
'range',
'reset',
'submit',
];

switch (tagName.toLowerCase()) {
case 'input':
return !nonTextInputs.includes(type);
case 'textarea':
return true;
default:
return false;
}
};

const modifierKey = key => ['KeyALT', 'KeyCONTROL'].indexOf(keyCode(key)) > -1;

const handleKeyDown = (commit, e, isEditable) => {
const { key } = e;

if (isEditable && !modifierKey(key)) {
commit('keyboardEvent', {
event: 'keyDown',
code: keyCode(key), // convert to standard event code
});
}
};

const handleKeyPress = (commit, e, isEditable) => {
const { key, target } = e;
const upcaseKey = key && key.toUpperCase();
if (isEditable && !isTextInput(target) && 'GU'.indexOf(upcaseKey) !== -1) {
commit('actionEvent', {
event: upcaseKey === 'G' ? 'group' : 'ungroup',
});
}
};

const handleKeyUp = (commit, { key }, isEditable) => {
if (isEditable && !modifierKey(key)) {
commit('keyboardEvent', {
event: 'keyUp',
code: keyCode(key), // convert to standard event code
});
}
};

export const eventHandlers = {
onMouseDown: props => e => handleMouseDown(props.commit, e, props.isEditable),
onMouseMove: props => e => handleMouseMove(props.commit, e, props.isEditable),
onKeyDown: props => e => handleKeyDown(props.commit, e, props.isEditable),
onKeyPress: props => e => handleKeyPress(props.commit, e, props.isEditable),
onKeyUp: props => e => handleKeyUp(props.commit, e, props.isEditable),
onKeyDown: props => () => props.commit('keyboardEvent'), // dummy event
onWheel: props => e => handleWheel(props.commit, e, props.isEditable),
resetHandler: () => () => resetHandler(),
};
17 changes: 14 additions & 3 deletions x-pack/plugins/canvas/public/components/workpad_page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ export const WorkpadPage = compose(
];
};

const selectedPrimaryShapeObjects = selectedPrimaryShapes.map(id =>
shapes.find(s => s.id === id)
);
const selectedPrimaryShapeObjects = selectedPrimaryShapes
.map(id => shapes.find(s => s.id === id))
.filter(shape => shape);

const selectedPersistentPrimaryShapes = flatten(
selectedPrimaryShapeObjects.map(shape =>
shape.subtype === 'adHocGroup'
Expand Down Expand Up @@ -217,6 +218,16 @@ export const WorkpadPage = compose(
};
}
), // Updates states; needs to have both local and global
withHandlers({
groupElements: ({ commit }) => () =>
commit('actionEvent', {
event: 'group',
}),
ungroupElements: ({ commit }) => () =>
commit('actionEvent', {
event: 'ungroup',
}),
}),
withHandlers(eventHandlers) // Captures user intent, needs to have reconciled state
)(Component);

Expand Down
Loading

0 comments on commit 4ad671d

Please sign in to comment.