mirror of
https://github.com/strapi/strapi.git
synced 2025-09-26 00:39:49 +00:00
Merge pull request #11545 from strapi/accordion-error-state
CM/ Accordion Error state
This commit is contained in:
commit
baae2f0252
@ -23,14 +23,15 @@ const IconWrapper = styled.span`
|
||||
}
|
||||
`;
|
||||
|
||||
const ComponentInitializer = ({ isReadOnly, onClick }) => {
|
||||
const ComponentInitializer = ({ error, isReadOnly, onClick }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
as="button"
|
||||
background="neutral100"
|
||||
borderColor="neutral200"
|
||||
borderColor={error ? 'danger600' : 'neutral200'}
|
||||
disabled={isReadOnly}
|
||||
hasRadius
|
||||
onClick={onClick}
|
||||
@ -54,14 +55,32 @@ const ComponentInitializer = ({ isReadOnly, onClick }) => {
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Box>
|
||||
{error?.id && (
|
||||
<Text textColor="danger600" small>
|
||||
{formatMessage(
|
||||
{
|
||||
id: error.id,
|
||||
defaultMessage: error.defaultMessage,
|
||||
},
|
||||
error.values
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ComponentInitializer.defaultProps = {
|
||||
error: undefined,
|
||||
isReadOnly: false,
|
||||
};
|
||||
|
||||
ComponentInitializer.propTypes = {
|
||||
error: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
isReadOnly: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -22,15 +22,18 @@ const IconButtonCustom = styled(IconButton)`
|
||||
`;
|
||||
|
||||
const StyledBox = styled(Box)`
|
||||
> div {
|
||||
> div:not(:first-of-type) {
|
||||
overflow: visible;
|
||||
}
|
||||
> div:first-child {
|
||||
box-shadow: ${({ theme }) => theme.shadows.tableShadow};
|
||||
}
|
||||
`;
|
||||
|
||||
const AccordionContentRadius = styled(Box)`
|
||||
border-radius: 0 0 ${({ theme }) => theme.spaces[1]} ${({ theme }) => theme.spaces[1]};
|
||||
`;
|
||||
|
||||
const Component = ({
|
||||
componentUid,
|
||||
formErrors,
|
||||
index,
|
||||
isOpen,
|
||||
isFieldAllowed,
|
||||
@ -74,11 +77,32 @@ const Component = ({
|
||||
{ name: friendlyName }
|
||||
);
|
||||
|
||||
const formErrorsKeys = Object.keys(formErrors);
|
||||
|
||||
const fieldsErrors = formErrorsKeys.filter(errorKey => {
|
||||
const errorKeysArray = errorKey.split('.');
|
||||
|
||||
if (`${errorKeysArray[0]}.${errorKeysArray[1]}` === `${name}.${index}`) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
let errorMessage;
|
||||
|
||||
if (fieldsErrors.length > 0) {
|
||||
errorMessage = formatMessage({
|
||||
id: getTrad('components.DynamicZone.error-message'),
|
||||
defaultMessage: 'The component contains error(s)',
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Rectangle />
|
||||
<StyledBox shadow="tableShadow" hasRadius>
|
||||
<Accordion expanded={isOpen} toggle={() => onToggle(index)} size="S">
|
||||
<StyledBox hasRadius>
|
||||
<Accordion expanded={isOpen} toggle={() => onToggle(index)} size="S" error={errorMessage}>
|
||||
<AccordionToggle
|
||||
startIcon={<FontAwesomeIcon icon={icon} />}
|
||||
action={
|
||||
@ -113,6 +137,7 @@ const Component = ({
|
||||
togglePosition="left"
|
||||
/>
|
||||
<AccordionContent>
|
||||
<AccordionContentRadius background="neutral0">
|
||||
<FocusTrap onEscape={() => onToggle(index)}>
|
||||
<FieldComponent
|
||||
componentUid={componentUid}
|
||||
@ -121,6 +146,7 @@ const Component = ({
|
||||
isFromDynamicZone
|
||||
/>
|
||||
</FocusTrap>
|
||||
</AccordionContentRadius>
|
||||
</AccordionContent>
|
||||
</Accordion>
|
||||
</StyledBox>
|
||||
@ -130,6 +156,7 @@ const Component = ({
|
||||
|
||||
Component.propTypes = {
|
||||
componentUid: PropTypes.string.isRequired,
|
||||
formErrors: PropTypes.object.isRequired,
|
||||
index: PropTypes.number.isRequired,
|
||||
isFieldAllowed: PropTypes.bool.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
|
@ -188,6 +188,7 @@ const DynamicZone = ({
|
||||
return (
|
||||
<Component
|
||||
componentUid={componentUid}
|
||||
formErrors={formErrors}
|
||||
key={index}
|
||||
index={index}
|
||||
isOpen={isOpen}
|
||||
|
@ -300,8 +300,8 @@ const EditViewDataManagerProvider = ({
|
||||
onPut(formData, trackerProperty);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('ValidationError');
|
||||
console.error(err);
|
||||
console.log('ValidationError');
|
||||
console.log(err);
|
||||
|
||||
errors = getYupInnerErrors(err);
|
||||
|
||||
|
@ -20,7 +20,7 @@ const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, nam
|
||||
|
||||
return (
|
||||
<Box
|
||||
background={isFromDynamicZone ? 'neutral0' : 'neutral100'}
|
||||
background={isFromDynamicZone ? '' : 'neutral100'}
|
||||
paddingLeft={6}
|
||||
paddingRight={6}
|
||||
paddingTop={6}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import React, { Children, cloneElement } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
import styled from 'styled-components';
|
||||
import { Box } from '@strapi/design-system/Box';
|
||||
import { Text } from '@strapi/design-system/Text';
|
||||
import { Typography } from '@strapi/design-system/Typography';
|
||||
import { Flex } from '@strapi/design-system/Flex';
|
||||
import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
|
||||
|
||||
@ -61,7 +63,12 @@ const LabelAction = styled(Box)`
|
||||
}
|
||||
`;
|
||||
|
||||
const AccordionGroupCustom = ({ children, footer, label, labelAction }) => {
|
||||
const AccordionGroupCustom = ({ children, footer, label, labelAction, error }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const childrenArray = Children.toArray(children).map(child => {
|
||||
return cloneElement(child, { hasErrorMessage: false });
|
||||
});
|
||||
|
||||
return (
|
||||
<KeyboardNavigable attributeName="data-strapi-accordion-toggle">
|
||||
{label && (
|
||||
@ -72,13 +79,21 @@ const AccordionGroupCustom = ({ children, footer, label, labelAction }) => {
|
||||
{labelAction && <LabelAction paddingLeft={1}>{labelAction}</LabelAction>}
|
||||
</Flex>
|
||||
)}
|
||||
<EnhancedGroup footer={footer}>{children}</EnhancedGroup>
|
||||
<EnhancedGroup footer={footer}>{childrenArray}</EnhancedGroup>
|
||||
{footer && <AccordionFooter>{footer}</AccordionFooter>}
|
||||
{error && (
|
||||
<Box paddingTop={1}>
|
||||
<Typography variant="pi" textColor="danger600">
|
||||
{formatMessage({ id: error.id, defaultMessage: error.defaultMessage }, error.values)}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</KeyboardNavigable>
|
||||
);
|
||||
};
|
||||
|
||||
AccordionGroupCustom.defaultProps = {
|
||||
error: undefined,
|
||||
footer: null,
|
||||
label: null,
|
||||
labelAction: undefined,
|
||||
@ -86,6 +101,11 @@ AccordionGroupCustom.defaultProps = {
|
||||
|
||||
AccordionGroupCustom.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
error: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
footer: PropTypes.node,
|
||||
label: PropTypes.string,
|
||||
labelAction: PropTypes.node,
|
||||
|
@ -5,7 +5,6 @@ import { useDrag, useDrop } from 'react-dnd';
|
||||
import { getEmptyImage } from 'react-dnd-html5-backend';
|
||||
import { useIntl } from 'react-intl';
|
||||
import toString from 'lodash/toString';
|
||||
import styled from 'styled-components';
|
||||
import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system/Accordion';
|
||||
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||
import { Stack } from '@strapi/design-system/Stack';
|
||||
@ -21,14 +20,6 @@ import DraggingSibling from './DraggingSibling';
|
||||
import { CustomIconButton, DragHandleWrapper } from './IconButtonCustoms';
|
||||
import { connect, select } from './utils';
|
||||
|
||||
const StyledBox = styled(Box)`
|
||||
> div {
|
||||
> div:not(:first-of-type) {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/* eslint-disable react/no-array-index-key */
|
||||
|
||||
// Issues:
|
||||
@ -37,11 +28,9 @@ const StyledBox = styled(Box)`
|
||||
|
||||
const DraggedItem = ({
|
||||
componentFieldName,
|
||||
// FIXME: errors
|
||||
// doesPreviousFieldContainErrorsAndIsOpen,
|
||||
// hasErrors,
|
||||
// hasMinError,
|
||||
// isFirst,
|
||||
// Errors are retrieved from the AccordionGroupCustom cloneElement
|
||||
hasErrorMessage,
|
||||
hasErrors,
|
||||
isDraggingSibling,
|
||||
isOpen,
|
||||
isReadOnly,
|
||||
@ -171,16 +160,24 @@ const DraggedItem = ({
|
||||
};
|
||||
|
||||
const accordionTitle = toString(displayedValue);
|
||||
const accordionHasError = hasErrors ? 'error' : undefined;
|
||||
|
||||
return (
|
||||
<StyledBox ref={refs ? refs.dropRef : null}>
|
||||
<Box ref={refs ? refs.dropRef : null}>
|
||||
{isDragging && <Preview />}
|
||||
{!isDragging && isDraggingSibling && (
|
||||
<DraggingSibling displayedValue={accordionTitle} componentFieldName={componentFieldName} />
|
||||
)}
|
||||
|
||||
{!isDragging && !isDraggingSibling && (
|
||||
<Accordion expanded={isOpen} toggle={onClickToggle} id={componentFieldName} size="S">
|
||||
<Accordion
|
||||
error={accordionHasError}
|
||||
hasErrorMessage={hasErrorMessage}
|
||||
expanded={isOpen}
|
||||
toggle={onClickToggle}
|
||||
id={componentFieldName}
|
||||
size="S"
|
||||
>
|
||||
<AccordionToggle
|
||||
action={
|
||||
isReadOnly ? null : (
|
||||
@ -264,15 +261,11 @@ const DraggedItem = ({
|
||||
</AccordionContent>
|
||||
</Accordion>
|
||||
)}
|
||||
</StyledBox>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
DraggedItem.defaultProps = {
|
||||
// doesPreviousFieldContainErrorsAndIsOpen: false,
|
||||
// hasErrors: false,
|
||||
// hasMinError: false,
|
||||
// isFirst: false,
|
||||
isDraggingSibling: false,
|
||||
isOpen: false,
|
||||
setIsDraggingSiblig: () => {},
|
||||
@ -281,10 +274,8 @@ DraggedItem.defaultProps = {
|
||||
|
||||
DraggedItem.propTypes = {
|
||||
componentFieldName: PropTypes.string.isRequired,
|
||||
// doesPreviousFieldContainErrorsAndIsOpen: PropTypes.bool,
|
||||
// hasErrors: PropTypes.bool,
|
||||
// hasMinError: PropTypes.bool,
|
||||
// isFirst: PropTypes.bool,
|
||||
hasErrorMessage: PropTypes.bool.isRequired,
|
||||
hasErrors: PropTypes.bool.isRequired,
|
||||
isDraggingSibling: PropTypes.bool,
|
||||
isOpen: PropTypes.bool,
|
||||
isReadOnly: PropTypes.bool.isRequired,
|
||||
|
@ -43,7 +43,7 @@ const RepeatableComponent = ({
|
||||
isNested,
|
||||
isReadOnly,
|
||||
max,
|
||||
// min,
|
||||
min,
|
||||
name,
|
||||
}) => {
|
||||
const toggleNotification = useNotification();
|
||||
@ -75,11 +75,10 @@ const RepeatableComponent = ({
|
||||
const toggleCollapses = () => {
|
||||
setCollapseToOpen('');
|
||||
};
|
||||
// TODO
|
||||
// const missingComponentsValue = min - componentValueLength;
|
||||
const errorsArray = componentErrorKeys.map(key => get(formErrors, [key, 'id'], ''));
|
||||
|
||||
const hasMinError = get(errorsArray, [0], '').includes('min');
|
||||
const missingComponentsValue = min - componentValueLength;
|
||||
|
||||
const hasMinError = get(formErrors, name, { id: '' }).id.includes('min');
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (!isReadOnly) {
|
||||
@ -108,13 +107,38 @@ const RepeatableComponent = ({
|
||||
toggleNotification,
|
||||
]);
|
||||
|
||||
let errorMessage = formErrors[name];
|
||||
|
||||
if (hasMinError) {
|
||||
errorMessage = {
|
||||
id: getTrad('components.DynamicZone.missing-components'),
|
||||
defaultMessage:
|
||||
'There {number, plural, =0 {are # missing components} one {is # missing component} other {are # missing components}}',
|
||||
values: { number: missingComponentsValue },
|
||||
};
|
||||
}
|
||||
|
||||
if (componentValueLength === 0) {
|
||||
return <ComponentInitializer isReadOnly={isReadOnly} onClick={handleClick} />;
|
||||
return (
|
||||
<ComponentInitializer error={errorMessage} isReadOnly={isReadOnly} onClick={handleClick} />
|
||||
);
|
||||
}
|
||||
|
||||
const doesRepComponentHasChildError = componentErrorKeys.some(
|
||||
error => error.split('.').length > 1
|
||||
);
|
||||
|
||||
if (doesRepComponentHasChildError && !hasMinError) {
|
||||
errorMessage = {
|
||||
id: getTrad('components.RepeatableComponent.error-message'),
|
||||
defaultMessage: 'The component(s) contain error(s)',
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Box hasRadius background="neutral0" shadow="tableShadow" ref={drop}>
|
||||
<Box hasRadius ref={drop}>
|
||||
<AccordionGroupCustom
|
||||
error={errorMessage}
|
||||
footer={
|
||||
<Flex justifyContent="center" height="48px" background="neutral0" hasRadius>
|
||||
<TextButtonCustom disabled={isReadOnly} onClick={handleClick} startIcon={<Plus />}>
|
||||
@ -130,24 +154,15 @@ const RepeatableComponent = ({
|
||||
const key = data.__temp_key__;
|
||||
const isOpen = collapseToOpen === key;
|
||||
const componentFieldName = `${name}.${index}`;
|
||||
const previousComponentTempKey = get(componentValue, [index - 1, '__temp_key__']);
|
||||
const doesPreviousFieldContainErrorsAndIsOpen =
|
||||
componentErrorKeys.includes(`${name}.${index - 1}`) &&
|
||||
index !== 0 &&
|
||||
collapseToOpen === previousComponentTempKey;
|
||||
|
||||
const hasErrors = componentErrorKeys.includes(componentFieldName);
|
||||
|
||||
return (
|
||||
<DraggedItem
|
||||
componentFieldName={componentFieldName}
|
||||
componentUid={componentUid}
|
||||
// TODO
|
||||
doesPreviousFieldContainErrorsAndIsOpen={doesPreviousFieldContainErrorsAndIsOpen}
|
||||
hasErrors={hasErrors}
|
||||
hasMinError={hasMinError}
|
||||
isDraggingSibling={isDraggingSibling}
|
||||
isFirst={index === 0}
|
||||
isOpen={isOpen}
|
||||
isReadOnly={isReadOnly}
|
||||
key={key}
|
||||
@ -167,139 +182,7 @@ const RepeatableComponent = ({
|
||||
})}
|
||||
</AccordionGroupCustom>
|
||||
</Box>
|
||||
// <Box hasRadius borderColor="neutral200">
|
||||
// <Box ref={drop}>
|
||||
// {componentValue.map((data, index) => {
|
||||
// const key = data.__temp_key__;
|
||||
// const isOpen = collapseToOpen === key;
|
||||
// const componentFieldName = `${name}.${index}`;
|
||||
// const previousComponentTempKey = get(componentValue, [index - 1, '__temp_key__']);
|
||||
// const doesPreviousFieldContainErrorsAndIsOpen =
|
||||
// componentErrorKeys.includes(`${name}.${index - 1}`) &&
|
||||
// index !== 0 &&
|
||||
// collapseToOpen === previousComponentTempKey;
|
||||
|
||||
// const hasErrors = componentErrorKeys.includes(componentFieldName);
|
||||
|
||||
// return (
|
||||
// <DraggedItem
|
||||
// componentFieldName={componentFieldName}
|
||||
// componentUid={componentUid}
|
||||
// // TODO
|
||||
// doesPreviousFieldContainErrorsAndIsOpen={doesPreviousFieldContainErrorsAndIsOpen}
|
||||
// hasErrors={hasErrors}
|
||||
// hasMinError={hasMinError}
|
||||
// isFirst={index === 0}
|
||||
// isOdd={index % 2 === 1}
|
||||
// isOpen={isOpen}
|
||||
// isReadOnly={isReadOnly}
|
||||
// key={key}
|
||||
// onClickToggle={() => {
|
||||
// if (isOpen) {
|
||||
// setCollapseToOpen('');
|
||||
// } else {
|
||||
// setCollapseToOpen(key);
|
||||
// }
|
||||
// }}
|
||||
// parentName={name}
|
||||
// schema={componentLayoutData}
|
||||
// toggleCollapses={toggleCollapses}
|
||||
// />
|
||||
// );
|
||||
// })}
|
||||
// </Box>
|
||||
// <Button
|
||||
// // TODO
|
||||
// // hasMinError={hasMinError}
|
||||
// disabled={isReadOnly}
|
||||
// // TODO
|
||||
// // doesPreviousFieldContainErrorsAndIsClosed={
|
||||
// // componentValueLength > 0 &&
|
||||
// // componentErrorKeys.includes(`${name}.${componentValueLength - 1}`) &&
|
||||
// // componentValue[componentValueLength - 1].__temp_key__ !== collapseToOpen
|
||||
// // }
|
||||
// onClick={handleClick}
|
||||
// />
|
||||
// </Box>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <div>
|
||||
// {componentValueLength === 0 && (
|
||||
// <EmptyComponent hasMinError={hasMinError}>
|
||||
// <FormattedMessage id={getTrad('components.empty-repeatable')}>
|
||||
// {msg => <p>{msg}</p>}
|
||||
// </FormattedMessage>
|
||||
// </EmptyComponent>
|
||||
// )}
|
||||
// <div ref={drop}>
|
||||
// {componentValueLength > 0 &&
|
||||
// componentValue.map((data, index) => {
|
||||
// const key = data.__temp_key__;
|
||||
// const isOpen = collapseToOpen === key;
|
||||
// const componentFieldName = `${name}.${index}`;
|
||||
// const previousComponentTempKey = get(componentValue, [index - 1, '__temp_key__']);
|
||||
// const doesPreviousFieldContainErrorsAndIsOpen =
|
||||
// componentErrorKeys.includes(`${name}.${index - 1}`) &&
|
||||
// index !== 0 &&
|
||||
// collapseToOpen === previousComponentTempKey;
|
||||
|
||||
// const hasErrors = componentErrorKeys.includes(componentFieldName);
|
||||
|
||||
// return (
|
||||
// <DraggedItem
|
||||
// componentFieldName={componentFieldName}
|
||||
// componentUid={componentUid}
|
||||
// doesPreviousFieldContainErrorsAndIsOpen={doesPreviousFieldContainErrorsAndIsOpen}
|
||||
// hasErrors={hasErrors}
|
||||
// hasMinError={hasMinError}
|
||||
// isFirst={index === 0}
|
||||
// isReadOnly={isReadOnly}
|
||||
// isOpen={isOpen}
|
||||
// key={key}
|
||||
// onClickToggle={() => {
|
||||
// if (isOpen) {
|
||||
// setCollapseToOpen('');
|
||||
// } else {
|
||||
// setCollapseToOpen(key);
|
||||
// }
|
||||
// }}
|
||||
// parentName={name}
|
||||
// schema={componentLayoutData}
|
||||
// toggleCollapses={toggleCollapses}
|
||||
// />
|
||||
// );
|
||||
// })}
|
||||
// </div>
|
||||
// <Button
|
||||
// hasMinError={hasMinError}
|
||||
// disabled={isReadOnly}
|
||||
// withBorderRadius={false}
|
||||
// doesPreviousFieldContainErrorsAndIsClosed={
|
||||
// componentValueLength > 0 &&
|
||||
// componentErrorKeys.includes(`${name}.${componentValueLength - 1}`) &&
|
||||
// componentValue[componentValueLength - 1].__temp_key__ !== collapseToOpen
|
||||
// }
|
||||
// type="button"
|
||||
// onClick={handleClick}
|
||||
// >
|
||||
// <i className="fa fa-plus" />
|
||||
// <FormattedMessage id={getTrad('containers.EditView.add.new')} />
|
||||
// </Button>
|
||||
// {hasMinError && (
|
||||
// <ErrorMessage>
|
||||
// <FormattedMessage
|
||||
// id={getTrad(
|
||||
// `components.DynamicZone.missing${
|
||||
// missingComponentsValue > 1 ? '.plural' : '.singular'
|
||||
// }`
|
||||
// )}
|
||||
// values={{ count: missingComponentsValue }}
|
||||
// />
|
||||
// </ErrorMessage>
|
||||
// )}
|
||||
// </div>
|
||||
// );
|
||||
};
|
||||
|
||||
RepeatableComponent.defaultProps = {
|
||||
@ -308,7 +191,7 @@ RepeatableComponent.defaultProps = {
|
||||
formErrors: {},
|
||||
isNested: false,
|
||||
max: Infinity,
|
||||
// min: -Infinity,
|
||||
min: 0,
|
||||
};
|
||||
|
||||
RepeatableComponent.propTypes = {
|
||||
@ -320,7 +203,7 @@ RepeatableComponent.propTypes = {
|
||||
isNested: PropTypes.bool,
|
||||
isReadOnly: PropTypes.bool.isRequired,
|
||||
max: PropTypes.number,
|
||||
// min: PropTypes.number,
|
||||
min: PropTypes.number,
|
||||
name: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
|
@ -461,6 +461,7 @@
|
||||
"content-manager.components.DynamicZone.ComponentPicker-label": "Pick one component",
|
||||
"content-manager.components.DynamicZone.add-component": "Add a component to {componentName}",
|
||||
"content-manager.components.DynamicZone.delete-label": "Delete {name}",
|
||||
"content-manager.components.DynamicZone.error-message": "The component contains error(s)",
|
||||
"content-manager.components.DynamicZone.missing-components": "There {number, plural, =0 {are # missing components} one {is # missing component} other {are # missing components}}",
|
||||
"content-manager.components.DynamicZone.move-down-label": "Move component down",
|
||||
"content-manager.components.DynamicZone.move-up-label": "Move component up",
|
||||
@ -481,6 +482,7 @@
|
||||
"content-manager.components.LeftMenu.single-types": "{number, plural, =0 {Single Types} one {Single Type } other {Single Types}}",
|
||||
"content-manager.components.LimitSelect.itemsPerPage": "Items per page",
|
||||
"content-manager.components.NotAllowedInput.text": "No permissions to see this field",
|
||||
"content-manager.components.RepeatableComponent.error-message": "The component(s) contain error(s)",
|
||||
"content-manager.components.Search.placeholder": "Search for an entry...",
|
||||
"content-manager.components.Select.draft-info-title": "State: Draft",
|
||||
"content-manager.components.Select.publish-info-title": "State: Published",
|
||||
|
Loading…
x
Reference in New Issue
Block a user