Skip to content

Commit

Permalink
Merge pull request #2 from rs21io/SNLBATTERY-11-add-checkboxes-to-the…
Browse files Browse the repository at this point in the history
…-table

Snlbattery 11 add checkboxes to the table
  • Loading branch information
jisbell347 authored Jul 22, 2021
2 parents 463c290 + a978aa8 commit ddfed83
Show file tree
Hide file tree
Showing 38 changed files with 2,321 additions and 52 deletions.
2 changes: 1 addition & 1 deletion client/app/components/QueryLink.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function QueryLink({ query, visualization, readOnly }) {
const getUrl = () => {
let hash = null;
if (visualization) {
if (visualization.type === "TABLE") {
if (visualization.type === "TABLE" || visualization.type === 'SELECTION_TABLE') {
// link to hard-coded table tab instead of the (hidden) visualization tab
hash = "table";
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default function VisualizationRenderer(props) {
let options = { ...visualization.options };

// define pagination size based on context for Table visualization
if (visualization.type === "TABLE") {
if (visualization.type === "TABLE" || visualization.type === "SELECTION_TABLE") {
options.paginationSize = props.context === "widget" ? "small" : "default";
}

Expand Down
2 changes: 2 additions & 0 deletions viz-lib/src/visualizations/registeredVisualizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import funnelVisualization from "./funnel";
import mapVisualization from "./map";
import pivotVisualization from "./pivot";
import sankeyVisualization from "./sankey";
import selectionTableVisualization from "./selection-table";
import sunburstVisualization from "./sunburst";
import tableVisualization from "./table";
import wordCloudVisualization from "./word-cloud";
Expand Down Expand Up @@ -88,6 +89,7 @@ each(
mapVisualization,
pivotVisualization,
sankeyVisualization,
selectionTableVisualization,
sunburstVisualization,
tableVisualization,
wordCloudVisualization,
Expand Down
100 changes: 100 additions & 0 deletions viz-lib/src/visualizations/selection-table/Editor/ColumnEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { map, keys } from "lodash";
import React from "react";
import { useDebouncedCallback } from "use-debounce";
import * as Grid from "antd/lib/grid";
import { Section, Select, Input, Checkbox, TextAlignmentSelect } from "@/components/visualizations/editor";

import ColumnTypes from "../columns";

type OwnProps = {
column: {
name: string;
title?: string;
visible?: boolean;
alignContent?: "left" | "center" | "right";
displayAs?: any; // TODO: PropTypes.oneOf(keys(ColumnTypes))
};
onChange?: (...args: any[]) => any;
};

type Props = OwnProps & typeof ColumnEditor.defaultProps;

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

const [handleChangeDebounced] = useDebouncedCallback(handleChange, 200);

// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
const AdditionalOptions = ColumnTypes[column.displayAs].Editor || null;

return (
<div className="table-visualization-editor-column">
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: Element[]; gutter: number; type:... Remove this comment to see the full error message */}
<Grid.Row gutter={15} type="flex" align="middle">
<Grid.Col span={16}>
<Input
data-test={`Table.Column.${column.name}.Title`}
defaultValue={column.title}
onChange={(event: any) => handleChangeDebounced({ title: event.target.value })}
/>
</Grid.Col>
<Grid.Col span={8}>
<TextAlignmentSelect
data-test={`Table.Column.${column.name}.TextAlignment`}
defaultValue={column.alignContent}
onChange={(event: any) => handleChange({ alignContent: event.target.value })}
/>
</Grid.Col>
</Grid.Row>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Checkbox
data-test={`Table.Column.${column.name}.UseForSearch`}
// @ts-expect-error ts-migrate(2339) FIXME: Property 'allowSearch' does not exist on type '{ n... Remove this comment to see the full error message
defaultChecked={column.allowSearch}
onChange={event => handleChange({ allowSearch: event.target.checked })}>
Use for search
</Checkbox>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Input
label="Description"
// @ts-expect-error ts-migrate(2339) FIXME: Property 'description' does not exist on type '{ n... Remove this comment to see the full error message
defaultValue={column.description}
onChange={(event: any) => handleChangeDebounced({ description: event.target.value })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Select
label="Display as:"
data-test={`Table.Column.${column.name}.DisplayAs`}
defaultValue={column.displayAs}
onChange={(displayAs: any) => handleChange({ displayAs })}>
{map(ColumnTypes, ({ friendlyName }, key) => (
// @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message
<Select.Option key={key} data-test={`Table.Column.${column.name}.DisplayAs.${key}`}>
{friendlyName}
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message */}
</Select.Option>
))}
</Select>
</Section>

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

ColumnEditor.defaultProps = {
onChange: () => {},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from "react";
import enzyme from "enzyme";

import getOptions from "../getOptions";
import ColumnsSettings from "./ColumnsSettings";

function findByTestID(wrapper: any, testId: any) {
return wrapper.find(`[data-test="${testId}"]`);
}

function mount(options: any, done: any) {
const data = {
columns: [{ name: "a", type: "string" }],
rows: [{ a: "test" }],
};
options = getOptions(options, data);
return enzyme.mount(
<ColumnsSettings
visualizationName="Test"
data={data}
options={options}
onOptionsChange={changedOptions => {
expect(changedOptions).toMatchSnapshot();
done();
}}
/>
);
}

describe("Visualizations -> Table -> Editor -> Columns Settings", () => {
test("Toggles column visibility", done => {
const el = mount({}, done);

findByTestID(el, "Table.Column.a.Visibility")
.last()
.simulate("click");
});

test("Changes column title", done => {
const el = mount({}, done);
findByTestID(el, "Table.Column.a.Name")
.last()
.simulate("click"); // expand settings

findByTestID(el, "Table.Column.a.Title")
.last()
.simulate("change", { target: { value: "test" } });
});

test("Changes column alignment", done => {
const el = mount({}, done);
findByTestID(el, "Table.Column.a.Name")
.last()
.simulate("click"); // expand settings

findByTestID(el, "Table.Column.a.TextAlignment")
.last()
.find('[data-test="TextAlignmentSelect.Right"] input')
.simulate("change", { target: { checked: true } });
});

test("Enables search by column data", done => {
const el = mount({}, done);
findByTestID(el, "Table.Column.a.Name")
.last()
.simulate("click"); // expand settings

findByTestID(el, "Table.Column.a.UseForSearch")
.last()
.find("input")
.simulate("change", { target: { checked: true } });
});

test("Changes column display type", done => {
const el = mount({}, done);
findByTestID(el, "Table.Column.a.Name")
.last()
.simulate("click"); // expand settings

findByTestID(el, "Table.Column.a.DisplayAs")
.last()
.simulate("mouseDown");
findByTestID(el, "Table.Column.a.DisplayAs.number")
.last()
.simulate("click");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { map } from "lodash";
import React from "react";
import Collapse from "antd/lib/collapse";
import Tooltip from "antd/lib/tooltip";
import Typography from "antd/lib/typography";
// @ts-expect-error ts-migrate(2724) FIXME: Module '"../../../../node_modules/react-sortable-h... Remove this comment to see the full error message
import { sortableElement } from "react-sortable-hoc";
import { SortableContainer, DragHandle } from "@/components/sortable";
import { EditorPropTypes } from "@/visualizations/prop-types";

import EyeOutlinedIcon from "@ant-design/icons/EyeOutlined";
import EyeInvisibleOutlinedIcon from "@ant-design/icons/EyeInvisibleOutlined";

import ColumnEditor from "./ColumnEditor";

const { Text } = Typography;

const SortableItem = sortableElement(Collapse.Panel);

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

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

return (
<SortableContainer
axis="y"
lockAxis="y"
useDragHandle
helperClass="table-editor-columns-dragged-item"
helperContainer={(container: any) => container.firstChild}
onSortEnd={handleColumnsReorder}
containerProps={{
className: "table-visualization-editor-columns",
}}>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'null | u... Remove this comment to see the full error message */}
<Collapse bordered={false} defaultActiveKey={[]} expandIconPosition="right">
{map(options.columns, (column, index) => (
<SortableItem
key={column.name}
index={index}
header={
<React.Fragment>
<DragHandle />
<span data-test={`Table.Column.${column.name}.Name`}>
{column.name}
{column.title !== "" && column.title !== column.name && (
<Text type="secondary" style={{ marginLeft: 5 }}>
<i>({column.title})</i>
</Text>
)}
</span>
</React.Fragment>
}
extra={
<Tooltip title="Toggle visibility" mouseEnterDelay={0} mouseLeaveDelay={0}>
{column.visible ? (
<EyeOutlinedIcon
data-test={`Table.Column.${column.name}.Visibility`}
onClick={event => handleColumnChange({ ...column, visible: !column.visible }, event)}
/>
) : (
<EyeInvisibleOutlinedIcon
data-test={`Table.Column.${column.name}.Visibility`}
onClick={event => handleColumnChange({ ...column, visible: !column.visible }, event)}
/>
)}
</Tooltip>
}>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '(newColumn: any, event: any) => void' is not... Remove this comment to see the full error message */}
<ColumnEditor column={column} onChange={handleColumnChange} />
</SortableItem>
))}
</Collapse>
</SortableContainer>
);
}

ColumnsSettings.propTypes = EditorPropTypes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react";
import enzyme from "enzyme";

import getOptions from "../getOptions";
import GridSettings from "./GridSettings";

function findByTestID(wrapper: any, testId: any) {
return wrapper.find(`[data-test="${testId}"]`);
}

function mount(options: any, done: any) {
const data = { columns: [], rows: [] };
options = getOptions(options, data);
return enzyme.mount(
<GridSettings
visualizationName="Test"
data={data}
options={options}
onOptionsChange={changedOptions => {
expect(changedOptions).toMatchSnapshot();
done();
}}
/>
);
}

describe("Visualizations -> Table -> Editor -> Grid Settings", () => {
test("Changes items per page", done => {
const el = mount(
{
itemsPerPage: 25,
},
done
);

findByTestID(el, "Table.ItemsPerPage")
.last()
.simulate("mouseDown");
findByTestID(el, "Table.ItemsPerPage.100")
.last()
.simulate("click");
});
});
29 changes: 29 additions & 0 deletions viz-lib/src/visualizations/selection-table/Editor/GridSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { map } from "lodash";
import React from "react";
import { Section, Select } from "@/components/visualizations/editor";
import { EditorPropTypes } from "@/visualizations/prop-types";

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

export default function GridSettings({ options, onOptionsChange }: any) {
return (
// @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message
<Section>
<Select
label="Items per page"
data-test="Table.ItemsPerPage"
defaultValue={options.itemsPerPage}
onChange={(itemsPerPage: any) => onOptionsChange({ itemsPerPage })}>
{map(ALLOWED_ITEM_PER_PAGE, value => (
// @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message
<Select.Option key={`ipp${value}`} value={value} data-test={`Table.ItemsPerPage.${value}`}>
{value}
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message */}
</Select.Option>
))}
</Select>
</Section>
);
}

GridSettings.propTypes = EditorPropTypes;
Loading

0 comments on commit ddfed83

Please sign in to comment.