Skip to content

Commit

Permalink
[Vis: Default editor] EUIficate Vega options tab (elastic#47473)
Browse files Browse the repository at this point in the history
* EUIficate Vega options tab

* Update  list of dependencies

* Fix comments

* Adjust styles
  • Loading branch information
sulemanof committed Oct 18, 2019
1 parent de88144 commit 96c036d
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 443 deletions.
33 changes: 7 additions & 26 deletions src/legacy/core_plugins/vis_type_vega/public/_vega_editor.scss
Original file line number Diff line number Diff line change
@@ -1,41 +1,22 @@
.visEditor--vega {
.visEditorSidebar__config {
padding: 0;
// Makes sure the vega options dropdown menu is visible
overflow: inherit;
position: relative;
}

// The following is necessary for the Vega editor to expand to full height of the editor panel
.visEditorSidebar__config,
.visEditorSidebar__options {
@include flex-parent(1, 1, auto);

> * {
@include flex-parent(1, 1, auto);
}
}

@include euiBreakpoint('xs', 's', 'm') {
.visEditor__collapsibleSidebar {
flex-grow: 1;
}
@include euiScrollBar;
flex-shrink: 1;
overflow-y: auto;
}
}


.vgaEditor {
@include flex-parent(1, 1, auto);
position: relative;

@include euiBreakpoint('xs', 's', 'm') {
min-height: $euiSize * 15;
@include euiScrollBar;
max-height: $euiSize * 15;
overflow-y: auto;
}

position: relative;
}

.vgaEditor__aceEditor {
flex: 1 1 auto;
}

.vgaEditor__aceEditorActions {
Expand Down
20 changes: 20 additions & 0 deletions src/legacy/core_plugins/vis_type_vega/public/components/index.ts
Original file line number Diff line number Diff line change
@@ -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 { VegaVisEditor } from './vega_vis_editor';
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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, { useState, useCallback } from 'react';
import { EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem, EuiPopover } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';

interface VegaActionsMenuProps {
formatHJson(): void;
formatJson(): void;
}

function VegaActionsMenu({ formatHJson, formatJson }: VegaActionsMenuProps) {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);

const onButtonClick = useCallback(() => setIsPopoverOpen(isOpen => !isOpen), []);
const onHJsonCLick = useCallback(() => {
formatHJson();
setIsPopoverOpen(false);
}, [isPopoverOpen, formatHJson]);

const onJsonCLick = useCallback(() => {
formatJson();
setIsPopoverOpen(false);
}, [isPopoverOpen, formatJson]);

const closePopover = useCallback(() => setIsPopoverOpen(false), []);

const button = (
<EuiButtonIcon
iconType="wrench"
onClick={onButtonClick}
aria-label={i18n.translate('visTypeVega.editor.vegaEditorOptionsButtonAriaLabel', {
defaultMessage: 'Vega editor options',
})}
/>
);

const items = [
<EuiContextMenuItem key="hjson" onClick={onHJsonCLick}>
<FormattedMessage
id="visTypeVega.editor.reformatAsHJSONButtonLabel"
defaultMessage="Reformat as HJSON"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem key="json" onClick={onJsonCLick}>
<FormattedMessage
id="visTypeVega.editor.reformatAsJSONButtonLabel"
defaultMessage="Reformat as JSON, delete comments"
/>
</EuiContextMenuItem>,
];

return (
<EuiPopover
id="helpMenu"
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel items={items} />
</EuiPopover>
);
}

export { VegaActionsMenu };
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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, { useCallback, useState } from 'react';
import { EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem, EuiPopover } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';

function VegaHelpMenu() {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const onButtonClick = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]);

const closePopover = useCallback(() => setIsPopoverOpen(false), []);

const button = (
<EuiButtonIcon
iconType="questionInCircle"
onClick={onButtonClick}
aria-label={i18n.translate('visTypeVega.editor.vegaHelpButtonAriaLabel', {
defaultMessage: 'Vega help',
})}
/>
);

const items = [
<EuiContextMenuItem
key="vegaHelp"
href="https://www.elastic.co/guide/en/kibana/master/vega-graph.html"
target="_blank"
onClick={closePopover}
>
<FormattedMessage
id="visTypeVega.editor.vegaHelpLinkText"
defaultMessage="Kibana Vega help"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
key="vegaLiteDocs"
href="https://vega.github.io/vega-lite/docs/"
target="_blank"
onClick={closePopover}
>
<FormattedMessage
id="visTypeVega.editor.vegaLiteDocumentationLinkText"
defaultMessage="Vega-Lite documentation"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
key="vegaDoc"
href="https://vega.github.io/vega/docs/"
target="_blank"
onClick={closePopover}
>
<FormattedMessage
id="visTypeVega.editor.vegaDocumentationLinkText"
defaultMessage="Vega documentation"
/>
</EuiContextMenuItem>,
];

return (
<EuiPopover
id="helpMenu"
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel items={items} />
</EuiPopover>
);
}

export { VegaHelpMenu };
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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, { useCallback } from 'react';
import { EuiCodeEditor } from '@elastic/eui';
import compactStringify from 'json-stringify-pretty-compact';
// @ts-ignore
import hjson from 'hjson';
import { i18n } from '@kbn/i18n';

import { toastNotifications } from 'ui/notify';
import { VisOptionsProps } from 'ui/vis/editors/default';
import { VisParams } from '../vega_fn';
import { VegaHelpMenu } from './vega_help_menu';
import { VegaActionsMenu } from './vega_actions_menu';

const aceOptions = {
maxLines: Infinity,
highlightActiveLine: false,
showPrintMargin: false,
tabSize: 2,
useSoftTabs: true,
wrap: true,
};

const hjsonStringifyOptions = {
bracesSameLine: true,
keepWsc: true,
};

function format(value: string, stringify: typeof compactStringify, options?: any) {
try {
const spec = hjson.parse(value, { legacyRoot: false, keepWsc: true });
return stringify(spec, options);
} catch (err) {
// This is a common case - user tries to format an invalid HJSON text
toastNotifications.addError(err, {
title: i18n.translate('visTypeVega.editor.formatError', {
defaultMessage: 'Error formatting spec',
}),
});

return value;
}
}

function VegaVisEditor({ stateParams, setValue }: VisOptionsProps<VisParams>) {
const onChange = useCallback(
(value: string) => {
setValue('spec', value);
},
[setValue]
);

const formatJson = useCallback(
() => setValue('spec', format(stateParams.spec, compactStringify)),
[setValue, stateParams.spec]
);

const formatHJson = useCallback(
() => setValue('spec', format(stateParams.spec, hjson.stringify, hjsonStringifyOptions)),
[setValue, stateParams.spec]
);

return (
<div className="vgaEditor">
<EuiCodeEditor
data-test-subj="vega-editor"
mode="hjson"
theme="textmate"
width="100%"
height="auto"
onChange={onChange}
value={stateParams.spec}
setOptions={aceOptions}
/>
<div className="vgaEditor__aceEditorActions">
<VegaHelpMenu />
<VegaActionsMenu formatHJson={formatHJson} formatJson={formatJson} />
</div>
</div>
);
}

export { VegaVisEditor };
Loading

0 comments on commit 96c036d

Please sign in to comment.