Skip to content

Commit

Permalink
Merge pull request #18 from associatedpress/form-structure
Browse files Browse the repository at this point in the history
Add `layout` array to schema for non-input content
  • Loading branch information
Andrew Milligan authored Nov 18, 2021
2 parents 0621326 + 42c918d commit f1a26ed
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 26 deletions.
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
registry=https://registry.yarnpkg.com
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license": "ISC",
"sideEffects": false,
"engines": {
"node": "12.x",
"node": ">=12.x",
"yarn": "1.x"
},
"scripts": {
Expand Down Expand Up @@ -84,6 +84,7 @@
"helmet": "^4.4.1",
"http-proxy-middleware": "^1.0.6",
"jsonwebtoken": "^8.5.1",
"marked": "^4.0.1",
"mini-css-extract-plugin": "^0.9.0",
"moment": "^2.29.1",
"react": "^16.13.1",
Expand All @@ -94,6 +95,7 @@
"react-redux": "^7.2.2",
"react-select": "^3.1.0",
"redux": "^4.0.5",
"sanitize-html": "^2.5.3",
"styled-components": "^5.1.1"
},
"babel": {
Expand Down
69 changes: 67 additions & 2 deletions server/stores/__tests__/schema.tests.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import parseSchema from '../schema'
import { marked } from 'marked'
import sanitizeHtml from 'sanitize-html'

const parseMarkdown = s => sanitizeHtml(marked(s || ''))

const describeFieldType = (opts) => {
const {
Expand Down Expand Up @@ -157,7 +161,7 @@ describe('schema', () => {
expect(schema.headline).toEqual(headline)
})

it('should handle the chatter config', () => {
it('should handle the chatter config and parse it as markdown', () => {
// GIVEN
const formType = 'd'
const formId = '12345'
Expand All @@ -170,7 +174,7 @@ describe('schema', () => {
const schema = parseSchema(formType, formId, configs)

// THEN
expect(schema.chatter).toEqual(chatter)
expect(schema.chatter).toEqual(parseMarkdown(chatter))
})

it('should handle the index config', () => {
Expand Down Expand Up @@ -430,5 +434,66 @@ describe('schema', () => {
expect(columnSchema.columns).toEqual(relativeSchema.relatives[relative])
})
})

describe('text', () => {
it('should parse text as markdown', () => {
// GIVEN
const formType = 'd'
const formId = '12345'
const text = 'some **markdown**'
const configs = [
['text', text],
]

// WHEN
const schema = parseSchema(formType, formId, configs)

// THEN
expect(schema.layout).toEqual([{
type: 'text',
html: parseMarkdown(text),
}])
})
})

describe('layout', () => {
it('should interleave text and columns in layout blocks', () => {
// GIVEN
const formType = 'd'
const formId = '12345'
const text1 = 'some **markdown**'
const text2 = 'some _more_ **markdown**'
const configs = [
['text', text1],
['column', 'Name', 'string'],
['text', text2],
['column', 'Occupation', 'string'],
]

// WHEN
const schema = parseSchema(formType, formId, configs)

// THEN
expect(schema.columns).toHaveLength(2)
expect(schema.layout).toEqual([
{
type: 'text',
html: parseMarkdown(text1),
},
{
type: 'column',
index: 0,
},
{
type: 'text',
html: parseMarkdown(text2),
},
{
type: 'column',
index: 1,
},
])
})
})
})
})
10 changes: 10 additions & 0 deletions server/stores/schema.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const CSV = require('csv-string')
const { marked } = require('marked')
const sanitizeHtml = require('sanitize-html')

function identity(x) {
return x
Expand Down Expand Up @@ -98,13 +100,15 @@ function parseSchema(type, form, configs) {
id: form,
path: `/${type}/${form}`,
},
layout: [],
}
for (let config of configs) {
const [configType, ...cfg] = config.filter(c => c)
if (configType === 'column') {
schema.columns = schema.columns || []
const column = parseColumnSchema(cfg, schema.columns.length)
schema.columns.push(column)
schema.layout.push({ type: 'column', index: schema.columns.length - 1 })
} else if (configType === 'relative_column') {
schema.relatives = schema.relatives || {}
const column = parseColumnSchema(cfg)
Expand All @@ -115,6 +119,12 @@ function parseSchema(type, form, configs) {
const relativeCols = schema.relatives[column.relative]
const id = relativeCols.length
relativeCols.push({ ...column, id })
} else if (configType === 'text') {
const html = sanitizeHtml(marked(cfg[0] || ''))
schema.layout.push({ type: 'text', html })
} else if (configType === 'chatter') {
const html = sanitizeHtml(marked(cfg[0] || ''))
schema.chatter = html
} else {
schema[configType.toLowerCase()] = cfg[0]
}
Expand Down
18 changes: 12 additions & 6 deletions src/js/components/app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@ function App(props) {
window.onbeforeunload = () => dirty ? true : undefined
}, [dirty])

const { index, columns = [] } = schema
const { index, columns = [], layout = [] } = schema
const indexKeys = index && index.split('+')
const indexFields = index && columns.filter(col => indexKeys.includes(col.config.key))
const indexMissing = index && indexFields.some(col => values[col.id] == null)
const nonIndexFields = columns.filter(col => !index || !indexKeys.includes(col.config.key))
const indexBlocks = index && columns
.map((col, i) => [i, col])
.filter(([i, col]) => indexKeys.includes(col.config.key))
.map(([i]) => ({ type: 'column', index: i }))
const indexMissing = index && indexBlocks.some(({ index }) => values[columns[index].id] == null)
const nonIndexBlocks = layout
.filter(block => !index || block.type !== 'column' || !indexKeys.includes(columns[block.index].config.key))

const submitForm = () => {
const msg = 'Submit data? Please make sure entered data is correct.'
Expand Down Expand Up @@ -93,7 +97,8 @@ function App(props) {
<>
{index && (
<Form
fields={indexFields}
blocks={indexBlocks}
columns={columns}
controls={[{
label: 'Search',
onClick: loadIndexData,
Expand All @@ -105,7 +110,8 @@ function App(props) {
)}
{indexLoaded && (
<Form
fields={nonIndexFields}
blocks={nonIndexBlocks}
columns={columns}
controls={[{ label: 'Submit', onClick: submitForm, primary: true }]}
values={values}
errors={errors}
Expand Down
38 changes: 26 additions & 12 deletions src/js/components/form/Form.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Controls, Field } from 'js/components'
import { Controls, Field, TextBlock } from 'js/components'

function Form(props) {
const {
fields,
blocks,
columns,
controls,
values,
errors,
setField,
validateField,
} = props

if (!fields) return null
if (!blocks || !columns) return null

function renderBlock(block) {
if (block.type === 'text') return (
<TextBlock dangerouslySetInnerHTML={{ __html: block.html }} />
)

const field = columns[block.index]
return (
<Field
key={field.id}
schema={field}
value={values[field.id]}
errors={errors[field.id]}
setField={value => setField({ fieldId: field.id, value })}
validateField={() => validateField({ fieldId: field.id })}
/>
)
}

return (
<>
{fields.map(field => (
<Field
key={field.id}
schema={field}
value={values[field.id]}
errors={errors[field.id]}
setField={value => setField({ fieldId: field.id, value })}
validateField={() => validateField({ fieldId: field.id })}
/>
{blocks.map((block, i) => (
<React.Fragment key={i}>
{renderBlock(block)}
</React.Fragment>
))}
<Controls buttons={controls} />
</>
Expand Down
3 changes: 2 additions & 1 deletion src/js/components/header/Header.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { TextBlock } from 'js/components'
import { TitleContainer, DownloadLink, DownloadIcon } from './styles'

function Header(props) {
Expand All @@ -21,7 +22,7 @@ function Header(props) {
</DownloadLink>
</TitleContainer>
)}
{chatter && <div>{chatter}</div>}
{chatter && <TextBlock dangerouslySetInnerHTML={{ __html: chatter }} />}
</div>
)
}
Expand Down
4 changes: 3 additions & 1 deletion src/js/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import ErrorBoundary from './error_boundary'
import LandingPage from './landing_page'
import Footer from './footer'
import Layout from './layout'
import TextBlock from './text_block'

export {
App,
Expand All @@ -43,7 +44,8 @@ export {
ErrorBoundary,
LandingPage,
Footer,
Layout
Layout,
TextBlock
}

export default App
22 changes: 22 additions & 0 deletions src/js/components/text_block/TextBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import styled from 'styled-components'
import { lightNeutral } from 'js/styles/colors'

const TextBlock = styled.div`
p {
margin-block-start: 1em;
margin-block-end: 0em;
margin-inline-start: 0px;
margin-inline-end: 0px;
}
hr {
border: none;
width: 100%;
max-width: 400px;
height: 4px;
margin: 2em auto;
background-color: ${lightNeutral};
}
`

export default TextBlock
3 changes: 3 additions & 0 deletions src/js/components/text_block/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import TextBlock from './TextBlock'

export default TextBlock
Loading

0 comments on commit f1a26ed

Please sign in to comment.