builds RenderCustomComponent

This commit is contained in:
James
2020-05-27 18:50:05 -04:00
parent 67f62d5e8f
commit 2db103233c
17 changed files with 306 additions and 182 deletions

View File

@@ -1,7 +1,6 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import AnimateHeight from 'react-animate-height';
// import customComponents from '../../customComponents';
import SearchFilter from '../SearchFilter';
import ColumnSelector from '../ColumnSelector';
import WhereBuilder from '../WhereBuilder';

View File

@@ -3,11 +3,14 @@ import PropTypes from 'prop-types';
import './index.scss';
const baseClass = 'condition-value-date';
const DateField = ({ onChange, value }) => {
return (
<input
className={baseClass}
type="text"
onChange={onChange}
onChange={e => onChange(e.target.value)}
value={value}
/>
);

View File

@@ -0,0 +1,6 @@
@import '../../../../../scss/styles.scss';
@import '../../../../forms/field-types/shared.scss';
.condition-value-date {
@include formInput;
}

View File

@@ -3,19 +3,27 @@ import PropTypes from 'prop-types';
import './index.scss';
const baseClass = 'condition-value-number';
const NumberField = ({ onChange, value }) => {
return (
<input
placeholder="Enter a value"
className={baseClass}
type="number"
onChange={onChange}
onChange={e => onChange(e.target.value)}
value={value}
/>
);
};
NumberField.defaultProps = {
value: null,
};
NumberField.propTypes = {
onChange: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
value: PropTypes.string,
};
export default NumberField;

View File

@@ -0,0 +1,6 @@
@import '../../../../../scss/styles.scss';
@import '../../../../forms/field-types/shared.scss';
.condition-value-number {
@include formInput;
}

View File

@@ -3,19 +3,27 @@ import PropTypes from 'prop-types';
import './index.scss';
const baseClass = 'condition-value-text';
const Text = ({ onChange, value }) => {
return (
<input
placeholder="Enter a value"
className={baseClass}
type="text"
onChange={onChange}
onChange={e => onChange(e.target.value)}
value={value}
/>
);
};
Text.defaultProps = {
value: '',
};
Text.propTypes = {
onChange: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
value: PropTypes.string,
};
export default Text;

View File

@@ -0,0 +1,6 @@
@import '../../../../../scss/styles.scss';
@import '../../../../forms/field-types/shared.scss';
.condition-value-text {
@include formInput;
}

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import RenderCustomComponent from '../../../utilities/RenderCustomComponent';
import ReactSelect from '../../ReactSelect';
import Button from '../../Button';
import Date from './Date';
@@ -23,6 +24,7 @@ const Condition = (props) => {
value,
orIndex,
andIndex,
collectionSlug,
} = props;
const [activeField, setActiveField] = useState({ operators: [] });
@@ -35,6 +37,8 @@ const Condition = (props) => {
}
}, [value, fields]);
const ValueComponent = valueFields[activeField.component] || valueFields.Text;
return (
<div className={baseClass}>
<div className={`${baseClass}__wrap`}>
@@ -64,15 +68,18 @@ const Condition = (props) => {
/>
</div>
<div className={`${baseClass}__value`}>
<ReactSelect
value={value.value}
options={[
{
label: 'Option 1',
value: 'option-1',
},
]}
onChange={newValue => console.log('changing value', newValue)}
<RenderCustomComponent
path={`${collectionSlug}.fields.${activeField.value}.filter`}
DefaultComponent={ValueComponent}
componentProps={{
value: value.value,
onChange: updatedValue => dispatch({
type: 'update',
orIndex,
andIndex,
value: updatedValue || '',
}),
}}
/>
</div>
</div>
@@ -125,6 +132,7 @@ Condition.propTypes = {
dispatch: PropTypes.func.isRequired,
orIndex: PropTypes.number.isRequired,
andIndex: PropTypes.number.isRequired,
collectionSlug: PropTypes.string.isRequired,
};
export default Condition;

View File

@@ -13,6 +13,7 @@ const WhereBuilder = (props) => {
const {
collection: {
fields,
slug,
labels: {
plural,
} = {},
@@ -26,13 +27,15 @@ const WhereBuilder = (props) => {
useEffect(() => {
setReducedFields(fields.reduce((reduced, field) => {
if (typeof fieldTypes[field.type] === 'object') {
const formattedField = {
label: field.label,
value: field.name,
...fieldTypes[field.type],
};
return [
...reduced,
{
label: field.label,
value: field.name,
...fieldTypes[field.type],
},
formattedField,
];
}
@@ -74,6 +77,7 @@ const WhereBuilder = (props) => {
</div>
)}
<Condition
collectionSlug={slug}
value={where[orIndex][andIndex]}
orIndex={orIndex}
andIndex={andIndex}
@@ -119,6 +123,7 @@ const WhereBuilder = (props) => {
WhereBuilder.propTypes = {
handleChange: PropTypes.func.isRequired,
collection: PropTypes.shape({
slug: PropTypes.string,
fields: PropTypes.arrayOf(
PropTypes.shape({}),
),

View File

@@ -51,7 +51,7 @@ const reducer = (state, action = {}) => {
newState[orIndex][andIndex].field = field;
}
if (value) {
if (value !== undefined) {
newState[orIndex][andIndex].value = value;
}

View File

@@ -0,0 +1,40 @@
import React, { Suspense } from 'react';
import PropTypes from 'prop-types';
import customComponents from '../../customComponents';
import Loading from '../../elements/Loading';
const RenderCustomComponent = (props) => {
const { path, DefaultComponent, componentProps } = props;
const CustomComponent = path.split('.').reduce((res, prop) => {
if (res) {
return res[prop];
}
return false;
}, customComponents);
if (CustomComponent) {
return (
<Suspense fallback={<Loading />}>
<CustomComponent {...componentProps} />
</Suspense>
);
}
return (
<DefaultComponent {...componentProps} />
);
};
RenderCustomComponent.propTypes = {
path: PropTypes.string.isRequired,
DefaultComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.node,
PropTypes.element,
]).isRequired,
componentProps: PropTypes.shape({}).isRequired,
};
export default RenderCustomComponent;

View File

@@ -0,0 +1,161 @@
import React, { useEffect, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import queryString from 'qs';
import PropTypes from 'prop-types';
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
import Paginator from '../../../elements/Paginator';
import ListControls from '../../../elements/ListControls';
import Pill from '../../../elements/Pill';
import Button from '../../../elements/Button';
import SortColumn from '../../../elements/SortColumn';
import Table from '../../../elements/Table';
import './index.scss';
const { serverURL, routes: { api, admin } } = PAYLOAD_CONFIG;
const baseClass = 'collection-list';
const DefaultList = (props) => {
const {
collection,
collection: {
fields,
slug,
labels: {
singular: singularLabel,
plural: pluralLabel,
},
},
} = props;
const location = useLocation();
const [listControls, setListControls] = useState({});
const [sort, setSort] = useState(null);
const newDocumentURL = `${admin}/collections/${slug}/create`;
const { page } = queryString.parse(location.search, { ignoreQueryPrefix: true });
const apiURL = `${serverURL}${api}/${slug}`;
const [{ data }, { setParams }] = usePayloadAPI(apiURL, {});
useEffect(() => {
const params = {};
if (page) params.page = page;
if (sort) params.sort = sort;
if (listControls?.where) params.where = listControls.where;
setParams(params);
}, [setParams, page, sort, listControls]);
return (
<div className={baseClass}>
<header className={`${baseClass}__header`}>
<h1>{pluralLabel}</h1>
<Pill to={newDocumentURL}>
Create New
</Pill>
</header>
<ListControls
handleChange={setListControls}
collection={collection}
/>
{(data.docs && data.docs.length > 0) && (
<Table
data={data.docs}
columns={listControls.columns.map((col, i) => {
const field = fields.find(fieldToCheck => fieldToCheck.name === col);
return {
accessor: field.name,
components: {
Heading: (
<SortColumn
label={field.label}
name={field.name}
handleChange={setSort}
/>
),
renderCell: (rowData, cellData) => {
if (i === 0) {
return (
<>
<Link to={`${admin}/collections/${collection.slug}/${rowData.id}`}>
{typeof cellData === 'string' && cellData}
{typeof cellData === 'object' && JSON.stringify(cellData)}
</Link>
</>
);
}
return cellData;
},
},
};
})}
/>
)}
{(!data.docs || data.docs.length === 0) && (
<div className={`${baseClass}__no-results`}>
<p>
No
{' '}
{pluralLabel}
{' '}
found. Either no
{' '}
{pluralLabel}
{' '}
exist yet or none match the filters you&apos;ve specified above.
</p>
<Button
el="link"
to={newDocumentURL}
>
Create new
{' '}
{singularLabel}
</Button>
</div>
)}
<div className={`${baseClass}__page-controls`}>
<Paginator
totalDocs={data.totalDocs}
limit={data.limit}
totalPages={data.totalPages}
page={data.page}
hasPrevPage={data.hasPrevPage}
hasNextPage={data.hasNextPage}
prevPage={data.prevPage}
nextPage={data.nextPage}
numberOfNeighbors={1}
/>
<div className={`${baseClass}__page-info`}>
{data.page}
-
{data.totalPages > 1 ? data.limit : data.totalDocs}
{' '}
of
{' '}
{data.totalDocs}
</div>
</div>
</div>
);
};
DefaultList.propTypes = {
collection: PropTypes.shape({
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
slug: PropTypes.string,
useAsTitle: PropTypes.string,
fields: PropTypes.arrayOf(PropTypes.shape),
timestamps: PropTypes.bool,
}).isRequired,
};
export default DefaultList;

View File

@@ -1,166 +1,12 @@
import React, { useEffect, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import queryString from 'qs';
import PropTypes from 'prop-types';
import DefaultList from './Default';
import customComponents from '../../../customComponents';
import { useStepNav } from '../../../elements/StepNav';
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
import Paginator from '../../../elements/Paginator';
import ListControls from '../../../elements/ListControls';
import Pill from '../../../elements/Pill';
import Button from '../../../elements/Button';
import SortColumn from '../../../elements/SortColumn';
import Table from '../../../elements/Table';
import formatListFields from './formatListFields';
import './index.scss';
const { serverURL, routes: { api, admin } } = PAYLOAD_CONFIG;
const baseClass = 'collection-list';
const DefaultList = (props) => {
const {
collection,
collection: {
fields,
slug,
labels: {
singular: singularLabel,
plural: pluralLabel,
},
},
} = props;
const location = useLocation();
const [listControls, setListControls] = useState({});
const [sort, setSort] = useState(null);
const newDocumentURL = `${admin}/collections/${slug}/create`;
const { page } = queryString.parse(location.search, { ignoreQueryPrefix: true });
const apiURL = `${serverURL}${api}/${slug}`;
const [{ data }, { setParams }] = usePayloadAPI(apiURL, {});
useEffect(() => {
const params = {};
if (page) params.page = page;
if (sort) params.sort = sort;
if (listControls?.where) params.where = listControls.where;
setParams(params);
}, [setParams, page, sort, listControls]);
return (
<div className={baseClass}>
<header className={`${baseClass}__header`}>
<h1>{pluralLabel}</h1>
<Pill to={newDocumentURL}>
Create New
</Pill>
</header>
<ListControls
handleChange={setListControls}
collection={collection}
/>
{(data.docs && data.docs.length > 0) && (
<Table
data={data.docs}
columns={listControls.columns.map((col, i) => {
const field = fields.find(fieldToCheck => fieldToCheck.name === col);
return {
accessor: field.name,
components: {
Heading: (
<SortColumn
label={field.label}
name={field.name}
handleChange={setSort}
/>
),
renderCell: (rowData, cellData) => {
if (i === 0) {
return (
<>
<Link to={`${admin}/collections/${collection.slug}/${rowData.id}`}>
{typeof cellData === 'string' && cellData}
{typeof cellData === 'object' && JSON.stringify(cellData)}
</Link>
</>
);
}
return cellData;
},
},
};
})}
/>
)}
{(!data.docs || data.docs.length === 0) && (
<div className={`${baseClass}__no-results`}>
<p>
No
{' '}
{pluralLabel}
{' '}
found. Either no
{' '}
{pluralLabel}
{' '}
exist yet or none match the filters you&apos;ve specified above.
</p>
<Button
el="link"
to={newDocumentURL}
>
Create new
{' '}
{singularLabel}
</Button>
</div>
)}
<div className={`${baseClass}__page-controls`}>
<Paginator
totalDocs={data.totalDocs}
limit={data.limit}
totalPages={data.totalPages}
page={data.page}
hasPrevPage={data.hasPrevPage}
hasNextPage={data.hasNextPage}
prevPage={data.prevPage}
nextPage={data.nextPage}
numberOfNeighbors={1}
/>
<div className={`${baseClass}__page-info`}>
{data.page}
-
{data.totalPages > 1 ? data.limit : data.totalDocs}
{' '}
of
{' '}
{data.totalDocs}
</div>
</div>
</div>
);
};
DefaultList.propTypes = {
collection: PropTypes.shape({
labels: PropTypes.shape({
singular: PropTypes.string,
plural: PropTypes.string,
}),
slug: PropTypes.string,
useAsTitle: PropTypes.string,
fields: PropTypes.arrayOf(PropTypes.shape),
timestamps: PropTypes.bool,
}).isRequired,
};
const ListView = (props) => {
const {
collection,