浏览代码

adding stub table input CSV

ryan 6 年之前
父节点
当前提交
9df47391ea

+ 2 - 0
packages/grafana-ui/package.json

@@ -24,6 +24,7 @@
     "jquery": "^3.2.1",
     "lodash": "^4.17.10",
     "moment": "^2.22.2",
+    "papaparse": "^4.6.3",
     "react": "^16.6.3",
     "react-color": "^2.17.0",
     "react-custom-scrollbars": "^4.2.1",
@@ -46,6 +47,7 @@
     "@types/jquery": "^1.10.35",
     "@types/lodash": "^4.14.119",
     "@types/node": "^10.12.18",
+    "@types/papaparse": "^4.5.9",
     "@types/react": "^16.7.6",
     "@types/react-custom-scrollbars": "^4.0.5",
     "@types/react-test-renderer": "^16.0.3",

+ 12 - 0
packages/grafana-ui/src/components/Table/TableInputCSV.story.tsx

@@ -0,0 +1,12 @@
+import { storiesOf } from '@storybook/react';
+import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
+import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
+import TableInputCSV from './TableInputCSV';
+
+const TableInputStories = storiesOf('UI/Table/Input', module);
+
+TableInputStories.addDecorator(withCenteredStory);
+
+TableInputStories.add('default', () => {
+  return renderComponentWithTheme(TableInputCSV, {});
+});

+ 12 - 0
packages/grafana-ui/src/components/Table/TableInputCSV.test.tsx

@@ -0,0 +1,12 @@
+import React from 'react';
+
+import renderer from 'react-test-renderer';
+import TableInputCSV from './TableInputCSV';
+
+describe('TableInputCSV', () => {
+  it('renders correctly', () => {
+    const tree = renderer.create(<TableInputCSV />).toJSON();
+    //expect(tree).toMatchSnapshot();
+    expect(tree).toBeDefined();
+  });
+});

+ 144 - 0
packages/grafana-ui/src/components/Table/TableInputCSV.tsx

@@ -0,0 +1,144 @@
+import React from 'react';
+import debounce from 'lodash/debounce';
+import Papa, { ParseError, ParseMeta } from 'papaparse';
+import { TableData, Column } from '../../types/data';
+
+// Subset of all parse configs
+export interface ParseConfig {
+  delimiter?: string; // default: ","
+  newline?: string; // default: "\r\n"
+  quoteChar?: string; // default: '"'
+  encoding?: string; // default: ""
+  comments?: boolean | string; // default: false
+}
+
+interface ParseResults {
+  table: TableData;
+  meta: ParseMeta;
+  errors: ParseError[];
+}
+
+export function parseCSV(text: string, config?: ParseConfig): ParseResults {
+  const results = Papa.parse(text, { ...config, dynamicTyping: true, skipEmptyLines: true });
+
+  const { data, meta, errors } = results;
+  if (!data || data.length < 1) {
+    if (!text) {
+      errors.length = 0; // clear other errors
+    }
+    errors.push({
+      type: 'warning', // A generalization of the error
+      message: 'Empty Data',
+      code: 'empty',
+      row: 0,
+    });
+    return {
+      table: {
+        columns: [],
+        rows: [],
+        type: 'table',
+        columnMap: {},
+      } as TableData,
+      meta,
+      errors,
+    };
+  }
+
+  let same = true;
+  let cols = data[0].length;
+  data.forEach(row => {
+    if (cols !== row.length) {
+      same = false;
+      cols = Math.max(cols, row.length);
+    }
+  });
+
+  // Use a second pass to update the sizes
+  if (!same) {
+    errors.push({
+      type: 'warning', // A generalization of the error
+      message: 'not all rows have the same width',
+      code: 'width',
+      row: 0,
+    });
+    // Add null values to the end of all short arrays
+    data.forEach(row => {
+      const diff = cols - row.length;
+      for (let i = 0; i < diff; i++) {
+        row.push(null);
+      }
+    });
+  }
+
+  const first = results.data.shift();
+  return {
+    table: {
+      columns: first.map((v: any, index: number) => {
+        if (!v) {
+          v = 'Column ' + (index + 1);
+        }
+        return {
+          text: v.toString().trim(),
+        } as Column;
+      }),
+      rows: results.data,
+      type: 'table',
+      columnMap: {},
+    } as TableData,
+    meta,
+    errors,
+  };
+}
+
+interface Props {
+  config?: ParseConfig;
+}
+
+interface State {
+  text: string;
+  results: ParseResults;
+}
+
+class TableInputCSV extends React.PureComponent<Props, State> {
+  constructor(props: Props) {
+    super(props);
+    this.state = {
+      text: '',
+      results: parseCSV('', this.props.config),
+    };
+  }
+
+  readCSV = debounce(() => {
+    const results = parseCSV(this.state.text, this.props.config);
+    this.setState({ results });
+    console.log('GOT:', results);
+  }, 150);
+
+  componentDidUpdate(prevProps: Props, prevState: State) {
+    if (this.state.text !== prevState.text || this.props.config !== prevProps.config) {
+      this.readCSV();
+    }
+  }
+
+  handleChange = (event: any) => {
+    this.setState({ text: event.target.value });
+  };
+  handleBlur = (event: React.SyntheticEvent<HTMLTextAreaElement>) => {
+    //  console.log('BLUR', event);
+  };
+
+  render() {
+    const { table, errors } = this.state.results;
+
+    return (
+      <div>
+        <textarea value={this.state.text} onChange={this.handleChange} onBlur={this.handleBlur} />
+        <div>
+          BAR: / ROWS:{table.rows.length} / COLS:{table.columns.length} / {JSON.stringify(errors)}
+        </div>
+      </div>
+    );
+  }
+}
+
+export default TableInputCSV;

+ 11 - 0
packages/grafana-ui/src/components/Table/__snapshots__/TableInputCSV.test.tsx.snap

@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TableInputCSV renders correctly 1`] = `
+<div>
+  <textarea
+    onBlur={[Function]}
+    onChange={[Function]}
+    value=""
+  />
+</div>
+`;

+ 1 - 1
packages/grafana-ui/src/types/data.ts

@@ -53,7 +53,7 @@ export interface TimeSeriesVMs {
   length: number;
 }
 
-interface Column {
+export interface Column {
   text: string;
   title?: string;
   type?: string;

+ 12 - 0
yarn.lock

@@ -1801,6 +1801,13 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.40.tgz#4314888d5cd537945d73e9ce165c04cc550144a4"
   integrity sha512-RRSjdwz63kS4u7edIwJUn8NqKLLQ6LyqF/X4+4jp38MBT3Vwetewi2N4dgJEshLbDwNgOJXNYoOwzVZUSSLhkQ==
 
+"@types/papaparse@^4.5.9":
+  version "4.5.9"
+  resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-4.5.9.tgz#ff887bd362f57cd0c87320d2de38ac232bb55e81"
+  integrity sha512-8Pmxp2IEd/y58tOIsiZkCbAkcKI7InYVpwZFVKJyweCVnqnVahKXVjfSo6gvxUVykQsJvtWB+s6Kc60znVfQVw==
+  dependencies:
+    "@types/node" "*"
+
 "@types/prop-types@*":
   version "15.5.8"
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce"
@@ -12993,6 +13000,11 @@ pako@~1.0.5:
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.8.tgz#6844890aab9c635af868ad5fecc62e8acbba3ea4"
   integrity sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==
 
+papaparse@^4.6.3:
+  version "4.6.3"
+  resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-4.6.3.tgz#742e5eaaa97fa6c7e1358d2934d8f18f44aee781"
+  integrity sha512-LRq7BrHC2kHPBYSD50aKuw/B/dGcg29omyJbKWY3KsYUZU69RKwaBHu13jGmCYBtOc4odsLCrFyk6imfyNubJQ==
+
 parallel-transform@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"