Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Table visualization to React Part 2: Editor #4175

Merged
merged 16 commits into from
Oct 24, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions client/app/visualizations/table/Editor/ColumnEditor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { map, keys } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import * as Grid from 'antd/lib/grid';
import Input from 'antd/lib/input';
import Radio from 'antd/lib/radio';
import Checkbox from 'antd/lib/checkbox';
import Select from 'antd/lib/select';
import Icon from 'antd/lib/icon';
import Tooltip from 'antd/lib/tooltip';

import ColumnTypes from '../columns';

export default function ColumnEditor({ column, onChange }) {
function handleChange(changes) {
onChange({ ...column, ...changes });
}

const AdditionalOptions = ColumnTypes[column.displayAs].Editor || null;

return (
<div className="table-visualization-editor-column">
<Grid.Row gutter={15} type="flex" align="middle" className="m-b-15">
<Grid.Col span={16}>
<Input value={column.title} onChange={event => handleChange({ title: event.target.value })} />
</Grid.Col>
<Grid.Col span={8}>
<Radio.Group
className="table-visualization-editor-column-align-content"
value={column.alignContent}
onChange={event => handleChange({ alignContent: event.target.value })}
>
<Tooltip title="Align left" mouseEnterDelay={0} mouseLeaveDelay={0}>
<Radio.Button value="left"><Icon type="align-left" /></Radio.Button>
</Tooltip>
<Tooltip title="Align center" mouseEnterDelay={0} mouseLeaveDelay={0}>
<Radio.Button value="center"><Icon type="align-center" /></Radio.Button>
</Tooltip>
<Tooltip title="Align right" mouseEnterDelay={0} mouseLeaveDelay={0}>
<Radio.Button value="right"><Icon type="align-right" /></Radio.Button>
</Tooltip>
</Radio.Group>
</Grid.Col>
</Grid.Row>

<div className="m-b-15">
kravets-levko marked this conversation as resolved.
Show resolved Hide resolved
<label htmlFor={`table-column-editor-${column.name}-allow-search`}>
<Checkbox
id={`table-column-editor-${column.name}-allow-search`}
checked={column.allowSearch}
onChange={event => handleChange({ allowSearch: event.target.checked })}
/>
<span>Use for search</span>
</label>
</div>

<div className="m-b-15">
<label htmlFor={`table-column-editor-${column.name}-display-as`}>Display as:</label>
<Select
id={`table-column-editor-${column.name}-display-as`}
className="w-100"
value={column.displayAs}
onChange={displayAs => handleChange({ displayAs })}
>
{map(ColumnTypes, ({ friendlyName }, key) => (
<Select.Option key={key}>{friendlyName}</Select.Option>
))}
</Select>
</div>

{AdditionalOptions && <AdditionalOptions column={column} onChange={handleChange} />}
</div>
);
}

ColumnEditor.propTypes = {
column: PropTypes.shape({
name: PropTypes.string.isRequired,
title: PropTypes.string,
visible: PropTypes.bool,
alignContent: PropTypes.oneOf(['left', 'center', 'right']),
displayAs: PropTypes.oneOf(keys(ColumnTypes)),
}).isRequired,
onChange: PropTypes.func,
};

ColumnEditor.defaultProps = {
onChange: () => {},
};
74 changes: 74 additions & 0 deletions client/app/visualizations/table/Editor/ColumnsSettings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { map } from 'lodash';
import React from 'react';
import Collapse from 'antd/lib/collapse';
import Icon from 'antd/lib/icon';
import Tooltip from 'antd/lib/tooltip';
import Typography from 'antd/lib/typography';
import { sortableElement } from 'react-sortable-hoc';
import { SortableContainer, DragHandle } from '@/components/sortable';
import { EditorPropTypes } from '@/visualizations';

import ColumnEditor from './ColumnEditor';

const { Text } = Typography;

const SortableItem = sortableElement(Collapse.Panel);

export default function ColumnsSettings({ options, onOptionsChange }) {
function handleColumnChange(newColumn, event) {
if (event) {
event.stopPropagation();
}
const columns = map(options.columns, c => (c.name === newColumn.name ? newColumn : c));
onOptionsChange({ columns });
}

function handleColumnsReorder({ oldIndex, newIndex }) {
const columns = [...options.columns];
columns.splice(newIndex, 0, ...columns.splice(oldIndex, 1));
onOptionsChange({ columns });
}

return (
<SortableContainer
axis="y"
lockAxis="y"
useDragHandle
helperContainer={container => container.firstChild}
onSortEnd={handleColumnsReorder}
containerProps={{
className: 'table-visualization-editor-columns',
}}
>
<Collapse bordered={false} defaultActiveKey={[]} expandIconPosition="right">
{map(options.columns, (column, index) => (
<SortableItem
key={column.name}
index={index}
header={(
<React.Fragment>
<DragHandle />
{column.name}
{(column.title !== '') && (column.title !== column.name) && (
<Text type="secondary" className="m-l-5"><i>({column.title})</i></Text>
)}
</React.Fragment>
)}
extra={(
<Tooltip title="Toggle visibility" mouseEnterDelay={0} mouseLeaveDelay={0}>
<Icon
type={column.visible ? 'eye' : 'eye-invisible'}
onClick={event => handleColumnChange({ ...column, visible: !column.visible }, event)}
/>
</Tooltip>
)}
>
<ColumnEditor column={column} onChange={handleColumnChange} />
</SortableItem>
))}
</Collapse>
</SortableContainer>
kravets-levko marked this conversation as resolved.
Show resolved Hide resolved
);
}

ColumnsSettings.propTypes = EditorPropTypes;
26 changes: 26 additions & 0 deletions client/app/visualizations/table/Editor/GridSettings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { map } from 'lodash';
import React from 'react';
import Select from 'antd/lib/select';
import { EditorPropTypes } from '@/visualizations';

const ALLOWED_ITEM_PER_PAGE = [5, 10, 15, 20, 25, 50, 100, 150, 200, 250];

export default function GridSettings({ options, onOptionsChange }) {
return (
<div className="m-b-15">
<label htmlFor="table-editor-items-per-page">Items per page</label>
<Select
id="table-editor-items-per-page"
className="w-100"
defaultValue={options.itemsPerPage}
onChange={itemsPerPage => onOptionsChange({ itemsPerPage })}
>
{map(ALLOWED_ITEM_PER_PAGE, value => (
<Select.Option key={`ipp${value}`} value={value}>{value}</Select.Option>
))}
</Select>
</div>
);
}

GridSettings.propTypes = EditorPropTypes;
33 changes: 33 additions & 0 deletions client/app/visualizations/table/Editor/editor.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.table-visualization-editor-columns {
.ant-collapse {
background: transparent;
}

.ant-collapse-item {
background: #ffffff;
kravets-levko marked this conversation as resolved.
Show resolved Hide resolved

.drag-handle {
height: 20px;
margin-left: -16px;
padding: 0 16px;
}
}
}

.table-visualization-editor-column {
padding-left: 6px;

.table-visualization-editor-column-align-content {
display: flex;
align-items: stretch;
justify-content: stretch;

.ant-radio-button-wrapper {
flex-grow: 1;
text-align: center;
// fit <Input> height
height: 35px;
line-height: 33px;
}
}
}
30 changes: 30 additions & 0 deletions client/app/visualizations/table/Editor/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { merge } from 'lodash';
import React from 'react';
import Tabs from 'antd/lib/tabs';
import { EditorPropTypes } from '@/visualizations';

import ColumnsSettings from './ColumnsSettings';
import GridSettings from './GridSettings';

import './editor.less';

export default function index(props) {
const { options, onOptionsChange } = props;

const optionsChanged = (newOptions) => {
onOptionsChange(merge({}, options, newOptions));
};

return (
<Tabs className="table-editor-container" animated={false} tabBarGutter={0}>
<Tabs.TabPane key="columns" tab={<span data-test="Counter.EditorTabs.General">Columns</span>}>
<ColumnsSettings {...props} onOptionsChange={optionsChanged} />
</Tabs.TabPane>
<Tabs.TabPane key="grid" tab={<span data-test="Counter.EditorTabs.Formatting">Grid</span>}>
<GridSettings {...props} onOptionsChange={optionsChanged} />
</Tabs.TabPane>
</Tabs>
);
}

index.propTypes = EditorPropTypes;
23 changes: 0 additions & 23 deletions client/app/visualizations/table/columns/boolean.js

This file was deleted.

72 changes: 72 additions & 0 deletions client/app/visualizations/table/columns/boolean.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import PropTypes from 'prop-types';
import Input from 'antd/lib/input';
import { createBooleanFormatter } from '@/lib/value-format';

function Editor({ column, onChange }) {
function handleChange(index, value) {
const booleanValues = [...column.booleanValues];
booleanValues.splice(index, 1, value);
onChange({ booleanValues });
}

return (
<React.Fragment>
<div className="m-b-15">
<div className="m-b-15">
<label htmlFor={`table-column-editor-${column.name}-boolean-false`}>
Value for <code>false</code>
</label>
<Input
id={`table-column-editor-${column.name}-boolean-false`}
defaultValue={column.booleanValues[0]}
onChange={event => handleChange(0, event.target.value)}
/>
</div>
</div>

<div className="m-b-15">
<div className="m-b-15">
<label htmlFor={`table-column-editor-${column.name}-boolean-true`}>
Value for <code>true</code>
</label>
<Input
id={`table-column-editor-${column.name}-boolean-true`}
defaultValue={column.booleanValues[1]}
onChange={event => handleChange(1, event.target.value)}
/>
</div>
</div>
</React.Fragment>
);
}

Editor.propTypes = {
column: PropTypes.shape({
name: PropTypes.string.isRequired,
booleanValues: PropTypes.arrayOf(PropTypes.string),
}).isRequired,
onChange: PropTypes.func.isRequired,
};

export default function initBooleanColumn(column) {
const format = createBooleanFormatter(column.booleanValues);

function prepareData(row) {
return {
text: format(row[column.name]),
};
}

function BooleanColumn({ row }) { // eslint-disable-line react/prop-types
const { text } = prepareData(row);
return text;
}

BooleanColumn.prepareData = prepareData;

return BooleanColumn;
}

initBooleanColumn.friendlyName = 'Boolean';
initBooleanColumn.Editor = Editor;
23 changes: 0 additions & 23 deletions client/app/visualizations/table/columns/datetime.js

This file was deleted.

Loading