Skip to content

Commit

Permalink
feat: @remirror/extension-tables (#254)
Browse files Browse the repository at this point in the history
This PR provides a minimal implementation for `@remirror/extension-tables`. Based on [prosemirror-tables](https://github.com/ProseMirror/prosemirror-tables)

The following will be added at a later date:

- `tableHeader`
- Merge cell 
- CSS style
- Provide a usable example in `examples/`

Closes #49
  • Loading branch information
ocavue committed Mar 2, 2020
1 parent 026d423 commit 20ad210
Show file tree
Hide file tree
Showing 15 changed files with 407 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .changeset/wild-tigers-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@remirror/extension-tables': minor
---

This version add a minimal implementation for tables. Based on
[prosemirror-tables](https://github.com/ProseMirror/prosemirror-tables).

Ses #49 and #254 for more information.
7 changes: 7 additions & 0 deletions .size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@
"ignore": ["@emotion/core"],
"running": false
},
{
"name": "@remirror/extension-tables",
"path": "@remirror/extension-tables/lib/dist/extension-tables.esm.js",
"limit": "120 KB",
"ignore": ["@types/react", "react"],
"running": false
},
{
"name": "@remirror/react",
"path": "@remirror/react/lib/dist/react.esm.js",
Expand Down
15 changes: 15 additions & 0 deletions @remirror/extension-tables/api-extractor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../support/api-extractor.json",
"mainEntryPointFilePath": "./lib/index.d.ts",
"apiReport": {
"enabled": true,
"reportFolder": "../../support/api/",
"reportFileName": "remirror__extension-tables.api.md",
"reportTempFolder": "./temp/"
},
"docModel": {
"enabled": true,
"apiJsonFilePath": "./temp/<unscopedPackageName>.api.json"
}
}
7 changes: 7 additions & 0 deletions @remirror/extension-tables/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const config = require('../../support/jest/jest.config');

module.exports = {
...config,
name: '@remirror/extension-tables',
displayName: 'extension-tables',
};
40 changes: 40 additions & 0 deletions @remirror/extension-tables/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@remirror/extension-tables",
"version": "0.0.0",
"description": "Table extension for the remirror wysiwyg editor",
"homepage": "https://github.com/remirror/remirror/tree/master/@remirror/extension-tables",
"repository": "https://github.com/remirror/remirror/tree/master/@remirror/extension-tables",
"license": "MIT",
"author": "Ocavue <ocavue@gmail.com>",
"files": [
"internal",
"lib",
"src"
],
"sideEffects": false,
"main": "lib/index.js",
"module": "lib/dist/extension-tables.esm.js",
"types": "lib/index.d.ts",
"dependencies": {
"@babel/runtime": "^7",
"@remirror/core": "^0.9.0",
"@types/prosemirror-commands": "^1.0.1",
"@types/prosemirror-keymap": "^1.0.1",
"@types/prosemirror-state": "^1.2.3",
"@types/prosemirror-transform": "^1.1.0",
"@types/prosemirror-view": "^1.11.2",
"prosemirror-commands": "^1.0.8",
"prosemirror-keymap": "^1.0.2",
"prosemirror-state": "^1.2.4",
"prosemirror-tables": "^1.0.0",
"prosemirror-transform": "^1.1.5",
"prosemirror-view": "^1.12.0"
},
"publishConfig": {
"access": "public"
},
"cjs": "lib/dist/extension-tables.cjs.js",
"meta": {
"sizeLimit": "120 KB"
}
}
16 changes: 16 additions & 0 deletions @remirror/extension-tables/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# @remirror/extension-tables

[![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@remirror/extension-tables.svg?)](https://bundlephobia.com/result?p=@remirror/extension-tables)
[![npm](https://img.shields.io/npm/dm/@remirror/extension-tables.svg?&logo=npm)](https://www.npmjs.com/package/@remirror/extension-tables)

## Installation

```bash
yarn add @remirror/extension-tables
```

```ts
import { TableExtension } from '@remirror/extension-tables';

new TableExtension();
```
182 changes: 182 additions & 0 deletions @remirror/extension-tables/src/__tests__/table.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { fromHTML, toHTML } from '@remirror/core';
import { BaseKeymapExtension } from '@remirror/core-extensions';
import { createBaseTestManager } from '@remirror/test-fixtures';
import { pmBuild } from 'jest-prosemirror';
import { renderEditor } from 'jest-remirror';

import { TableCellExtension, TableExtension, TableRowExtension } from '..';

describe('schema', () => {
const { schema } = createBaseTestManager([
{ extension: new TableExtension() },
{ extension: new TableRowExtension() },
{ extension: new TableCellExtension() },
]);

const { doc, table, tableRow, tableCell } = pmBuild(schema, {
table: { nodeType: 'table' },
tableRow: { nodeType: 'tableRow' },
tableCell: { nodeType: 'tableCell' },
});

const node = table(
tableRow(tableCell('A1'), tableCell('B1')),
tableRow(tableCell('A2'), tableCell('B2')),
tableRow(tableCell('A3'), tableCell('B3')),
);

const html = `<table><tr><td>A1</td><td>B1</td></tr><tr><td>A2</td><td>B2</td></tr><tr><td>A3</td><td>B3</td></tr></table>`;

it('dump to html', () => {
expect(toHTML({ node, schema })).toBe(html);
});

it('parse from html', () => {
expect(fromHTML({ content: html, schema })).toEqualProsemirrorNode(doc(node));
});
});

const create = () =>
renderEditor({
plainNodes: [new TableExtension(), new TableRowExtension(), new TableCellExtension()],
others: [{ priority: 10, extension: new BaseKeymapExtension() }],
});

describe('command', () => {
const setup = () => {
const {
view,
add,
nodes: { doc, p, table, tableRow, tableCell },
} = create();

const buildRegularTable = (rows: string[][]) => {
// Esnure that all rows have same length
expect(Array.from(new Set(rows.map(row => row.length)))).toHaveLength(1);

return table(...rows.map(row => tableRow(...row.map(cell => tableCell(cell)))));
};

return {
view,
add,
doc,
p,
buildRegularTable,
};
};

let { add, doc, p, buildRegularTable } = setup();
beforeEach(() => {
({ add, doc, p, buildRegularTable } = setup());
});

test('tableAddColumnAfter', () => {
const table = buildRegularTable([
['A1', 'B1<cursor>', 'C1'],
['A2', 'B2', 'C2'],
]);
const { state } = add(doc(table)).actionsCallback(actions => actions.tableAddColumnAfter());
expect(state.doc).toEqualRemirrorDocument(
doc(
buildRegularTable([
['A1', 'B1', '', 'C1'],
['A2', 'B2', '', 'C2'],
]),
),
);
});

test('tableAddColumnBefore', () => {
const table = buildRegularTable([
['A1', 'B1', 'C1'],
['A2', 'B2<cursor>', 'C2'],
]);
const { state } = add(doc(table)).actionsCallback(actions => actions.tableAddColumnBefore());
expect(state.doc).toEqualRemirrorDocument(
doc(
buildRegularTable([
['A1', '', 'B1', 'C1'],
['A2', '', 'B2', 'C2'],
]),
),
);
});

test('tableAddRowAfter', () => {
const table = buildRegularTable([
['A1<cursor>', 'B1'],
['A2', 'B2'],
]);
const { state } = add(doc(table)).actionsCallback(actions => actions.tableAddRowAfter());
expect(state.doc).toEqualRemirrorDocument(
doc(
buildRegularTable([
['A1', 'B1'],
['', ''],
['A2', 'B2'],
]),
),
);
});

test('tableAddRowBefore', () => {
const table = buildRegularTable([
['A1', 'B1<cursor>'],
['A2', 'B2'],
]);
const { state } = add(doc(table)).actionsCallback(actions => actions.tableAddRowBefore());
expect(state.doc).toEqualRemirrorDocument(
doc(
buildRegularTable([
['', ''],
['A1', 'B1'],
['A2', 'B2'],
]),
),
);
});

test('tableDeleteColumn', () => {
const table = buildRegularTable([
['A1', 'B1', 'C1'],
['A2<cursor>', 'B2', 'C2'],
]);
const { state } = add(doc(table)).actionsCallback(actions => actions.tableDeleteColumn());
expect(state.doc).toEqualRemirrorDocument(
doc(
buildRegularTable([
['B1', 'C1'],
['B2', 'C2'],
]),
),
);
});

test('tableDeleteRow', () => {
const table = buildRegularTable([
['A1', 'B1', 'C1'],
['A2', 'B2<cursor>', 'C2'],
['A3', 'B3', 'C3'],
]);
const { state } = add(doc(table)).actionsCallback(actions => actions.tableDeleteRow());
expect(state.doc).toEqualRemirrorDocument(
doc(
buildRegularTable([
['A1', 'B1', 'C1'],
['A3', 'B3', 'C3'],
]),
),
);
});

test('tableDeleteTable', () => {
const table = buildRegularTable([
['A1', 'B1', 'C1'],
['A2', 'B2', 'C2'],
['A3', 'B3<cursor>', 'C3'],
]);
const { state } = add(doc(table)).actionsCallback(actions => actions.tableDeleteTable());
expect(state.doc).toEqualRemirrorDocument(doc(p()));
});
});
2 changes: 2 additions & 0 deletions @remirror/extension-tables/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './table-extension';
export * from './table-types';
82 changes: 82 additions & 0 deletions @remirror/extension-tables/src/table-extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { NodeExtension } from '@remirror/core';
import {
addColumnAfter,
addColumnBefore,
addRowAfter,
addRowBefore,
deleteColumn,
deleteRow,
deleteTable,
tableEditing,
} from 'prosemirror-tables';

import { TableSchemaSpec } from './table-types';

export class TableExtension extends NodeExtension {
public readonly name = 'table';

public readonly schema: TableSchemaSpec = {
content: 'tableRow+',
tableRole: 'table',
isolating: true,
group: 'block',
parseDOM: [{ tag: 'table' }],
toDOM() {
return ['table', 0];
},
};

public plugin() {
return tableEditing();
}

public commands() {
return {
tableAddColumnAfter: () => addColumnAfter,
tableAddColumnBefore: () => addColumnBefore,
tableAddRowAfter: () => addRowAfter,
tableAddRowBefore: () => addRowBefore,
tableDeleteColumn: () => deleteColumn,
tableDeleteRow: () => deleteRow,
tableDeleteTable: () => deleteTable,
};
}
}

export class TableRowExtension extends NodeExtension {
public readonly name = 'tableRow';

public readonly schema: TableSchemaSpec = {
content: 'tableCell+',
tableRole: 'row',
parseDOM: [{ tag: 'tr' }],
toDOM() {
return ['tr', 0];
},
};
}

export class TableCellExtension extends NodeExtension {
public readonly name = 'tableCell';

public readonly schema: TableSchemaSpec = {
content: 'inline*',
attrs: {
colspan: {
default: 1,
},
rowspan: {
default: 1,
},
colwidth: {
default: null,
},
},
tableRole: 'cell',
isolating: true,
parseDOM: [{ tag: 'td' }, { tag: 'th' }],
toDOM() {
return ['td', 0];
},
};
}
5 changes: 5 additions & 0 deletions @remirror/extension-tables/src/table-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NodeExtensionSpec } from '@remirror/core';

export interface TableSchemaSpec extends NodeExtensionSpec {
tableRole: 'table' | 'row' | 'cell';
}

0 comments on commit 20ad210

Please sign in to comment.