feat(list): Add heading for list widgets (#5544)

This commit is contained in:
Sanjoth Rai 2021-07-14 20:13:50 +05:30 committed by GitHub
parent b94d5f6e7f
commit d60df8786d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 9 deletions

View File

@ -4,11 +4,17 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { css, ClassNames } from '@emotion/core'; import { css, ClassNames } from '@emotion/core';
import { List, Map, fromJS } from 'immutable'; import { List, Map, fromJS } from 'immutable';
import { partial, isEmpty } from 'lodash'; import { partial, isEmpty, uniqueId } from 'lodash';
import uuid from 'uuid/v4'; import uuid from 'uuid/v4';
import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'; import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
import NetlifyCmsWidgetObject from 'netlify-cms-widget-object'; import NetlifyCmsWidgetObject from 'netlify-cms-widget-object';
import { ListItemTopBar, ObjectWidgetTopBar, colors, lengths } from 'netlify-cms-ui-default'; import {
ListItemTopBar,
ObjectWidgetTopBar,
colors,
lengths,
FieldLabel,
} from 'netlify-cms-ui-default';
import { stringTemplate, validations } from 'netlify-cms-lib-widgets'; import { stringTemplate, validations } from 'netlify-cms-lib-widgets';
import { import {
@ -87,6 +93,14 @@ function validateItem(field, item) {
return true; return true;
} }
function LabelComponent({ field, isActive, hasErrors, uniqueFieldId, isFieldOptional, t }) {
const label = `${field.get('label', field.get('name'))}`;
return (
<FieldLabel isActive={isActive} hasErrors={hasErrors} htmlFor={uniqueFieldId}>
{label} {`${isFieldOptional ? ` (${t('editor.editorControl.field.optional')})` : ''}`}
</FieldLabel>
);
}
export default class ListControl extends React.Component { export default class ListControl extends React.Component {
validations = []; validations = [];
@ -164,6 +178,7 @@ export default class ListControl extends React.Component {
} }
}; };
uniqueFieldId = uniqueId(`${this.props.field.get('name')}-field-`);
/** /**
* Always update so that each nested widget has the option to update. This is * Always update so that each nested widget has the option to update. This is
* required because ControlHOC provides a default `shouldComponentUpdate` * required because ControlHOC provides a default `shouldComponentUpdate`
@ -493,6 +508,7 @@ export default class ListControl extends React.Component {
resolveWidget, resolveWidget,
parentIds, parentIds,
forID, forID,
t,
} = this.props; } = this.props;
const { itemsCollapsed, keys } = this.state; const { itemsCollapsed, keys } = this.state;
@ -500,20 +516,29 @@ export default class ListControl extends React.Component {
const key = keys[index]; const key = keys[index];
let field = this.props.field; let field = this.props.field;
const hasError = this.hasError(index); const hasError = this.hasError(index);
const isVariableTypesList = this.getValueType() === valueTypes.MIXED;
if (this.getValueType() === valueTypes.MIXED) { if (isVariableTypesList) {
field = getTypedFieldForValue(field, item); field = getTypedFieldForValue(field, item);
if (!field) { if (!field) {
return this.renderErroneousTypedItem(index, item); return this.renderErroneousTypedItem(index, item);
} }
} }
return ( return (
<SortableListItem <SortableListItem
css={[styles.listControlItem, collapsed && styles.listControlItemCollapsed]} css={[styles.listControlItem, collapsed && styles.listControlItemCollapsed]}
index={index} index={index}
key={key} key={key}
> >
{isVariableTypesList && (
<LabelComponent
field={field}
isActive={false}
hasErrors={hasError}
uniqueFieldId={this.uniqueFieldId}
isFieldOptional={field.get('required') === false}
t={t}
/>
)}
<StyledListItemTopBar <StyledListItemTopBar
collapsed={collapsed} collapsed={collapsed}
onCollapseToggle={partial(this.handleItemCollapseToggle, index)} onCollapseToggle={partial(this.handleItemCollapseToggle, index)}

View File

@ -299,14 +299,14 @@ describe('ListControl', () => {
], ],
}); });
const { getByText } = render( const { getAllByText } = render(
<ListControl <ListControl
{...props} {...props}
field={field} field={field}
value={fromJS([{ first_name: 'hello', last_name: 'world', type: 'type_1_object' }])} value={fromJS([{ first_name: 'hello', last_name: 'world', type: 'type_1_object' }])}
/>, />,
); );
expect(getByText('type_1_object')).toBeInTheDocument(); expect(getAllByText('type_1_object')[1]).toBeInTheDocument();
}); });
it('should use label when no summary is configured for mixed types', () => { it('should use label when no summary is configured for mixed types', () => {
@ -327,14 +327,14 @@ describe('ListControl', () => {
], ],
}); });
const { getByText } = render( const { getAllByText } = render(
<ListControl <ListControl
{...props} {...props}
field={field} field={field}
value={fromJS([{ first_name: 'hello', last_name: 'world', type: 'type_1_object' }])} value={fromJS([{ first_name: 'hello', last_name: 'world', type: 'type_1_object' }])}
/>, />,
); );
expect(getByText('Type 1 Object')).toBeInTheDocument(); expect(getAllByText('Type 1 Object')[1]).toBeInTheDocument();
}); });
it('should use summary when configured for mixed types', () => { it('should use summary when configured for mixed types', () => {