Skip to content

Commit

Permalink
feat(common): add array field components
Browse files Browse the repository at this point in the history
add array field form component and index component
  • Loading branch information
bahdcoder committed Apr 2, 2021
1 parent a20146e commit 9444865
Show file tree
Hide file tree
Showing 19 changed files with 263 additions and 23 deletions.
12 changes: 8 additions & 4 deletions examples/blog/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const {
textarea,
dateTime,
slug,
array,
hasMany,
belongsTo,
} = require('@tensei/core')
Expand All @@ -21,12 +22,15 @@ tensei()
.resources([
resource('Post')
.fields([
text('Title'),
text('Title').rules('required'),
slug('Slug').from('Title'),
textarea('Description'),
textarea('Description').rules('required', 'max:255'),
textarea('Content'),
dateTime('Published At'),
belongsTo('Category')
dateTime('Published At').rules('required'),
belongsTo('Category'),
array('Procedure')
.of('string')
.rules('required', 'min:3', 'max:10')
])
.displayField('Title'),
resource('Category')
Expand Down
5 changes: 2 additions & 3 deletions examples/blog/nodemon.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{
"watch": ["node_modules/", "index.js"],
"ext": "js, css"
}
"ignoreRoot": [".git"]
}
2 changes: 1 addition & 1 deletion examples/blog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"marked": "^2.0.1"
},
"scripts": {
"example:dev": "nodemon --watch node_modules/ src/index"
"example:dev": "nodemon --watch ../../node_modules/ index.js"
},
"devDependencies": {
"nodemon": "^2.0.7"
Expand Down
4 changes: 3 additions & 1 deletion packages/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,9 @@ class Auth {
})

if (this.config.httpOnlyCookiesAuth) {
const excludedRoutes = routes.filter(route => ! route.config.csrf)
const excludedRoutes = routes.filter(
route => !route.config.csrf
)

this.config.excludedPathsFromCsrf = [
...this.config.excludedPathsFromCsrf,
Expand Down
6 changes: 5 additions & 1 deletion packages/cms/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import Home from './pages/Home'
import IndexID from './index/ID'
import IndexText from './index/Text'

// Form
// Details
import DetailID from './detail/ID'
import DetailDate from './detail/Date'
import DetailText from './detail/Text'
import DetailArray from './detail/Array'
import DetailBoolean from './detail/Boolean'
import DetailOneToOne from './detail/OneToOne'
import DetailTextarea from './detail/Textarea'
Expand All @@ -27,6 +28,7 @@ import DetailManyToMany from './detail/ManyToMany'
// Form
import FormText from './form/Text'
import FormSlug from './form/Slug'
import FormArray from './form/Array'
import FormSelect from './form/Select'
import FormBoolean from './form/Boolean'
import FormDate from './form/DatePicker'
Expand Down Expand Up @@ -106,6 +108,7 @@ class Core {
Text: FormText,
Slug: FormSlug,
Date: FormDate,
Array: FormArray,
DateTime: FormDate,
Timestamp: FormDate,
Select: FormSelect,
Expand All @@ -128,6 +131,7 @@ class Core {
ID: DetailID,
Text: DetailText,
Date: DetailDate,
Array: DetailArray,
DateTime: DetailDate,
Timestamp: DetailDate,
Boolean: DetailBoolean,
Expand Down
25 changes: 25 additions & 0 deletions packages/cms/detail/Array/Array.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { EventHandler, SyntheticEvent } from 'react'
import { DetailComponentProps } from '@tensei/components'

const Array: React.FC<
DetailComponentProps & {
className?: string
onClick?: EventHandler<SyntheticEvent<HTMLParagraphElement>>
}
> = ({ value }) => {
if (!value) {
return null
}

return (
<ul className="list-disc">
{value.map((item: string | number, itemIndex: number) => (
<li className="mt-1" key={itemIndex}>
{item}
</li>
))}
</ul>
)
}

export default Array
1 change: 1 addition & 0 deletions packages/cms/detail/Array/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Array'
135 changes: 135 additions & 0 deletions packages/cms/form/Array/Array.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React, { useState, ChangeEvent, useEffect } from 'react'
import {
FormComponentProps,
Label,
TextInput,
Button
} from '@tensei/components'

const FormArray: React.FC<FormComponentProps> = ({
field,
name,
id,
value,
onChange,
error
}) => {
const [list, setList] = useState<string[]>(value || [])
const [newItem, setNewItem] = useState('')

useEffect(() => {
onChange(list)
}, [list])

const cross = (
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
)

const onListItemUpdate = (
event: ChangeEvent<HTMLInputElement>,
changedIndex: number
) => {
setList(
list.map((item, idx) => {
if (idx === changedIndex) {
return event.target.value
}

return item
})
)
}

const onListItemDelete = (deleteIndex: number) => {
setList(list.filter((item, itemIndex) => itemIndex !== deleteIndex))
}

const onListItemAdded = () => {
setList([...list, newItem])

setNewItem('')
}

return (
<>
<Label label={field.name} id={id} />
<div className="flex flex-wrap w-full">
{list.map((item, itemIndex) => (
<div
key={itemIndex}
className="w-full flex items-center mb-2"
>
<TextInput
value={item}
onChange={event =>
onListItemUpdate(event, itemIndex)
}
className="flex-1"
id={id}
name={name}
/>
<Button
onClick={() => onListItemDelete(itemIndex)}
className="ml-2"
danger
>
{cross}
</Button>
</div>
))}
<div className="w-full flex items-center">
<TextInput
placeholder={`Add a new ${field.name.toLowerCase()}`}
value={newItem}
onChange={event => setNewItem(event.target.value)}
className="flex-1"
id={id}
name={name}
/>
<Button
disabled={!newItem}
onClick={() => onListItemAdded()}
className="ml-2"
type="button"
primary
>
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</Button>
</div>
{error ? (
<i className="w-full text-tensei-error inline-block mt-2 text-sm">
{error}
</i>
) : null}
</div>
</>
)
}

export default FormArray
1 change: 1 addition & 0 deletions packages/cms/form/Array/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Array'
3 changes: 1 addition & 2 deletions packages/cms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@types/react-select": "^3.1.2",
"@types/speakingurl": "^13.0.2",
"@types/throttle-debounce": "^2.1.0",
"autoprefixer": "^10.2.4",
"autoprefixer": "^10.2.5",
"axios": "^0.21.1",
"classnames": "^2.2.6",
"copyfiles": "^2.4.1",
Expand Down Expand Up @@ -78,7 +78,6 @@
"@tensei/mail": "^0.7.35",
"@types/csurf": "^1.11.0",
"@types/express-session": "^1.17.3",
"autoprefixer": "^10.2.5",
"csurf": "^1.11.0",
"express-session": "^1.17.1",
"express-session-mikro-orm": "^0.7.35"
Expand Down
5 changes: 4 additions & 1 deletion packages/cms/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,10 @@ class CmsPlugin {
mailer.send(message => {
message
.to(user.email)
.from(process.env.ADMIN_SUPPORT_MAIL || `no-reply@${new URL(serverUrl).host}`)
.from(
process.env.ADMIN_SUPPORT_MAIL ||
`no-reply@${new URL(serverUrl).host}`
)
.subject(`Sign-in link for ${name}.`)
.html(
`
Expand Down
23 changes: 21 additions & 2 deletions packages/common/src/fields/Array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,34 @@ type ArrayTypes = 'string' | 'number' | 'decimal' | 'date'
export class ArrayField extends Field {
protected arrayOf: ArrayTypes = 'string'

public component = {
form: 'Array',
index: 'Array',
detail: 'Array'
}

constructor(name: string, databaseField?: string) {
super(name, databaseField)

this.property.type = 'string[]'
this.property.type = 'json'

this.rules('array')
this.arrayRules('string')

this.hideOnIndex()
}

public of(arrayOf: ArrayTypes) {
this.arrayOf = arrayOf
this.property.type = `${arrayOf}[]`
this.arrayValidationRules = []
this.arrayRules(
{
string: 'string',
decimal: 'number',
date: 'date',
number: 'number'
}[arrayOf]
)

return this
}
Expand Down
31 changes: 31 additions & 0 deletions packages/common/src/fields/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ export class Field implements FieldContract {
*/
public validationRules: Array<string> = []

/**
*
* Validation rules for fields that are in array format
*/
public arrayValidationRules: Array<string> = []

/**
*
* Rules for sanitizing data for a field
*/
public sanitizeRules: Array<SanitizationRules> = []

/**
Expand Down Expand Up @@ -664,6 +674,9 @@ export class Field implements FieldContract {
}

/**
*
* Set validation rules. These rules will be applied on both create
* and update
*
* @param this
*/
Expand All @@ -675,6 +688,24 @@ export class Field implements FieldContract {
return this
}

/**
*
* Set validation rules. These rules will be applied on both create
* and update
*
* @param this
*/
public arrayRules<T extends FieldContract>(
this: T,
...rules: Array<string>
): T {
this.arrayValidationRules = Array.from(
new Set([...this.arrayValidationRules, ...rules])
)

return this
}

/**
* Define a sanitization rule to be used when saving data for this field.
*
Expand Down
4 changes: 1 addition & 3 deletions packages/common/src/resources/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ export class Resource<ResourceType = {}> implements ResourceContract {
validationMessages: {
required: 'The {{ field }} is required.',
email: 'The {{ field }} must be a valid email address.',
unique: 'This {{ field }} has already been taked.',
min: 'This {{ field }} .',
max: 'This {{ field }} is too long.'
unique: 'This {{ field }} has already been taked.'
},
displayInNavigation: true,
perPageOptions: [10, 25, 50]
Expand Down
Loading

0 comments on commit 9444865

Please sign in to comment.