GROWI CSVプレビュープラグインの紹介

[オープンソースのWikiであるGROWI](https://growi.org/ja/)にはプラグイン機能が用意されています。自社のデータを表示したり、表示をカスタマイズするのに利用できます。

今回は、GROWIプラグインとして作ったCSVプレビュープラグインを紹介します。

![image2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/a65f25fd-8777-ef7e-2efa-f137bce3434b.jpeg)


## プラグインを追加する

利用する際には、GROWIの管理画面の `プラグイン` にて追加してください。URLは `https://github.com/goofmint/growi-plugin-csv-preview` です。

![Admin](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/f5b3e2d3-ccf3-d536-d59e-7301ea1b2f7e.jpeg)

## 使い方

利用する際には、編集画面でCSVファイルをアップロードします。そうすると、右側のプレビューにてCSVファイルの内容が表示されます。

![image1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/e3b08f66-841e-00c8-62fa-996d09ac031b.jpeg)


これは編集中のプレビュー、ページ表示時の両方に対応しています。

![image3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/c1ddcff5-acbe-2f36-00c7-7221ed2971db.jpeg)


## プレビュー表示させたくない場合

プレビュー表示させたくない場合は、ファイル名を `.csv` で終わらない形（末尾にスペースを入れるなど）に変更してください。

## コードについて

今回は添付ファイルを利用しているので、プラグインコンポーネントで `attachment` の処理を上書きしています。 `CSVPreview` がCSVプレビュー用のコンポーネントです。

```tsx
const activate = (): void => {
  if (growiFacade == null || growiFacade.markdownRenderer == null) {
    return;
  }

  const { optionsGenerators } = growiFacade.markdownRenderer;

  // For page view
  optionsGenerators.customGenerateViewOptions = (...args) => {
    const options = optionsGenerators.generateViewOptions(...args);
    const { attachment } = options.components;
    options.components.attachment = CSVPreview(attachment); // Wrap the default component
    return options;
  };

  // For preview
  optionsGenerators.customGeneratePreviewOptions = (...args) => {
    const preview = optionsGenerators.generatePreviewOptions(...args);
    const { attachment } = preview.components;
    preview.components.attachment = CSVPreview(attachment); // Wrap the default component
    return preview;
  };
};
```

そして、添付ファイル名が `.csv` で終わっていれば、プレビュー表示処理に入ります。

```tsx
export const CSVPreview = (Tag: React.FunctionComponent<any>): React.FunctionComponent<any> => {
  return ({ children, ...props }) => {
    const { attachmentName, url }: {attachmentName: string, url: string} = props;
    if (!attachmentName.endsWith('.csv')) {
			// 拡張子が.csvでない場合は、通常の処理を行う
      return (<Tag {...props}>
        {children}
      </Tag>);
    }
    try {
      return (<>
				{/* プレビュー表示用の処理 */}
      </>
      );
    }
    catch (err) {
      // console.error(err);
    }
    // Return the original component if an error occurs
    return (
      <Tag {...props}>{children}</Tag>
    );
  };
};
```

### プレビュー表示について

プレビュー表示はファイルを読み込む（非同期処理）なので、 `react-async` を使います。

```tsx
return (<>
	<strong>Preview: {attachmentName}</strong>
	<Async promiseFn={loadCSV} filePath={url}>
		{({ data, error, isPending }) => {
			if (isPending) return 'Loading...';
			if (error) return `Something went wrong: ${error.message}`;
			if (data) {
				return (<>
					{/* プレビュー表示処理 */}
				</>
				);
			}
			return null;
		}}
	</Async>
</>
);
```

`loadCSV` は以下のようになります。ファイルパスを受け取り、それをテキストとして読み込みます。また、 [vanillaes/csv](https://github.com/vanillaes/csv) を使ってCSVをパースします。

```tsx
const loadCSV = async({ filePath }: any, { signal }: any) => {
  const res = await fetch(filePath, { signal });
  const text = await res.text();
  return parse(text);
};
```

そして、受け取った配列をテーブル表示します。

```tsx
return (<>
	<table className='table'>
		<thead>
			<tr>
				{data[0].map((row: string) => (
					<td>{row}</td>
				))}
			</tr>
		</thead>
		<tbody>
			{data.slice(1).map((row: string[]) => (
				<tr>
					{row.map(str => (
						<td>{str}</td>
					))}
				</tr>
			))}
		</tbody>
	</table>
	<Tag {...props}>
		{children}
	</Tag>
</>
);
```

## まとめ

GROWIプラグインを使うと、表示を自由に拡張できます。足りない機能があれば、どんどん追加できます。ぜひ、自分のWikiをカスタマイズしましょう。

[OSS開発wikiツールのGROWI | 快適な情報共有を、全ての人へ](https://growi.org/ja/)