Skip to content

Commit fbfd692

Browse files
committed
More colour blind friendly, and a bit of brutalist design
1 parent ed3b2bd commit fbfd692

File tree

9 files changed

+159
-56
lines changed

9 files changed

+159
-56
lines changed

docs/index.js

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/index.js.map

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

javascript/package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

javascript/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@codemirror/commands": "^0.19.5",
5454
"@codemirror/rangeset": "^0.19.1",
5555
"@codemirror/state": "^0.19.2",
56+
"@codemirror/tooltip": "^0.19.2",
5657
"@codemirror/view": "^0.19.9",
5758
"@headlessui/react": "1.4.1",
5859
"@stryker-mutator/core": "^5.4.1",

javascript/try/Try.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ export const Try: React.FunctionComponent<Props> = ({
9696

9797
return (
9898
<div>
99-
<CucumberExpressionInput value={expressionText} setValue={setExpressionText} />
99+
<CucumberExpressionInput
100+
value={expressionText}
101+
setValue={setExpressionText}
102+
error={expressionResult.error !== undefined}
103+
/>
100104
<ErrorComponent message={expressionResult.error?.message} />
101105
<TextInput value={stepText} setValue={setStepText} args={args} />
102106
<div className="flex justify-end">
@@ -146,11 +150,12 @@ export const Try: React.FunctionComponent<Props> = ({
146150
const CucumberExpressionInput: React.FunctionComponent<{
147151
value: string
148152
setValue: Dispatch<SetStateAction<string>>
149-
}> = ({ value, setValue }) => (
153+
error: boolean
154+
}> = ({ value, setValue, error }) => (
150155
<div className="mb-4">
151156
<label className="block">
152157
<Label>Cucumber Expression</Label>
153-
<ExpressionEditor value={value} setValue={setValue} autoFocus={true} />
158+
<ExpressionEditor value={value} setValue={setValue} error={error} autoFocus={true} />
154159
</label>
155160
</div>
156161
)

javascript/try/codemirror/ExpressionEditor.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ import React from 'react'
44
import { CodeMirrorElement, useEditorView, useExtension } from './codemirror.js'
55
import setLinesExtension from './setStateExtension.js'
66
import singleLineExtension from './singleLineExtension.js'
7-
import { baseTheme } from './theme.js'
7+
import { baseTheme, errorTheme, okTheme } from './theme.js'
88

99
export const ExpressionEditor: React.FunctionComponent<{
1010
value: string
1111
setValue: (newValue: string) => void
12+
error: boolean
1213
autoFocus: boolean
13-
}> = ({ value, setValue, autoFocus }) => {
14+
}> = ({ value, setValue, error, autoFocus }) => {
1415
const view = useEditorView(() =>
1516
EditorState.create({ doc: value, selection: EditorSelection.single(value.length) })
1617
)
1718
useExtension(view, () => baseTheme, [])
19+
useExtension(view, () => (error ? errorTheme : okTheme), [error])
1820
useExtension(view, () => singleLineExtension, [])
1921
useExtension(view, () => setLinesExtension((lines) => setValue(lines[0])), [])
2022

javascript/try/codemirror/TextEditor.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,31 @@ import { EditorSelection, EditorState } from '@codemirror/state'
22
import React from 'react'
33

44
import { Argument } from '../../src'
5+
import argTooltipExtension from './argTooltipExtension.js'
56
import { CodeMirrorElement, useEditorView, useExtension } from './codemirror.js'
67
import highlightArgsExtension from './highlightArgsExtension.js'
78
import setLinesExtension from './setStateExtension.js'
89
import singleLineExtension from './singleLineExtension.js'
9-
import { baseTheme, matchTheme, noMatchTheme } from './theme.js'
10+
import { baseTheme, cursorTooltipBaseTheme, errorTheme, okTheme } from './theme.js'
1011

1112
export const TextEditor: React.FunctionComponent<{
1213
value: string
1314
setValue: (newValue: string) => void
14-
autoFocus: boolean
1515
args: readonly Argument[] | null | undefined
16-
}> = ({ value, setValue, autoFocus, args }) => {
16+
autoFocus: boolean
17+
}> = ({ value, setValue, args, autoFocus }) => {
1718
const view = useEditorView(() =>
1819
EditorState.create({
1920
doc: value,
2021
selection: EditorSelection.single(value.length),
2122
})
2223
)
2324
useExtension(view, () => baseTheme, [])
24-
useExtension(view, () => (Array.isArray(args) ? matchTheme : noMatchTheme), [args])
25+
useExtension(view, () => (Array.isArray(args) ? okTheme : errorTheme), [args])
2526
useExtension(view, () => singleLineExtension, [])
2627
useExtension(view, () => setLinesExtension((lines) => setValue(lines[0])), [])
2728
useExtension(view, () => highlightArgsExtension(args), [args])
29+
useExtension(view, () => [argTooltipExtension(args), cursorTooltipBaseTheme], [args])
2830

2931
return <CodeMirrorElement autoFocus={autoFocus} view={view} />
3032
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { EditorState, StateField } from '@codemirror/state'
2+
import { showTooltip, Tooltip } from '@codemirror/tooltip'
3+
4+
import { Argument } from '../../src'
5+
6+
export default function argTooltipExtension(args: readonly Argument[] | undefined | null) {
7+
return StateField.define<readonly Tooltip[]>({
8+
create: getCursorTooltips,
9+
10+
update(tooltips, tr) {
11+
if (!tr.docChanged && !tr.selection) return tooltips
12+
return getCursorTooltips(tr.state)
13+
},
14+
15+
provide: (f) => showTooltip.computeN([f], (state) => state.field(f)),
16+
})
17+
18+
function getCursorTooltips(state: EditorState): readonly Tooltip[] {
19+
const cursorRange = state.selection.ranges.filter((range) => range.empty)
20+
if (cursorRange.length !== 1) return []
21+
const range = cursorRange[0]
22+
const line = state.doc.lineAt(range.head)
23+
const column = range.head - line.from
24+
const arg = (args || []).find(
25+
(arg) => (arg.group.start || -1) <= column && column < (arg.group.end || -1)
26+
)
27+
if (!arg) return []
28+
29+
const type = arg.parameterType.name
30+
const value = JSON.stringify(arg.getValue(null))
31+
const toolTip: Tooltip = {
32+
pos: range.head,
33+
above: true,
34+
strictSide: true,
35+
create: () => {
36+
const dom = document.createElement('div')
37+
dom.classList.add('cm-cursor-tooltip')
38+
dom.textContent = `${type}: ${value}`
39+
return { dom }
40+
},
41+
}
42+
return [toolTip]
43+
}
44+
}

javascript/try/codemirror/theme.ts

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,63 @@
11
import { EditorView } from '@codemirror/view'
22

3-
export const baseTheme = EditorView.theme(
4-
{
5-
'&': {
6-
border: 'solid',
7-
borderWidth: '1px',
8-
borderColor: '#6b7280', // text-gray-700
9-
padding: '6px',
10-
fontSize: '1rem',
11-
},
12-
'.cm-arg': {
13-
background: '#ffc010',
14-
},
15-
'&.cm-focused': {
16-
outline: '2px solid #2563eb',
17-
},
18-
'&.cm-focused .cm-selectionBackground, ::selection': {
19-
outline: '2px solid #2563eb',
20-
},
3+
/*
4+
IBM palette for colour-blind people
5+
https://davidmathlogic.com/colorblind/#%23648FFF-%23785EF0-%23DC267F-%23FE6100-%23FFB000
6+
*/
7+
8+
export const baseTheme = EditorView.theme({
9+
'&': {
10+
border: 'solid',
11+
borderWidth: '1px',
12+
borderColor: '#000000', // text-gray-700
13+
padding: '6px',
14+
fontSize: '1rem',
15+
color: '#ffffff',
16+
},
17+
'.cm-content': {
18+
caretColor: '#ffffff',
19+
},
20+
'&.cm-focused': {
21+
outline: '2px solid #000000',
2122
},
22-
{ dark: false }
23-
)
23+
'&.cm-focused .cm-selectionBackground, ::selection': {
24+
outline: '2px solid #000000',
25+
backgroundColor: '#785EF0',
26+
},
27+
'.cm-arg': {
28+
backgroundColor: '#FE6100',
29+
},
30+
})
2431

25-
export const matchTheme = EditorView.theme({
32+
export const okTheme = EditorView.theme({
2633
'&': {
27-
backgroundColor: 'rgba(209, 250, 229)',
34+
backgroundColor: '#648FFF',
2835
},
2936
})
3037

31-
export const noMatchTheme = EditorView.theme({
38+
export const errorTheme = EditorView.theme({
3239
'&': {
33-
backgroundColor: 'rgba(254, 226, 226)',
40+
backgroundColor: '#DC267F',
41+
},
42+
})
43+
44+
export const cursorTooltipBaseTheme = EditorView.baseTheme({
45+
'.cm-tooltip.cm-cursor-tooltip': {
46+
backgroundColor: '#000000',
47+
color: '#ffffff',
48+
transform: 'translate(-50%, -7px)',
49+
border: 'none',
50+
padding: '2px 7px',
51+
borderRadius: '10px',
52+
'&:before': {
53+
position: 'absolute',
54+
content: '""',
55+
left: '50%',
56+
marginLeft: '-5px',
57+
bottom: '-5px',
58+
borderLeft: '5px solid transparent',
59+
borderRight: '5px solid transparent',
60+
borderTop: '5px solid #000000',
61+
},
3462
},
3563
})

0 commit comments

Comments
 (0)