diff --git a/examples/getstarted/api/address/content-types/address/schema.json b/examples/getstarted/api/address/content-types/address/schema.json index 1121090c1d..94a41e2ca1 100755 --- a/examples/getstarted/api/address/content-types/address/schema.json +++ b/examples/getstarted/api/address/content-types/address/schema.json @@ -115,30 +115,6 @@ "component": "blog.test-como", "required": false, "min": 2 - }, - "temp_one_to_one": { - "type": "relation", - "relation": "oneToOne", - "target": "api::temp.temp", - "inversedBy": "one_to_one" - }, - "temp": { - "type": "relation", - "relation": "manyToOne", - "target": "api::temp.temp", - "inversedBy": "one_to_many" - }, - "temps": { - "type": "relation", - "relation": "oneToMany", - "target": "api::temp.temp", - "mappedBy": "many_to_one" - }, - "many_to_many_temp": { - "type": "relation", - "relation": "manyToMany", - "target": "api::temp.temp", - "inversedBy": "many_to_many" } } } diff --git a/examples/getstarted/api/temp/content-types/temp/schema.json b/examples/getstarted/api/temp/content-types/temp/schema.json index 41e854efd1..16d64a891f 100644 --- a/examples/getstarted/api/temp/content-types/temp/schema.json +++ b/examples/getstarted/api/temp/content-types/temp/schema.json @@ -9,7 +9,7 @@ "description": "" }, "options": { - "draftAndPublish": true + "draftAndPublish": false }, "pluginOptions": {}, "attributes": { @@ -17,7 +17,7 @@ "type": "string" }, "long": { - "type": "string" + "type": "text" }, "email": { "type": "email" @@ -28,7 +28,7 @@ "password": { "type": "password" }, - "number": { + "number_int": { "type": "integer" }, "enum": { @@ -77,39 +77,33 @@ "uid": { "type": "uid" }, - "one_way": { - "type": "relation", - "relation": "oneToOne", - "target": "api::address.address" + "number_big": { + "type": "biginteger" }, - "one_to_one": { - "type": "relation", - "relation": "oneToOne", - "target": "api::address.address", - "inversedBy": "temp_one_to_one" + "number_dec": { + "type": "decimal" }, - "one_to_many": { - "type": "relation", - "relation": "oneToMany", - "target": "api::address.address", - "mappedBy": "temp" + "number_float": { + "type": "float" }, - "many_to_one": { - "type": "relation", - "relation": "manyToOne", - "target": "api::address.address", - "inversedBy": "temps" + "enum_req_def": { + "type": "enumeration", + "enum": [ + "un", + "deux", + "trois" + ], + "required": true, + "default": "un" }, - "many_to_many": { - "type": "relation", - "relation": "manyToMany", - "target": "api::address.address", - "inversedBy": "many_to_many_temp" - }, - "many_way": { - "type": "relation", - "relation": "oneToMany", - "target": "api::address.address" + "enum_req": { + "type": "enumeration", + "enum": [ + "un", + "deux", + "trois" + ], + "required": true } } } diff --git a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js index 8d6fc45438..fcba10cd12 100644 --- a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +++ b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js @@ -93,6 +93,7 @@ const EditViewDataManagerProvider = ({ const errorsInForm = Object.keys(formErrors); // TODO check if working with DZ, components... + // TODO use querySelector querySelectorAll('[data-strapi-field-error]') if (errorsInForm.length > 0) { const firstError = errorsInForm[0]; const el = document.getElementById(firstError); diff --git a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js index c29bd33307..da8b24aeb9 100644 --- a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +++ b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js @@ -1,4 +1,6 @@ -import { get, isArray, isObject } from 'lodash'; +import get from 'lodash/get'; +import isArray from 'lodash/isArray'; +import isObject from 'lodash/isObject'; /* eslint-disable indent */ @@ -23,13 +25,23 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => { } break; - case 'date': - cleanedData = - value && value._isAMomentObject === true ? value.format('YYYY-MM-DD') : value; - break; - case 'datetime': - cleanedData = value && value._isAMomentObject === true ? value.toISOString() : value; + // TODO + // case 'date': + // cleanedData = + // value && value._isAMomentObject === true ? value.format('YYYY-MM-DD') : value; + // break; + // case 'datetime': + // cleanedData = value && value._isAMomentObject === true ? value.toISOString() : value; + // break; + case 'time': { + cleanedData = value; + + if (value && value.split(':').length < 3) { + cleanedData = `${value}:00`; + } + break; + } case 'media': if (getOtherInfos(schema, [current, 'multiple']) === true) { cleanedData = value ? value.filter(file => !(file instanceof File)) : null; diff --git a/packages/core/admin/admin/src/content-manager/components/Inputs/GenericInput.js b/packages/core/admin/admin/src/content-manager/components/Inputs/GenericInput.js index 30cf68ab68..c87dcdbaea 100644 --- a/packages/core/admin/admin/src/content-manager/components/Inputs/GenericInput.js +++ b/packages/core/admin/admin/src/content-manager/components/Inputs/GenericInput.js @@ -1,16 +1,26 @@ /** * - * Input + * GenericInput * This is a temp file move it to the helper plugin when ready */ -import React from 'react'; -import { useIntl } from 'react-intl'; -import { ToggleInput } from '@strapi/parts/ToggleInput'; -import { TextInput } from '@strapi/parts/TextInput'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; +import { useIntl } from 'react-intl'; +import cloneDeep from 'lodash/cloneDeep'; +import { formatISO } from 'date-fns'; +import { DatePicker } from '@strapi/parts/DatePicker'; +import { NumberInput } from '@strapi/parts/NumberInput'; +import { Select, Option } from '@strapi/parts/Select'; +import { Textarea } from '@strapi/parts/Textarea'; +import { TextInput } from '@strapi/parts/TextInput'; +import { TimePicker } from '@strapi/parts/TimePicker'; +import { ToggleInput } from '@strapi/parts/ToggleInput'; +import Hide from '@strapi/icons/Hide'; +import Show from '@strapi/icons/Show'; -const Input = ({ +const GenericInput = ({ + autoComplete, customInputs, description, disabled, @@ -19,12 +29,15 @@ const Input = ({ error, name, onChange, + options, placeholder, + step, type, value, ...rest }) => { const { formatMessage } = useIntl(); + const [showPassword, setShowPassword] = useState(false); const CustomInput = customInputs ? customInputs[type] : null; @@ -46,10 +59,12 @@ const Input = ({ ); } - const label = formatMessage( - { id: intlLabel.id, defaultMessage: intlLabel.defaultMessage }, - { ...intlLabel.values } - ); + const label = intlLabel.id + ? formatMessage( + { id: intlLabel.id, defaultMessage: intlLabel.defaultMessage }, + { ...intlLabel.values } + ) + : name; const hint = description ? formatMessage( @@ -58,30 +73,6 @@ const Input = ({ ) : ''; - if (type === 'bool') { - return ( - { - onChange({ target: { name, value: e.target.checked } }); - }} - /> - ); - } - const formattedPlaceholder = placeholder ? formatMessage( { id: placeholder.id, defaultMessage: placeholder.defaultMessage }, @@ -91,34 +82,228 @@ const Input = ({ const errorMessage = error ? formatMessage({ id: error, defaultMessage: error }) : ''; - return ( - - ); + switch (type) { + case 'bool': { + return ( + { + onChange({ target: { name, value: e.target.checked } }); + }} + /> + ); + } + case 'date': { + return ( + { + const formattedDate = formatISO(cloneDeep(date), { representation: 'date' }); + + onChange({ target: { name, value: formattedDate, type } }); + }} + onClear={() => onChange({ target: { name, value: '', type } })} + placeholder={formattedPlaceholder} + selectedDate={value ? new Date(value) : null} + selectedDateLabel={formattedDate => `Date picker, current is ${formattedDate}`} + /> + ); + } + case 'number': { + return ( + { + onChange({ target: { name, value, type } }); + }} + placeholder={formattedPlaceholder} + step={step} + value={value || undefined} + /> + ); + } + case 'email': + case 'text': + case 'string': { + return ( + + ); + } + case 'password': { + return ( + { + setShowPassword(prev => !prev); + }} + style={{ + border: 'none', + padding: 0, + background: 'transparent', + }} + type="button" + > + {showPassword ? : } + + } + label={label} + labelAction={labelAction} + id={name} + hint={hint} + name={name} + onChange={onChange} + placeholder={formattedPlaceholder} + type={showPassword ? 'text' : 'password'} + value={value || ''} + /> + ); + } + case 'select': { + return ( + + ); + } + case 'textarea': { + return ( + + ); + } + case 'time': { + let time = value; + + // The backend send a value which has the following format: '00:45:00.000' + // or the time picker only supports hours & minutes so we need to mutate the value + if (value && value.split(':').length > 2) { + time = time.split(':'); + time.pop(); + time = time.join(':'); + } + + return ( + { + onChange({ target: { name, value: `${time}`, type } }); + }} + onClear={() => { + onChange({ target: { name, value: null, type } }); + }} + placeholder={formattedPlaceholder} + step={step} + value={time} + /> + ); + } + default: { + return
{type} is not supported
; + } + } }; -Input.defaultProps = { +GenericInput.defaultProps = { + autoComplete: undefined, customInputs: null, description: null, disabled: false, error: '', labelAction: undefined, placeholder: null, + options: [], + step: 1, value: '', }; -Input.propTypes = { +GenericInput.propTypes = { + autoComplete: PropTypes.string, customInputs: PropTypes.object, description: PropTypes.shape({ id: PropTypes.string.isRequired, @@ -135,13 +320,28 @@ Input.propTypes = { labelAction: PropTypes.element, name: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, + options: PropTypes.arrayOf( + PropTypes.shape({ + metadatas: PropTypes.shape({ + intlLabel: PropTypes.shape({ + id: PropTypes.string.isRequired, + defaultMessage: PropTypes.string.isRequired, + }).isRequired, + disabled: PropTypes.bool, + hidden: PropTypes.bool, + }).isRequired, + key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + }).isRequired + ), placeholder: PropTypes.shape({ id: PropTypes.string.isRequired, defaultMessage: PropTypes.string.isRequired, values: PropTypes.object, }), + step: PropTypes.number, type: PropTypes.string.isRequired, value: PropTypes.any, }; -export default Input; +export default GenericInput; diff --git a/packages/core/admin/admin/src/content-manager/components/Inputs/index.js b/packages/core/admin/admin/src/content-manager/components/Inputs/index.js index ef595bba38..2d8e1d1732 100644 --- a/packages/core/admin/admin/src/content-manager/components/Inputs/index.js +++ b/packages/core/admin/admin/src/content-manager/components/Inputs/index.js @@ -166,7 +166,7 @@ function Inputs({ isRequired, ]); - const { label, description, visible } = metadatas; + const { label, description, placeholder, visible } = metadatas; if (visible === false) { return null; @@ -218,8 +218,10 @@ function Inputs({ // wysiwyg: WysiwygWithErrors, // uid: InputUID, // ...fields, + json: () =>
TODO json
, media: () =>
TODO media
, uid: () =>
TODO uid
, + wysiwyg: () =>
TODO wysiwyg
, }} multiple={fieldSchema.multiple || false} attribute={fieldSchema} @@ -227,9 +229,10 @@ function Inputs({ onBlur={onBlur} onChange={onChange} options={options} + placeholder={placeholder ? { id: placeholder, defaultMessage: placeholder } : null} step={step} type={inputType} - validations={validations} + // validations={validations} value={inputValue} withDefaultValue={false} /> diff --git a/packages/core/admin/admin/src/content-manager/components/Inputs/utils/generateOptions.js b/packages/core/admin/admin/src/content-manager/components/Inputs/utils/generateOptions.js index 5f55781d2c..3ba48d8f65 100644 --- a/packages/core/admin/admin/src/content-manager/components/Inputs/utils/generateOptions.js +++ b/packages/core/admin/admin/src/content-manager/components/Inputs/utils/generateOptions.js @@ -1,19 +1,32 @@ -import React from 'react'; -import { FormattedMessage } from 'react-intl'; - -const generateOptions = (options, isRequired = false) => [ - - {msg => ( - - )} - , - ...options.map(v => ( - - )), -]; +const generateOptions = (options, isRequired = false) => { + return [ + { + metadatas: { + intlLabel: { + id: 'components.InputSelect.option.placeholder', + defaultMessage: 'Choose here', + }, + disabled: isRequired, + hidden: isRequired, + }, + key: '__enum_option_null', + value: '', + }, + ...options.map(option => { + return { + metadatas: { + intlLabel: { + id: option, + defaultMessage: option, + }, + hidden: false, + disabled: false, + }, + key: option, + value: option, + }; + }), + ]; +}; export default generateOptions; diff --git a/packages/core/admin/admin/src/content-manager/pages/EditView/index.js b/packages/core/admin/admin/src/content-manager/pages/EditView/index.js index c3b5d73320..c0b61545e0 100644 --- a/packages/core/admin/admin/src/content-manager/pages/EditView/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/EditView/index.js @@ -156,7 +156,7 @@ const EditView = ({ {grid.map( ({ fieldSchema, labelAction, metadatas, name, size }) => { return ( - + { const { formatMessage } = useIntl(); diff --git a/packages/plugins/i18n/admin/src/components/CMEditViewInjectedComponents/CMEditViewLocalePicker/index.js b/packages/plugins/i18n/admin/src/components/CMEditViewInjectedComponents/CMEditViewLocalePicker/index.js index 947e1e89c9..5a5bd819d4 100644 --- a/packages/plugins/i18n/admin/src/components/CMEditViewInjectedComponents/CMEditViewLocalePicker/index.js +++ b/packages/plugins/i18n/admin/src/components/CMEditViewInjectedComponents/CMEditViewLocalePicker/index.js @@ -110,6 +110,7 @@ const CMEditViewLocalePicker = ({