Skip to content

Commit ede01af

Browse files
authored
Merge pull request #25 from SundeepChand/feat/templates
Initial Request templates implementation
2 parents d7b2351 + fabc2c6 commit ede01af

File tree

6 files changed

+153
-65
lines changed

6 files changed

+153
-65
lines changed

package-lock.json

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

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
"private": false,
55
"homepage": "https://userstory.eosdesignsystem.com/",
66
"dependencies": {
7-
"@ckeditor/ckeditor5-build-classic": "^19.0.2",
8-
"@ckeditor/ckeditor5-react": "^2.1.0",
97
"@reach/router": "^1.2.1",
108
"@testing-library/jest-dom": "^4.2.4",
119
"@testing-library/react": "^9.5.0",
@@ -14,6 +12,7 @@
1412
"better-docs": "^2.0.1",
1513
"eos-icons": "^4.0.3",
1614
"i18next": "^19.4.5",
15+
"markdown-it": "^12.0.6",
1716
"node-sass": "^4.13.1",
1817
"react": "^16.13.1",
1918
"react-dom": "^16.13.1",
@@ -22,6 +21,7 @@
2221
"react-hook-form": "^6.3.2",
2322
"react-i18next": "^11.5.0",
2423
"react-markdown": "^5.0.2",
24+
"react-markdown-editor-lite": "^1.2.4",
2525
"react-promise-tracker": "^2.1.0",
2626
"react-router-dom": "^5.2.0",
2727
"react-scripts": "3.4.1",

src/components/MarkdownEditor.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React, { useState } from 'react'
2+
import MarkdownIt from 'markdown-it'
3+
import MdEditor from 'react-markdown-editor-lite'
4+
import 'react-markdown-editor-lite/lib/index.css'
5+
6+
const mdParser = new MarkdownIt().set({ html: true })
7+
8+
function MarkdownEditor({ value, callback }) {
9+
const [userInput, setUserInput] = useState('')
10+
11+
return (
12+
<MdEditor
13+
config={{
14+
view: {
15+
html: false
16+
}
17+
}}
18+
plugins={[
19+
'header',
20+
'font-bold',
21+
'font-italic',
22+
'list-unordered',
23+
'list-ordered',
24+
'link',
25+
'mode-toggle'
26+
]}
27+
style={{ height: '350px' }}
28+
renderHTML={(text) => mdParser.render(text)}
29+
onChange={({ html, text }) => {
30+
if (typeof value === typeof undefined) {
31+
setUserInput(text)
32+
}
33+
callback(html, text)
34+
}}
35+
value={value ?? userInput}
36+
/>
37+
)
38+
}
39+
40+
export default MarkdownEditor

src/pages/NewStory.js

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { useLayoutEffect, useState, useEffect, useContext } from 'react'
22
import { useForm } from 'react-hook-form'
33

4-
import CKEditor from '@ckeditor/ckeditor5-react'
5-
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
4+
import MarkdownEditor from '../components/MarkdownEditor'
5+
import { filterDescriptionText } from '../utils/filterText'
66
import axios from 'axios'
77
import { apiURL } from '../config.json'
88
import { trackPromise, usePromiseTracker } from 'react-promise-tracker'
@@ -18,13 +18,25 @@ import { navigate } from '@reach/router'
1818
import Context from '../modules/Context'
1919
import Login from './Login'
2020

21+
const initialDescriptionInputsValue = {
22+
None: ''
23+
}
24+
2125
const NewStory = () => {
2226
const { state } = useContext(Context)
2327

24-
const { register, handleSubmit, errors, setValue, watch } = useForm()
28+
const { register, handleSubmit, errors, watch } = useForm()
29+
30+
const [currentProductSelected, setCurrentProductSelected] = useState('None')
2531

2632
const [descriptionError, setDescriptionError] = useState(false)
2733

34+
const [descriptionInputs, setDescriptionInputs] = useState(
35+
initialDescriptionInputsValue
36+
)
37+
38+
const [description, setDescription] = useState('')
39+
2840
const [categories, setCategories] = useState([])
2941

3042
const [priorities, setPriorities] = useState([])
@@ -69,14 +81,24 @@ const NewStory = () => {
6981
products {
7082
id
7183
Name
84+
user_story_template
7285
}
7386
}`
7487
},
7588
{
7689
withCredentials: true
7790
}
7891
)
79-
setProducts(response.data.data.products)
92+
const { products } = response.data.data
93+
setProducts(products)
94+
const productToTemplateTextMap = {}
95+
products.forEach((product) => {
96+
productToTemplateTextMap[product.id] = product.user_story_template ?? ''
97+
})
98+
setDescriptionInputs({
99+
...initialDescriptionInputsValue,
100+
...productToTemplateTextMap
101+
})
80102
}
81103

82104
trackPromise(fetchProducts())
@@ -131,13 +153,18 @@ const NewStory = () => {
131153
}
132154
feature coming in next PR
133155
*/
156+
const handleProductSelectChange = (event) => {
157+
const id = event.target.value
158+
const selectedProduct = products.find((product) => product.id === id)
159+
setCurrentProductSelected(selectedProduct?.id ?? 'None')
160+
}
134161

135162
const onSubmit = async (data) => {
136-
if (data.description === undefined || data.description.length === 0) {
163+
if (!description?.length) {
137164
setDescriptionError(true)
138165
return
139166
}
140-
data.description = data.description.replace(/"/g, '\\"') // Replace all occurences of " with \"
167+
data.description = filterDescriptionText(description)
141168
await axios.post(
142169
`${apiURL}/graphql`,
143170
{
@@ -148,7 +175,6 @@ const NewStory = () => {
148175
Description: "${data.description}"
149176
Title: "${data.title}"
150177
Category: ${data.category}
151-
user_story_status: "5f0f33205f5695666b0d2e7e"
152178
product: "${data.product}"
153179
Priority: ${data.priority}
154180
}
@@ -165,9 +191,6 @@ const NewStory = () => {
165191
)
166192
navigate('/')
167193
}
168-
useEffect(() => {
169-
register('description')
170-
})
171194

172195
return state.auth ? (
173196
<>
@@ -208,6 +231,7 @@ const NewStory = () => {
208231
<select
209232
className='select-default'
210233
name='product'
234+
onChange={handleProductSelectChange}
211235
ref={register({ required: true })}
212236
>
213237
<option defaultValue={true} value=''>
@@ -268,25 +292,18 @@ const NewStory = () => {
268292
</div>
269293
<div className='form-element'>
270294
<label htmlFor='description'>Description</label>
271-
<CKEditor
272-
editor={ClassicEditor}
273-
config={{
274-
toolbar: [
275-
'heading',
276-
'|',
277-
'bold',
278-
'italic',
279-
'|',
280-
'link',
281-
'bulletedList',
282-
'numberedList'
283-
]
284-
}}
285-
onChange={(event, editor) => {
286-
setValue('description', editor.getData())
295+
<MarkdownEditor
296+
callback={(html, text) => {
297+
const result = {}
298+
result[currentProductSelected] = text
299+
setDescription(html)
287300
setDescriptionError(false)
301+
setDescriptionInputs({
302+
...descriptionInputs,
303+
...result
304+
})
288305
}}
289-
ref={register}
306+
value={descriptionInputs[currentProductSelected]}
290307
/>
291308
{descriptionError && <FormError type='emptyDescription' />}
292309
</div>

src/pages/Story.js

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import React, { useEffect, useState } from 'react'
2-
import CKEditor from '@ckeditor/ckeditor5-react'
3-
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
42
import axios from 'axios'
53
import { apiURL } from '../config.json'
64
import { trackPromise, usePromiseTracker } from 'react-promise-tracker'
@@ -17,6 +15,8 @@ import {
1715

1816
import { Helmet } from 'react-helmet'
1917

18+
import MarkdownEditor from '../components/MarkdownEditor'
19+
import { filterDescriptionText } from '../utils/filterText'
2020
import Comments from '../components/Comments'
2121
import Timeline from '../components/Timeline'
2222
import Button from '../components/Button'
@@ -99,15 +99,18 @@ const Story = (props) => {
9999
}, [storyId, userId])
100100

101101
const save = async (event) => {
102+
if (editDescription.length <= 0) {
103+
return
104+
}
102105
event.preventDefault()
106+
const combinedDescription = story.Description + editDescription
107+
const filteredDescription = filterDescriptionText(combinedDescription)
103108
await axios.post(
104109
`${apiURL}/graphql`,
105110
{
106111
query: `mutation {
107112
updateUserStory(
108-
input: { where: { id: "${storyId}" }, data: { Description: "${
109-
story.Description + editDescription
110-
}" } }
113+
input: { where: { id: "${storyId}" }, data: { Description: "${filteredDescription}" } }
111114
) {
112115
userStory {
113116
updatedAt
@@ -122,7 +125,7 @@ const Story = (props) => {
122125
setEditor(false)
123126
setStory({
124127
...story,
125-
Description: `${story.Description + editDescription}`
128+
Description: `${combinedDescription}`
126129
})
127130
}
128131

@@ -187,23 +190,9 @@ const Story = (props) => {
187190

188191
{editor ? (
189192
<>
190-
<CKEditor
191-
editor={ClassicEditor}
192-
config={{
193-
toolbar: [
194-
'heading',
195-
'|',
196-
'bold',
197-
'italic',
198-
'|',
199-
'link',
200-
'bulletedList',
201-
'numberedList'
202-
]
203-
}}
204-
onChange={(event, editor) => {
205-
const response = editor.getData()
206-
setDescription(response)
193+
<MarkdownEditor
194+
callback={(html) => {
195+
setDescription(html)
207196
}}
208197
/>
209198
</>

src/utils/filterText.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const filterDescriptionText = (text) => {
2+
text = text.replace(/\\*"/g, '\\"') // Replace all occurences of " with \"
3+
text = text.replace(/[\r\n]/g, '') // Remove the line endings
4+
return text
5+
}

0 commit comments

Comments
 (0)