Skip to content

Commit d9655ea

Browse files
olegfedakmartinjagodicdemshy
authored
fix: clear field error in Editor after the field value is changed (#7216)
* fix: remove field error in Editor after the field is changed * fix: add px to control hint * fix: add ControlTopbar for correct error display at widget * chore: update after tests * chore: update caniuse-lite * fix: child list element should have key prop * chore: update formatting * fix: add ControlTopbar margin and padding * fix: add changes to the tests * fix: remove the comments --------- Co-authored-by: Martin Jagodic <jagodicmartin1@gmail.com> Co-authored-by: Anze Demsar <anze.demsar@p-m.si>
1 parent 1d0cd61 commit d9655ea

File tree

7 files changed

+92
-42
lines changed

7 files changed

+92
-42
lines changed

cypress/e2e/markdown_widget_code_block_spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ function codeBlock(content) {
7777
<div>
7878
<div></div>
7979
<div>
80-
<div><label>Code Block</label>
80+
<div>
81+
<div><label>Code Block</label></div>
8182
<div><button><span><svg>
8283
<path></path>
8384
</svg></span></button>

cypress/utils/steps.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ function assertColorOn(cssProperty, color, opts) {
6969
} else {
7070
(opts.scope ? opts.scope : cy)
7171
.contains('label', opts.label)
72+
.parents()
7273
.next()
7374
.should(assertion);
7475
}
@@ -518,23 +519,31 @@ function validateNestedListFields() {
518519
cy.contains('button', 'hotel locations').click();
519520
cy.contains('button', 'cities').click();
520521
cy.contains('label', 'City')
522+
.parents()
521523
.next()
524+
.first()
522525
.type('Washington DC');
523526
cy.contains('label', 'Number of Hotels in City')
527+
.parents()
524528
.next()
529+
.first()
525530
.type('5');
526531
cy.contains('button', 'city locations').click();
527532

528533
// add second city list item
529534
cy.contains('button', 'cities').click();
530535
cy.contains('label', 'Cities')
536+
.parents()
531537
.next()
538+
.first()
532539
.find('div[class*=SortableListItem]')
533540
.eq(2)
534541
.as('secondCitiesListControl');
535542
cy.get('@secondCitiesListControl')
536543
.contains('label', 'City')
544+
.parents()
537545
.next()
546+
.first()
538547
.type('Boston');
539548
cy.get('@secondCitiesListControl')
540549
.contains('button', 'city locations')
@@ -561,21 +570,25 @@ function validateNestedListFields() {
561570

562571
// list control aliases
563572
cy.contains('label', 'Hotel Locations')
573+
.parents()
564574
.next()
565575
.find('div[class*=SortableListItem]')
566576
.first()
567577
.as('hotelLocationsListControl');
568578
cy.contains('label', 'Cities')
579+
.parents()
569580
.next()
570581
.find('div[class*=SortableListItem]')
571582
.eq(0)
572583
.as('firstCitiesListControl');
573584
cy.contains('label', 'City Locations')
585+
.parents()
574586
.next()
575587
.find('div[class*=SortableListItem]')
576588
.eq(0)
577589
.as('firstCityLocationsListControl');
578590
cy.contains('label', 'Cities')
591+
.parents()
579592
.next()
580593
.find('div[class*=SortableListItem]')
581594
.eq(3)
@@ -589,7 +602,9 @@ function validateNestedListFields() {
589602
assertListControlErrorStatus([colorError, colorError], '@secondCityLocationsListControl');
590603

591604
cy.contains('label', 'Hotel Name')
605+
.parents()
592606
.next()
607+
.first()
593608
.type('The Ritz Carlton');
594609
cy.contains('button', 'Save').click();
595610
assertNotification(notifications.error.missingField);
@@ -598,12 +613,20 @@ function validateNestedListFields() {
598613
// fill out rest of form and save
599614
cy.get('@secondCitiesListControl')
600615
.contains('label', 'Number of Hotels in City')
616+
.parents()
617+
.next()
618+
.first()
601619
.type(3);
602620
cy.get('@secondCitiesListControl')
603621
.contains('label', 'Hotel Name')
622+
.parents()
623+
.next()
624+
.first()
604625
.type('Grand Hyatt');
605626
cy.contains('label', 'Country')
627+
.parents()
606628
.next()
629+
.first()
607630
.type('United States');
608631
flushClockAndSave();
609632
assertNotification(notifications.saved);

package-lock.json

Lines changed: 9 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/decap-cms-core/src/actions/entries.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,11 @@ export function changeDraftFieldValidation(
431431
};
432432
}
433433

434-
export function clearFieldErrors() {
435-
return { type: DRAFT_CLEAR_ERRORS };
434+
export function clearFieldErrors(uniqueFieldId: string) {
435+
return {
436+
type: DRAFT_CLEAR_ERRORS,
437+
payload: { uniqueFieldId },
438+
};
436439
}
437440

438441
export function localBackupRetrieved(entry: EntryValue) {

packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControl.js

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ const styleStrings = {
6262
disabled: `
6363
pointer-events: none;
6464
opacity: 0.5;
65-
background: #ccc;
6665
`,
6766
hidden: `
6867
visibility: hidden;
@@ -77,18 +76,26 @@ const ControlContainer = styled.div`
7776
}
7877
`;
7978

79+
const ControlTopbar = styled.div`
80+
display: flex;
81+
justify-content: space-between;
82+
gap: 20px;
83+
align-items: end;
84+
`;
8085
const ControlErrorsList = styled.ul`
8186
list-style-type: none;
8287
font-size: 12px;
8388
color: ${colors.errorText};
84-
margin-bottom: 8px;
8589
text-align: right;
90+
text-transform: uppercase;
8691
font-weight: 600;
92+
margin: 0;
93+
padding: 2px 0 3px;
8794
`;
8895

8996
export const ControlHint = styled.p`
9097
margin-bottom: 0;
91-
padding: 3px 0;
98+
padding: 6px 0 0;
9299
font-size: 12px;
93100
color: ${props =>
94101
props.error ? colors.errorText : props.active ? colors.active : colors.controlLabel};
@@ -238,28 +245,30 @@ class EditorControl extends React.Component {
238245
${isHidden && styleStrings.hidden};
239246
`}
240247
>
241-
{widget.globalStyles && <Global styles={coreCss`${widget.globalStyles}`} />}
242-
{errors && (
243-
<ControlErrorsList>
244-
{errors.map(
245-
error =>
246-
error.message &&
247-
typeof error.message === 'string' && (
248-
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>
249-
{error.message}
250-
</li>
251-
),
252-
)}
253-
</ControlErrorsList>
254-
)}
255-
<LabelComponent
256-
field={field}
257-
isActive={isSelected || this.state.styleActive}
258-
hasErrors={hasErrors}
259-
uniqueFieldId={this.uniqueFieldId}
260-
isFieldOptional={isFieldOptional}
261-
t={t}
262-
/>
248+
<ControlTopbar>
249+
{widget.globalStyles && <Global styles={coreCss`${widget.globalStyles}`} />}
250+
<LabelComponent
251+
field={field}
252+
isActive={isSelected || this.state.styleActive}
253+
hasErrors={hasErrors}
254+
uniqueFieldId={this.uniqueFieldId}
255+
isFieldOptional={isFieldOptional}
256+
t={t}
257+
/>
258+
{errors && (
259+
<ControlErrorsList>
260+
{errors.map(
261+
error =>
262+
error.message &&
263+
typeof error.message === 'string' && (
264+
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>
265+
{error.message}
266+
</li>
267+
),
268+
)}
269+
</ControlErrorsList>
270+
)}
271+
</ControlTopbar>
263272
<Widget
264273
classNameWrapper={cx(
265274
css`
@@ -302,7 +311,10 @@ class EditorControl extends React.Component {
302311
value={value}
303312
mediaPaths={mediaPaths}
304313
metadata={metadata}
305-
onChange={(newValue, newMetadata) => onChange(field, newValue, newMetadata)}
314+
onChange={(newValue, newMetadata) => {
315+
onChange(field, newValue, newMetadata);
316+
clearFieldErrors(this.uniqueFieldId); // Видаляємо помилки лише для цього поля
317+
}}
306318
onValidate={onValidate && partial(onValidate, this.uniqueFieldId)}
307319
onOpenMediaLibrary={openMediaLibrary}
308320
onClearMediaControl={clearMediaControl}

packages/decap-cms-core/src/components/Editor/EditorToolbar.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,11 +594,20 @@ export class EditorToolbar extends React.Component {
594594
</SaveButton>,
595595
currentStatus
596596
? [
597-
this.renderWorkflowStatusControls(),
598-
this.renderNewEntryWorkflowPublishControls({ canCreate, canPublish }),
597+
<React.Fragment key="workflow-status-controls">
598+
{this.renderWorkflowStatusControls()}
599+
{!hasChanged && this.renderNewEntryWorkflowPublishControls({ canCreate, canPublish })}
600+
</React.Fragment>,
599601
]
600-
: !isNewEntry &&
601-
this.renderExistingEntryWorkflowPublishControls({ canCreate, canPublish, canDelete }),
602+
: !isNewEntry && (
603+
<React.Fragment key="existing-entry-workflow-publish-controls">
604+
{this.renderExistingEntryWorkflowPublishControls({
605+
canCreate,
606+
canPublish,
607+
canDelete,
608+
})}
609+
</React.Fragment>
610+
),
602611
(!showDelete || useOpenAuthoring) && !hasUnpublishedChanges && !isModification ? null : (
603612
<DeleteButton
604613
key="delete-button"

packages/decap-cms-core/src/reducers/entryDraft.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ function entryDraftReducer(state = Map(), action) {
129129
}
130130

131131
case DRAFT_CLEAR_ERRORS: {
132-
return state.set('fieldsErrors', Map());
132+
const { uniqueFieldId } = action.payload;
133+
return state.deleteIn(['fieldsErrors', uniqueFieldId]);
133134
}
134135

135136
case ENTRY_PERSIST_REQUEST:

0 commit comments

Comments
 (0)