Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markdown editor #1618

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f745a23
Basic Prosemirror editor with Markdown
aslakhellesoy May 25, 2021
ca25620
Start implementing GFM table support
aslakhellesoy May 26, 2021
cd85b28
Merge main
aslakhellesoy May 26, 2021
69569d5
Merge branch 'main' into prosemirror
aslakhellesoy May 27, 2021
8add80e
Link to code for inserting table
aslakhellesoy May 27, 2021
79cab54
Merge branch 'main' into markdown-editor
aslakhellesoy May 27, 2021
458b932
Fix table serialization
aslakhellesoy May 27, 2021
382d094
Add styles
aslakhellesoy May 27, 2021
5586667
WIP: insert table
aslakhellesoy May 30, 2021
de0e47f
Merge main
aslakhellesoy Jun 8, 2021
fd4114d
Less table margin
aslakhellesoy Jun 8, 2021
77be679
Insert table works
aslakhellesoy Jun 8, 2021
1b55548
Merge main
aslakhellesoy Jun 17, 2021
94407e9
console.log the markdown
aslakhellesoy Jun 17, 2021
e600325
Add screenshot
aslakhellesoy Jun 17, 2021
c99b4c4
run rsync
aslakhellesoy Jun 17, 2021
404dd82
Added a MarkdownSimpleCodeEditor to storybook for rich<->text editing
aslakhellesoy Jun 17, 2021
64c4f07
Fix table insertion and serialization
aslakhellesoy Jun 18, 2021
a6b198a
Add animated gif to showcase Markdown generation
aslakhellesoy Jun 18, 2021
6a475cf
Add Gherkin, split things up
aslakhellesoy Jun 18, 2021
38b3e52
Start styling Gherkin documents
aslakhellesoy Jun 19, 2021
8e0818c
Find keyword lines
aslakhellesoy Jun 19, 2021
bc12605
Basic syntax highlighting
aslakhellesoy Jun 20, 2021
055fa9e
Highlight more keywords
aslakhellesoy Jun 21, 2021
6f09143
Simplify
aslakhellesoy Jun 21, 2021
450c61e
Cleanup
aslakhellesoy Jun 21, 2021
3e6c3d8
Extract highlight plugin
aslakhellesoy Jun 21, 2021
d76e189
eslint
aslakhellesoy Jun 21, 2021
c84864d
Cleanup
aslakhellesoy Jun 21, 2021
f430d51
Formatting
aslakhellesoy Jun 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions markdown-editor/javascript/.github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PLEASE DO NOT CREATE ISSUES IN THIS REPO.
THIS REPO IS A READ-ONLY MIRROR.

Create your issue in the Cucumber monorepo instead:
https://github.com/cucumber/cucumber/issues
5 changes: 5 additions & 0 deletions markdown-editor/javascript/.github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PLEASE DO NOT CREATE PULL REAUESTS IN THIS REPO.
THIS REPO IS A READ-ONLY MIRROR.

Create your pull request in the Cucumber monorepo instead:
https://github.com/cucumber/cucumber/pulls
18 changes: 18 additions & 0 deletions markdown-editor/javascript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
dist/
.idea/
.nyc_output/
coverage/
node_modules/
yarn.lock
package-lock.json
*.log
.deps
.tested*
.linted
.built*
.compared
.codegen
acceptance/
storybook-static
*-go
*.iml
6 changes: 6 additions & 0 deletions markdown-editor/javascript/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"require": ["tsconfig-paths/register", "ts-node/register", "source-map-support/register"],
"extension": ["ts", "tsx"],
"recursive": true,
"timeout": 10000
}
3 changes: 3 additions & 0 deletions markdown-editor/javascript/.rsync
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
../../LICENSE LICENSE
../../.templates/github/ .github/
../../.templates/javascript/ .
20 changes: 20 additions & 0 deletions markdown-editor/javascript/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
"stories": [
"../src/stories/**/*.stories.mdx",
"../src/stories/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links"
],
webpackFinal: async (config, { configType }) => {
config.resolve.fallback = { "assert": false }
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
})
return config
},
core: {
builder: "webpack5",
},
}
3 changes: 3 additions & 0 deletions markdown-editor/javascript/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
}
21 changes: 21 additions & 0 deletions markdown-editor/javascript/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) Cucumber Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
28 changes: 28 additions & 0 deletions markdown-editor/javascript/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
include default.mk

FEATURE_FILES = $(sort $(wildcard ../../compatibility-kit/javascript/features/**/*.ndjson))
TS_MESSAGE_FILES = $(patsubst ../../compatibility-kit/javascript/features/%.ndjson,acceptance/%.ts,$(FEATURE_FILES))

.codegen: $(TS_MESSAGE_FILES) dist/src/styles/cucumber-react.css

dist/src/styles/cucumber-react.css: src/styles/styles.scss src/styles/react-accessible-accordion.css
mkdir -p $(@D)
npx -y sass $< > $@
cat src/styles/react-accessible-accordion.css >> $@

# Convert an .ndjson file to a .ts file with Envelope objects that can be imported
acceptance/%.ts: ../../compatibility-kit/javascript/features/%.ndjson Makefile
mkdir -p $(@D)
echo "// Generated file. Do not edit." > $@
echo "export default [" >> $@
cat $< | sed "s/$$/,/" >> $@
echo "]" >> $@

.tested: .tested-storybook

.tested-storybook: $(TYPESCRIPT_SOURCE_FILES)
npm run build-storybook
touch $@

clean:
rm -f $(GHERKIN_DOCUMENT_NDJSON_FILES)
42 changes: 42 additions & 0 deletions markdown-editor/javascript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Cucumber Markdown Editor

Cucumber Markdown Editor is a React component that renders a rich-text editor for editing Gherkin Markdown documents.

It can import and export Gherkin Markdown documents.

## Internal Design

The editor is based on [ProseMirror](https://prosemirror.net/)

## Screenshot

![Markdown Editor](images/markdown-editor.gif)

## TODO

### Basic syntax highlighting

Users must be able to visually distinguish the parts of the Markdown document that contains
Gherkin contents from "regular" Markdown contents. They also need to know if there are any
Gherkin parser syntax errors so they can fix them.

This can be done with basic syntax highlighting, for example highlighting Gherkin contents in
a light blue colour, and highlighting lines with parse errors in a light red colour.

High level overview of how this is currently done:

* For every edit, serialize the ProseMirror document to Markdown with `markdownSerializer`
* Parse the Markdown into a GherkinDocument using Gherkin and a `GherkinInMarkdownTokenMatcher`
* Walk the GherkinDocument and build a list of line numbers (`gherkinLines`) that should be highlighted
* Parse the Markdown into a new ProseMirror document using `makeMarkdownParser`
* This will set a `gherkin` attribute on document nodes that are derived from Markdown nodes
that have a line number in the `gherkinLines` list.
* Use [decorations](https://prosemirror.net/docs/ref/#view.Decorations) to add a `gherkin` class to DOM
nodes corresponding to ProseMirror nodes with a `gherkin` attribute.

This works fine for the initial rendering. However, when the user edits the document, the `gherkin` classes
are not updated. For example, changing a non-Gherkin heading from `ScenariX` to `Scenario` does not
add the `gherkin` class.

Editing a heading doesn't seem to create a new node, so the `gherkin` attribute isn't updated on the node.
Or something like that...
88 changes: 88 additions & 0 deletions markdown-editor/javascript/default.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
SHELL := /usr/bin/env bash
# https://stackoverflow.com/questions/2483182/recursive-wildcards-in-gnu-make
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
TYPESCRIPT_SOURCE_FILES = $(sort $(call rwildcard,src test,*.ts *.tsx))
PRIVATE = $(shell node -e "console.log(require('./package.json').private)")
IS_TESTDATA = $(findstring -testdata,${CURDIR})
NPM_MODULE = $(shell cat package.json | jq .name --raw-output)

default: .tested
.PHONY: default

../../node_modules ../../package-lock.json: package.json
cd ../.. && npm install

.codegen:
touch $@

.tested: .tested-npm .built

.built: $(TYPESCRIPT_SOURCE_FILES) ../../node_modules ../../package-lock.json .codegen
pushd ../.. && \
npm run build && \
popd && \
touch $@

.tested-npm: $(TYPESCRIPT_SOURCE_FILES) ../../node_modules ../../package-lock.json .codegen
npm run test
touch $@

pre-release: clean update-version update-dependencies default
.PHONY: pre-release

update-dependencies:
../../node_modules/.bin/npm-check-updates --upgrade --reject hast-util-sanitize
.PHONY: update-dependencies

update-version:
ifeq ($(IS_TESTDATA),-testdata)
# no-op
else
ifdef NEW_VERSION
npm --no-git-tag-version --allow-same-version version "$(NEW_VERSION)"
# Update all npm packages that depend on us
pushd ../.. && \
./scripts/npm-each update_npm_dependency_if_exists package.json "$(NPM_MODULE)" "$(NEW_VERSION)"
# npm install
else
@echo -e "\033[0;31mNEW_VERSION is not defined. Can't update version :-(\033[0m"
exit 1
endif
endif
.PHONY: update-version

publish: .codegen
ifeq ($(IS_TESTDATA),-testdata)
# no-op
else
ifneq (true,$(PRIVATE))
npm publish --access public
else
@echo "Not publishing private npm module"
endif
endif
.PHONY: publish

post-release:
.PHONY: post-release

clean: clean-javascript
.PHONY: clean

clean-javascript:
rm -rf .deps .codegen .tested* coverage dist acceptance
.PHONY: clean-javascript

clobber: clean
rm -rf node_modules ../../node_modules
.PHONY: clobber

### COMMON stuff for all platforms

BERP_VERSION = 1.3.0
BERP_GRAMMAR = gherkin.berp

define berp-generate-parser =
-! dotnet tool list --tool-path /usr/bin | grep "berp\s*$(BERP_VERSION)" && dotnet tool update Berp --version $(BERP_VERSION) --tool-path /usr/bin
berp -g $(BERP_GRAMMAR) -t $< -o $@ --noBOM
endef
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions markdown-editor/javascript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "@cucumber/markdown-editor",
"version": "14.0.0",
"description": "Markdown editor for Cucumber",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/cucumber/cucumber.git"
},
"author": "Aslak Hellesøy",
"license": "MIT",
"scripts": {
"test": "mocha",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"dependencies": {
"@cucumber/gherkin": "19.0.3",
"@cucumber/messages": "16.0.1",
"@types/prosemirror-commands": "1.0.4",
"@types/prosemirror-history": "1.0.2",
"@types/prosemirror-keymap": "1.0.4",
"@types/prosemirror-markdown": "1.5.1",
"@types/prosemirror-menu": "1.0.3",
"@types/prosemirror-schema-basic": "1.0.2",
"@types/prosemirror-view": "1.17.1",
"markdown-it": "12.0.6",
"prosemirror-commands": "1.1.8",
"prosemirror-example-setup": "1.1.2",
"prosemirror-history": "1.1.3",
"prosemirror-keymap": "1.1.4",
"prosemirror-markdown": "1.5.1",
"prosemirror-menu": "1.1.4",
"prosemirror-model": "1.14.1",
"prosemirror-schema-basic": "1.1.2",
"prosemirror-state": "1.3.4",
"prosemirror-tables": "1.1.1",
"prosemirror-view": "1.18.7",
"use-prosemirror": "1.2.1"
},
"peerDependencies": {
"react": "~17",
"react-dom": "~17"
},
"devDependencies": {
"@babel/core": "7.14.3",
"@storybook/addon-actions": "6.2.9",
"@storybook/addon-links": "6.2.9",
"@storybook/builder-webpack5": "6.2.9",
"@storybook/react": "6.2.9",
"@types/jsdom": "16.2.10",
"@types/mocha": "8.2.2",
"@types/node": "15.3.1",
"@types/react": "17.0.6",
"@types/react-dom": "17.0.5",
"babel-loader": "8.2.2",
"core-js": "3.12.1",
"jsdom": "16.6.0",
"mocha": "8.4.0",
"prismjs": "1.23.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-is": "17.0.2",
"react-simple-code-editor": "0.11.0",
"source-map-support": "0.5.19",
"style-loader": "2.0.0",
"ts-node": "10.0.0",
"typescript": "4.2.4"
},
"bugs": {
"url": "https://github.com/cucumber/cucumber/issues"
},
"homepage": "https://github.com/cucumber/cucumber#readme",
"directories": {
"test": "test"
},
"keywords": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'prosemirror-view/style/prosemirror.css'
import 'prosemirror-example-setup/style/style.css'
import 'prosemirror-menu/style/menu.css'
import 'prosemirror-tables/style/tables.css'
import '../styles.css'

import React, { Dispatch, SetStateAction } from 'react'
import { ProseMirror } from 'use-prosemirror'
import { EditorState } from 'prosemirror-state'
import { cucumberMarkdownSerializer } from '../markdown/markdownSerializer'

type Props = {
state: EditorState
setState: Dispatch<SetStateAction<EditorState>>
setMarkdown: Dispatch<SetStateAction<string>>
}

const CucumberMarkdownProseMirror: React.FunctionComponent<Props> = ({
setMarkdown,
state,
setState,
}) => {
return (
<ProseMirror
state={state}
onChange={(newState) => {
const markdown = cucumberMarkdownSerializer.serialize(newState.doc)
setState(newState)
setMarkdown(markdown)
}}
/>
)
}

export default CucumberMarkdownProseMirror