chore: merges master

This commit is contained in:
James
2021-07-27 13:28:27 -04:00
64 changed files with 704 additions and 264 deletions

View File

@@ -130,7 +130,7 @@
backdrop-filter: saturate(180%) blur(5px);
width: 100%;
height: base(3);
z-index: $z-nav;
z-index: $z-modal;
&__scroll {
padding: 0;

View File

@@ -0,0 +1,28 @@
import React from 'react';
import { Props, isComponent } from './types';
import './index.scss';
const ViewDescription: React.FC<Props> = (props) => {
const {
description,
} = props;
if (isComponent(description)) {
const Description = description;
return <Description />;
}
if (description) {
return (
<div
className="view-description"
>
{typeof description === 'function' ? description() : description}
</div>
);
}
return null;
};
export default ViewDescription;

View File

@@ -0,0 +1,15 @@
import React from 'react';
export type DescriptionFunction = () => string
export type DescriptionComponent = React.ComponentType
type Description = string | DescriptionFunction | DescriptionComponent
export type Props = {
description?: Description
}
export function isComponent(description: Description): description is DescriptionComponent {
return React.isValidElement(description);
}

View File

@@ -0,0 +1,8 @@
@import '../../../scss/styles.scss';
.field-description {
display: flex;
padding-top: base(.25);
padding-bottom: base(.25);
color: $color-gray;
}

View File

@@ -0,0 +1,30 @@
import React from 'react';
import { Props, isComponent } from './types';
import './index.scss';
const FieldDescription: React.FC<Props> = (props) => {
const {
description,
value,
} = props;
if (isComponent(description)) {
const Description = description;
return <Description value={value} />;
}
if (description) {
return (
<div
className="field-description"
>
{typeof description === 'function' ? description({ value }) : description}
</div>
);
}
return null;
};
export default FieldDescription;

View File

@@ -0,0 +1,16 @@
import React from 'react';
export type DescriptionFunction = (value: unknown) => string
export type DescriptionComponent = React.ComponentType<{value: unknown}>
type Description = string | DescriptionFunction | DescriptionComponent
export type Props = {
description?: Description
value: unknown;
}
export function isComponent(description: Description): description is DescriptionComponent {
return React.isValidElement(description);
}

View File

@@ -3,7 +3,7 @@
label.field-label {
display: flex;
padding-bottom: base(.25);
color: $color-gray;
color: $color-dark-gray;
.required {
color: $color-red;

View File

@@ -11,6 +11,7 @@ import useFieldType from '../../useFieldType';
import Error from '../../Error';
import { array } from '../../../../../fields/validations';
import Banner from '../../../elements/Banner';
import FieldDescription from '../../FieldDescription';
import { Props, RenderArrayProps } from './types';
import './index.scss';
@@ -30,6 +31,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
permissions,
admin: {
readOnly,
description,
},
} = props;
@@ -130,6 +132,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
minRows={minRows}
maxRows={maxRows}
required={required}
description={description}
/>
);
};
@@ -154,6 +157,7 @@ const RenderArray = React.memo((props: RenderArrayProps) => {
minRows,
maxRows,
required,
description,
} = props;
const hasMaxRows = maxRows && rows.length >= maxRows;
@@ -171,6 +175,10 @@ const RenderArray = React.memo((props: RenderArrayProps) => {
</div>
<header className={`${baseClass}__header`}>
<h3>{label}</h3>
<FieldDescription
value={value}
description={description}
/>
</header>
<Droppable droppableId="array-drop">
{(provided) => (

View File

@@ -4,6 +4,14 @@
margin: base(2) 0;
min-width: base(15);
&__header {
h3 {
margin-bottom: 0;
}
margin-bottom: base(1);
}
&__error-wrap {
position: relative;
}

View File

@@ -1,5 +1,5 @@
import { Data } from '../../Form/types';
import { ArrayField, Labels, Field } from '../../../../../fields/config/types';
import { ArrayField, Labels, Field, Description } from '../../../../../fields/config/types';
import { FieldTypes } from '..';
import { FieldPermissions } from '../../../../../auth/types';
@@ -30,4 +30,5 @@ export type RenderArrayProps = {
showError: boolean
errorMessage: string
rows: Data[]
description?: Description
}

View File

@@ -17,6 +17,7 @@ import Popup from '../../../elements/Popup';
import BlockSelector from './BlockSelector';
import { blocks as blocksValidator } from '../../../../../fields/validations';
import Banner from '../../../elements/Banner';
import FieldDescription from '../../FieldDescription';
import { Props, RenderBlockProps } from './types';
import { DocumentPreferences } from '../../../../../preferences/types';
@@ -44,6 +45,7 @@ const Blocks: React.FC<Props> = (props) => {
permissions,
admin: {
readOnly,
description,
},
} = props;
@@ -179,6 +181,7 @@ const Blocks: React.FC<Props> = (props) => {
minRows={minRows}
maxRows={maxRows}
required={required}
description={description}
/>
);
};
@@ -204,6 +207,7 @@ const RenderBlocks = React.memo((props: RenderBlockProps) => {
minRows,
maxRows,
required,
description,
} = props;
const hasMaxRows = maxRows && rows.length >= maxRows;
@@ -221,6 +225,10 @@ const RenderBlocks = React.memo((props: RenderBlockProps) => {
</div>
<header className={`${baseClass}__header`}>
<h3>{label}</h3>
<FieldDescription
value={value}
description={description}
/>
</header>
<Droppable

View File

@@ -4,6 +4,14 @@
margin: base(2) 0;
min-width: base(15);
&__header {
h3 {
margin-bottom: 0;
}
margin-bottom: base(1);
}
&__error-wrap {
position: relative;
}

View File

@@ -1,5 +1,5 @@
import { Data } from '../../Form/types';
import { BlockField, Labels, Block } from '../../../../../fields/config/types';
import { BlockField, Labels, Block, Description } from '../../../../../fields/config/types';
import { FieldTypes } from '..';
import { FieldPermissions } from '../../../../../auth/types';
@@ -28,6 +28,7 @@ export type RenderBlockProps = {
showError: boolean
errorMessage: string
rows: Data[]
blocks: Block[],
blocks: Block[]
setCollapse: (id: string, collapsed: boolean) => void
description?: Description
}

View File

@@ -4,6 +4,7 @@ import withCondition from '../../withCondition';
import Error from '../../Error';
import { checkbox } from '../../../../../fields/validations';
import Check from '../../../icons/Check';
import FieldDescription from '../../FieldDescription';
import { Props } from './types';
import './index.scss';
@@ -23,6 +24,7 @@ const Checkbox: React.FC<Props> = (props) => {
readOnly,
style,
width,
description,
} = {},
} = props;
@@ -85,6 +87,10 @@ const Checkbox: React.FC<Props> = (props) => {
{label}
</span>
</button>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -7,6 +7,7 @@ import useFieldType from '../../useFieldType';
import withCondition from '../../withCondition';
import Label from '../../Label';
import Error from '../../Error';
import FieldDescription from '../../FieldDescription';
import { code } from '../../../../../fields/validations';
import { Props } from './types';
@@ -23,6 +24,7 @@ const Code: React.FC<Props> = (props) => {
style,
width,
language,
description,
} = {},
label,
minLength,
@@ -92,6 +94,10 @@ const Code: React.FC<Props> = (props) => {
pointerEvents: readOnly ? 'none' : 'auto',
}}
/>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -5,6 +5,7 @@ import withCondition from '../../withCondition';
import useFieldType from '../../useFieldType';
import Label from '../../Label';
import Error from '../../Error';
import FieldDescription from '../../FieldDescription';
import { date as dateValidation } from '../../../../../fields/validations';
import { Props } from './types';
@@ -25,6 +26,7 @@ const DateTime: React.FC<Props> = (props) => {
style,
width,
date,
description,
} = {},
} = props;
@@ -78,6 +80,10 @@ const DateTime: React.FC<Props> = (props) => {
value={value as Date}
/>
</div>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -3,6 +3,7 @@ import withCondition from '../../withCondition';
import useFieldType from '../../useFieldType';
import Label from '../../Label';
import Error from '../../Error';
import FieldDescription from '../../FieldDescription';
import { email } from '../../../../../fields/validations';
import { Props } from './types';
@@ -20,6 +21,7 @@ const Email: React.FC<Props> = (props) => {
width,
placeholder,
autoComplete,
description,
} = {},
label,
} = props;
@@ -78,6 +80,10 @@ const Email: React.FC<Props> = (props) => {
name={path}
autoComplete={autoComplete}
/>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -5,6 +5,14 @@
margin-bottom: base(2);
display: flex;
&__header {
margin-bottom: base(1);
}
&__title {
margin-bottom: 0;
}
&:hover {
.field-type-gutter__content-container {
box-shadow: #{$style-stroke-width-m} 0px 0px 0px $color-dark-gray;

View File

@@ -1,6 +1,7 @@
import React from 'react';
import RenderFields from '../../RenderFields';
import withCondition from '../../withCondition';
import FieldDescription from '../../FieldDescription';
import FieldTypeGutter from '../../FieldTypeGutter';
import { NegativeFieldGutterProvider } from '../../FieldTypeGutter/context';
import { Props } from './types';
@@ -21,6 +22,7 @@ const Group: React.FC<Props> = (props) => {
style,
width,
hideGutter,
description,
},
permissions,
} = props;
@@ -42,8 +44,16 @@ const Group: React.FC<Props> = (props) => {
{ !hideGutter && (<FieldTypeGutter />) }
<div className={`${baseClass}__content-wrapper`}>
{label && (
<h2 className={`${baseClass}__title`}>{label}</h2>
{(label || description) && (
<header className={`${baseClass}__header`}>
{label && (
<h2 className={`${baseClass}__title`}>{label}</h2>
)}
<FieldDescription
value={null}
description={description}
/>
</header>
)}
<div className={`${baseClass}__fields-wrapper`}>
<NegativeFieldGutterProvider allow={false}>

View File

@@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
import useFieldType from '../../useFieldType';
import Label from '../../Label';
import Error from '../../Error';
import FieldDescription from '../../FieldDescription';
import withCondition from '../../withCondition';
import { number } from '../../../../../fields/validations';
import { Props } from './types';
@@ -23,6 +24,7 @@ const NumberField: React.FC<Props> = (props) => {
width,
step,
placeholder,
description,
} = {},
} = props;
@@ -88,6 +90,10 @@ const NumberField: React.FC<Props> = (props) => {
name={path}
step={step}
/>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Validate } from '../../../../../fields/config/types';
import { Description, Validate } from '../../../../../fields/config/types';
export type Props = {
autoComplete?: string
@@ -10,4 +10,5 @@ export type Props = {
style?: React.CSSProperties
width?: string
label?: string
description?: Description
}

View File

@@ -64,13 +64,13 @@
cursor: default;
&__label {
color: $color-gray;
color: $color-dark-gray;
}
&--is-selected {
.radio-input__styled-radio {
&:before {
background-color: $color-gray;
background-color: $color-dark-gray;
}
}
}

View File

@@ -4,6 +4,7 @@ import useFieldType from '../../useFieldType';
import withCondition from '../../withCondition';
import Error from '../../Error';
import Label from '../../Label';
import FieldDescription from '../../FieldDescription';
import RadioInput from './RadioInput';
import { radio } from '../../../../../fields/validations';
import { optionIsObject } from '../../../../../fields/config/types';
@@ -25,6 +26,7 @@ const RadioGroup: React.FC<Props> = (props) => {
layout = 'horizontal',
style,
width,
description,
} = {},
options,
} = props;
@@ -97,6 +99,10 @@ const RadioGroup: React.FC<Props> = (props) => {
);
})}
</ul>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -9,6 +9,7 @@ import { Value } from '../../../elements/ReactSelect/types';
import useFieldType from '../../useFieldType';
import Label from '../../Label';
import Error from '../../Error';
import FieldDescription from '../../FieldDescription';
import { relationship } from '../../../../../fields/validations';
import { PaginatedDocs } from '../../../../../collections/config/types';
import { useFormProcessing } from '../../Form/context';
@@ -35,6 +36,7 @@ const Relationship: React.FC<Props> = (props) => {
readOnly,
style,
width,
description,
} = {},
} = props;
@@ -391,6 +393,10 @@ const Relationship: React.FC<Props> = (props) => {
{errorLoading}
</div>
)}
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -15,6 +15,7 @@ import hotkeys from './hotkeys';
import enablePlugins from './enablePlugins';
import defaultValue from '../../../../../fields/richText/defaultValue';
import FieldTypeGutter from '../../FieldTypeGutter';
import FieldDescription from '../../FieldDescription';
import withHTML from './plugins/withHTML';
import { Props } from './types';
import { RichTextElement, RichTextLeaf } from '../../../../../fields/config/types';
@@ -43,6 +44,7 @@ const RichText: React.FC<Props> = (props) => {
style,
width,
placeholder,
description,
hideGutter,
} = {},
} = props;
@@ -297,6 +299,10 @@ const RichText: React.FC<Props> = (props) => {
/>
</div>
</Slate>
<FieldDescription
value={value}
description={description}
/>
</div>
</div>
);

View File

@@ -4,6 +4,7 @@ import ReactSelect from '../../../elements/ReactSelect';
import useFieldType from '../../useFieldType';
import Label from '../../Label';
import Error from '../../Error';
import FieldDescription from '../../FieldDescription';
import { select } from '../../../../../fields/validations';
import { Option } from '../../../../../fields/config/types';
import { Props, Option as ReactSelectOption } from './types';
@@ -36,6 +37,7 @@ const Select: React.FC<Props> = (props) => {
readOnly,
style,
width,
description,
} = {},
} = props;
@@ -108,6 +110,10 @@ const Select: React.FC<Props> = (props) => {
options={options}
isMulti={hasMany}
/>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -5,6 +5,7 @@ import Label from '../../Label';
import Error from '../../Error';
import { text } from '../../../../../fields/validations';
import { Props } from './types';
import FieldDescription from '../../FieldDescription';
import './index.scss';
@@ -20,6 +21,7 @@ const Text: React.FC<Props> = (props) => {
readOnly,
style,
width,
description,
} = {},
} = props;
@@ -63,7 +65,7 @@ const Text: React.FC<Props> = (props) => {
required={required}
/>
<input
value={value || ''}
value={value}
onChange={setValue}
disabled={readOnly}
placeholder={placeholder}
@@ -71,6 +73,10 @@ const Text: React.FC<Props> = (props) => {
id={path}
name={path}
/>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -3,6 +3,7 @@ import useFieldType from '../../useFieldType';
import withCondition from '../../withCondition';
import Label from '../../Label';
import Error from '../../Error';
import FieldDescription from '../../FieldDescription';
import { textarea } from '../../../../../fields/validations';
import { Props } from './types';
@@ -20,6 +21,7 @@ const Textarea: React.FC<Props> = (props) => {
width,
placeholder,
rows,
description,
} = {},
label,
minLength,
@@ -77,6 +79,10 @@ const Textarea: React.FC<Props> = (props) => {
name={path}
rows={rows}
/>
<FieldDescription
value={value}
description={description}
/>
</div>
);
};

View File

@@ -11,9 +11,12 @@
}
&__header {
display: flex;
margin-bottom: $baseline;
div {
display: flex;
}
h1 {
margin: 0 auto 0 0;
}
@@ -22,4 +25,8 @@
margin: 0 0 0 $baseline;
}
}
&__sub-header {
margin-top: base(.25);
}
}

View File

@@ -17,6 +17,11 @@ const baseClass = 'add-upload-modal';
const AddUploadModal: React.FC<Props> = (props) => {
const {
collection,
collection: {
admin: {
description,
} = {},
} = {},
slug,
fieldTypes,
setValue,
@@ -50,19 +55,24 @@ const AddUploadModal: React.FC<Props> = (props) => {
disableSuccessStatus
>
<header className={`${baseClass}__header`}>
<h1>
New
{' '}
{collection.labels.singular}
</h1>
<FormSubmit>Save</FormSubmit>
<Button
icon="x"
round
buttonStyle="icon-label"
iconStyle="with-border"
onClick={closeAll}
/>
<div>
<h1>
New
{' '}
{collection.labels.singular}
</h1>
<FormSubmit>Save</FormSubmit>
<Button
icon="x"
round
buttonStyle="icon-label"
iconStyle="with-border"
onClick={closeAll}
/>
</div>
{description && (
<div className={`${baseClass}__sub-header`}>{description}</div>
)}
</header>
<Upload
collection={collection}

View File

@@ -11,9 +11,12 @@
}
&__header {
display: flex;
margin-bottom: $baseline;
div {
display: flex;
}
h1 {
margin: 0 auto 0 0;
}
@@ -22,4 +25,8 @@
margin: 0 0 0 $baseline;
}
}
&__sub-header {
margin-top: base(.25);
}
}

View File

@@ -20,6 +20,9 @@ const SelectExistingUploadModal: React.FC<Props> = (props) => {
collection,
collection: {
slug: collectionSlug,
admin: {
description,
} = {},
} = {},
slug: modalSlug,
} = props;
@@ -68,19 +71,24 @@ const SelectExistingUploadModal: React.FC<Props> = (props) => {
{isOpen && (
<MinimalTemplate width="wide">
<header className={`${baseClass}__header`}>
<h1>
{' '}
Select existing
{' '}
{collection.labels.singular}
</h1>
<Button
icon="x"
round
buttonStyle="icon-label"
iconStyle="with-border"
onClick={closeAll}
/>
<div>
<h1>
{' '}
Select existing
{' '}
{collection.labels.singular}
</h1>
<Button
icon="x"
round
buttonStyle="icon-label"
iconStyle="with-border"
onClick={closeAll}
/>
</div>
{description && (
<div className={`${baseClass}__sub-header`}>{description}</div>
)}
</header>
<ListControls
handleChange={setListControls}

View File

@@ -13,6 +13,7 @@ import SelectExistingModal from './SelectExisting';
import { Props } from './types';
import './index.scss';
import FieldDescription from '../../FieldDescription';
const baseClass = 'upload';
@@ -30,6 +31,7 @@ const Upload: React.FC<Props> = (props) => {
readOnly,
style,
width,
description,
} = {},
label,
validate = upload,
@@ -159,6 +161,10 @@ const Upload: React.FC<Props> = (props) => {
addModalSlug,
}}
/>
<FieldDescription
value={value}
description={description}
/>
</React.Fragment>
)}
</div>

View File

@@ -13,6 +13,7 @@ import LeaveWithoutSaving from '../../modals/LeaveWithoutSaving';
import { Props } from './types';
import './index.scss';
import ViewDescription from '../../elements/ViewDescription';
const baseClass = 'global-edit';
@@ -26,6 +27,9 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
fields,
preview,
label,
admin: {
description,
} = {},
} = global;
const hasSavePermission = permissions?.update?.permission;
@@ -55,6 +59,11 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
{' '}
{label}
</h1>
{description && (
<div className={`${baseClass}__sub-header`}>
<ViewDescription description={description} />
</div>
)}
</header>
<RenderFields
operation="update"

View File

@@ -19,7 +19,13 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
margin-bottom: base(1);
}
&__sub-header {
margin-top: base(.25);
}
&__collection-actions {

View File

@@ -12,6 +12,7 @@ import Meta from '../../../utilities/Meta';
import { Props } from './types';
import './index.scss';
import ViewDescription from '../../../elements/ViewDescription';
const baseClass = 'collection-list';
@@ -25,6 +26,9 @@ const DefaultList: React.FC<Props> = (props) => {
singular: singularLabel,
plural: pluralLabel,
},
admin: {
description,
} = {},
},
data,
newDocumentURL,
@@ -52,6 +56,11 @@ const DefaultList: React.FC<Props> = (props) => {
Create New
</Pill>
)}
{description && (
<div className={`${baseClass}__sub-header`}>
<ViewDescription description={description} />
</div>
)}
</header>
<ListControls
handleChange={setListControls}

View File

@@ -29,6 +29,11 @@
}
}
&__sub-header {
flex-basis: 100%;
margin-top: base(.25);
}
.table {
table {
width: 100%;

View File

@@ -35,7 +35,7 @@ const ListView: React.FC<ListIndexProps> = (props) => {
const { permissions } = useAuth();
const location = useLocation();
const { setStepNav } = useStepNav();
const { getPreference, setPreference } = usePreferences();
const { getPreference } = usePreferences();
const [fields] = useState(() => formatFields(collection));
const [listControls, setListControls] = useState<ListControls>({});