Skip to content

Commit 2a26764

Browse files
Merge pull request #2235 from nextcloud/feature/114/tables
Feature/114/tables
2 parents 0db660b + 52c36ba commit 2a26764

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1460
-43
lines changed

.github/workflows/cypress.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ jobs:
102102
config: defaultCommandTimeout=10000,video=false
103103
env:
104104
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
105+
npm_package_name: ${{ env.APP_NAME }}
105106

106107
- name: Upload test failure screenshots
107108
uses: actions/upload-artifact@v2

css/icons.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
@include icon-black-white('ol', 'text', 1);
99
@include icon-black-white('ul', 'text', 1);
1010
@include icon-black-white('tasklist', 'text', 1);
11+
@include icon-black-white('table', 'text', 1);
1112
@include icon-black-white('hr', 'text', 1);
1213
@include icon-black-white('quote', 'text', 1);
1314
@include icon-black-white('paragraph', 'text', 1);
@@ -23,3 +24,10 @@
2324
@include icon-black-white('h4', 'text', 1);
2425
@include icon-black-white('h5', 'text', 1);
2526
@include icon-black-white('h6', 'text', 1);
27+
@include icon-black-white('add_col_before', 'text', 1);
28+
@include icon-black-white('add_col_after', 'text', 1);
29+
@include icon-black-white('add_row_before', 'text', 1);
30+
@include icon-black-white('add_row_after', 'text', 1);
31+
@include icon-black-white('delete_col', 'text', 1);
32+
@include icon-black-white('delete_row', 'text', 1);
33+
@include icon-black-white('table_settings', 'text', 1);

css/prosemirror.scss

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@use "sass:selector";
2+
13
/* Document rendering styles */
24
div.ProseMirror {
35
margin-top: 44px;
@@ -252,6 +254,66 @@ div.ProseMirror {
252254
}
253255
}
254256

257+
// table variables
258+
@at-root :root {
259+
--table-color-border: var(--color-border);
260+
--table-color-heading: var(--color-text-maxcontrast);
261+
--table-color-heading-border: var(--color-border-dark);
262+
--table-color-background: var(--color-main-background);
263+
--table-color-background-hover: var(--color-primary-light);
264+
--table-border-radius: var(--border-radius);
265+
}
266+
267+
table {
268+
border-spacing: 0;
269+
width: calc(100% - 50px);
270+
table-layout: fixed;
271+
white-space: normal; // force text to wrapping
272+
margin-bottom: 1em;
273+
+ & {
274+
margin-top: 1em;
275+
}
276+
277+
278+
td, th {
279+
border: 1px solid var(--table-color-border);
280+
border-left: 0;
281+
vertical-align: top;
282+
max-width: 100%;
283+
&:first-child {
284+
border-left: 1px solid var(--table-color-border);
285+
}
286+
}
287+
td {
288+
padding: 0.5em 0.75em;
289+
border-top: 0;
290+
color: var(--color-main-text);
291+
}
292+
th {
293+
padding: 0 0 0 0.75em;
294+
font-weight: normal;
295+
border-bottom-color: var(--table-color-heading-border);
296+
color: var(--table-color-heading);
297+
}
298+
tr {
299+
background-color: var(--table-color-background);
300+
&:hover, &:active, &:focus {
301+
background-color: var(--table-color-background-hover);
302+
}
303+
}
304+
305+
tr:first-child {
306+
th:first-child { border-top-left-radius: var(--table-border-radius); }
307+
th:last-child { border-top-right-radius: var(--table-border-radius); }
308+
}
309+
310+
tr:last-child {
311+
td:first-child { border-bottom-left-radius: var(--table-border-radius); }
312+
td:last-child { border-bottom-right-radius: var(--table-border-radius); }
313+
}
314+
315+
}
316+
255317
}
256318

257319
.ProseMirror-focused .ProseMirror-gapcursor {

cypress/fixtures/Table.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
## Preserve Tables
2+
3+
This is a table
4+
5+
| Header | other Header |
6+
|--------|--------------|
7+
| Cell | other cell |
8+
| Cell | other cell |
9+
10+
---
11+
12+
This is a table
13+
14+
| Header | other Header |
15+
|--------|--------------|
16+
| Cell | other cell |
17+
| Cell | other cell |
18+
19+
## Create a table
20+
21+
insertTable
22+
23+
---
24+
25+
| | | |
26+
|--|--|--|
27+
| | | |
28+
| | | |
29+
30+
did insertTable
31+
32+
## Create second tables
33+
34+
| | | |
35+
|--|--|--|
36+
| | | |
37+
| | | |
38+
39+
insertTable
40+
41+
---
42+
43+
| | | |
44+
|--|--|--|
45+
| | | |
46+
| | | |
47+
48+
| | | |
49+
|--|--|--|
50+
| | | |
51+
| | | |
52+
53+
did insertTable
54+
55+
## Add a new row at the end
56+
57+
| | | |
58+
|--|--|--|
59+
| | | |
60+
| | | addRowAfter |
61+
62+
63+
---
64+
65+
| | | |
66+
|--|--|--|
67+
| | | |
68+
| | | did addRowAfter |
69+
| | | |
70+
71+
## Add a new column at the end
72+
73+
| | | |
74+
|--|--|--|
75+
| | | |
76+
| | | addColumnAfter |
77+
78+
79+
---
80+
81+
| | | | |
82+
|--|--|--|--|
83+
| | | | |
84+
| | | did addColumnAfter | |
85+
86+
## Delete row at the end
87+
88+
| | | |
89+
|--|--|--|
90+
| | | |
91+
| | | deleteRow |
92+
93+
94+
---
95+
96+
| | | |
97+
|--|--|--|
98+
| | | |
99+
100+
## Delete column at the end
101+
102+
| | | |
103+
|--|--|--|
104+
| | | |
105+
| | | deleteColumn |
106+
107+
108+
---
109+
110+
| | |
111+
|--|--|
112+
| | |
113+
| | |
114+

cypress/integration/Table.spec.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import Table from './../../src/nodes/Table'
2+
import TableCell from './../../src/nodes/TableCell'
3+
import TableHeader from './../../src/nodes/TableHeader'
4+
import TableRow from './../../src/nodes/TableRow'
5+
import TableHeadRow from './../../src/nodes/TableHeadRow'
6+
import Markdown from './../../src/extensions/Markdown'
7+
import markdownit from './../../src/markdownit'
8+
import { createMarkdownSerializer } from './../../src/extensions/Markdown';
9+
import { findChildren, findChildrenByType } from 'prosemirror-utils'
10+
import createEditor from './../../src/tests/createEditor'
11+
import testData from '../fixtures/Table.md'
12+
13+
describe('ListItem extension integrated in the editor', () => {
14+
15+
const editor = createEditor({
16+
content: '',
17+
extensions: [
18+
Markdown,
19+
Table,
20+
TableCell,
21+
TableHeader,
22+
TableHeadRow,
23+
TableRow,
24+
],
25+
})
26+
27+
for (const spec of testData.split(/#+\s+/)){
28+
const [description, ...rest] = spec.split(/\n/)
29+
const [input, output] = rest.join('\n').split(/\n\n---\n\n/)
30+
if (!description) {
31+
continue
32+
}
33+
it(description, () => {
34+
expect(spec).to.include('\n')
35+
expect(input).to.be.ok
36+
expect(output).to.be.ok
37+
loadMarkdown(input)
38+
runCommands()
39+
expectMarkdown(output.replace(/\n*$/, ''))
40+
})
41+
}
42+
43+
function loadMarkdown(markdown) {
44+
editor.commands.setContent(markdownit.render(markdown))
45+
}
46+
47+
function runCommands() {
48+
let found
49+
while (found = findCommand()) {
50+
const name = found.node.text
51+
editor.commands.setTextSelection(found.pos)
52+
editor.commands[name]()
53+
const updated = findCommand()
54+
if (updated) {
55+
editor.commands.setTextSelection(updated.pos)
56+
editor.commands.insertContent('did ')
57+
}
58+
}
59+
}
60+
61+
function findCommand() {
62+
const doc = editor.state.doc
63+
return findChildren(doc, child => {
64+
return child.isText && editor.commands.hasOwnProperty(child.text)
65+
})[0]
66+
}
67+
68+
function expectMarkdown(markdown) {
69+
expect(getMarkdown().replace(/\n$/, '')).to.equal(markdown)
70+
}
71+
72+
function getMarkdown() {
73+
const serializer = createMarkdownSerializer(editor.schema)
74+
return serializer.serialize(editor.state.doc)
75+
}
76+
})

cypress/integration/workspace.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,22 @@ describe('Workspace', function() {
213213
menuButton('info').should('not.have.class', 'is-active')
214214
})
215215
})
216+
217+
it('inserts and removes a table', function() {
218+
openWorkspace()
219+
.type('Let\'s insert a Table')
220+
toggleMoreActions()
221+
popoverButton('table')
222+
.click()
223+
cy.get(`.ProseMirror`).type('content')
224+
cy.get(`.ProseMirror table tr:first-child th:first-child`)
225+
.should('contain', 'content')
226+
cy.get(`.ProseMirror .table-settings`).click()
227+
popoverButton('delete').click()
228+
cy.get(`.ProseMirror`)
229+
.should('not.contain', 'content')
230+
})
231+
216232
})
217233

218234
const menuButton = (name) => {
@@ -223,6 +239,14 @@ const submenuButton = (name) => {
223239
return cy.get(`#editor button .icon-${name}`)
224240
}
225241

242+
const popoverButton = (name) => {
243+
return cy.get(`.popover button .icon-${name}`)
244+
}
245+
246+
const toggleMoreActions = () => {
247+
cy.get('.menubar .action-item__menutoggle--default-icon').click()
248+
}
249+
226250
const menuBubbleButton = submenuButton
227251

228252
const openWorkspace = () => {

cypress/plugins/index.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,10 @@
1313

1414
const browserify = require('@cypress/browserify-preprocessor')
1515
const webpack = require('@cypress/webpack-preprocessor')
16-
const defaults = webpack.defaultOptions
16+
const webpackOptions = require('@nextcloud/webpack-vue-config')
1717

1818
module.exports = (on, config) => {
1919
on('file:preprocessor', browserify())
20-
defaults.webpackOptions.module.rules.push({
21-
test: /\.md/,
22-
type: 'asset/source',
23-
})
24-
on('file:preprocessor', webpack(defaults))
20+
webpackOptions.module.rules.push({ test: /\.md/, type: 'asset/source' })
21+
on('file:preprocessor', webpack({webpackOptions}))
2522
}

img/add_col_after.svg

Lines changed: 1 addition & 1 deletion
Loading

img/add_col_before.svg

Lines changed: 1 addition & 1 deletion
Loading

img/add_row_after.svg

Lines changed: 1 addition & 1 deletion
Loading

0 commit comments

Comments
 (0)