fix: Error UI improvements for nested lists/objects (#3726)
This commit is contained in:
@ -3,6 +3,10 @@ const editorStatus = { draft: 'Draft', review: 'In review', ready: 'Ready' };
|
||||
const setting1 = { limit: 10, author: 'John Doe' };
|
||||
const setting2 = { name: 'Jane Doe', description: 'description' };
|
||||
const publishTypes = { publishNow: 'Publish now', publishAndCreateNew: 'Publish and create new', publishAndDuplicate: 'Publish and duplicate' };
|
||||
const colorError = 'rgb(255, 0, 59)';
|
||||
const colorNormal = 'rgb(223, 223, 227)';
|
||||
const textColorNormal = 'rgb(68, 74, 87)';
|
||||
|
||||
const notifications = {
|
||||
saved: 'Entry saved',
|
||||
published: 'Entry published',
|
||||
@ -40,5 +44,8 @@ module.exports = {
|
||||
setting2,
|
||||
notifications,
|
||||
publishTypes,
|
||||
HOT_KEY_MAP
|
||||
HOT_KEY_MAP,
|
||||
colorError,
|
||||
colorNormal,
|
||||
textColorNormal,
|
||||
};
|
||||
|
@ -1,4 +1,12 @@
|
||||
const { notifications, workflowStatus, editorStatus, publishTypes } = require('./constants');
|
||||
const {
|
||||
notifications,
|
||||
workflowStatus,
|
||||
editorStatus,
|
||||
publishTypes,
|
||||
colorError,
|
||||
colorNormal,
|
||||
textColorNormal,
|
||||
} = require('./constants');
|
||||
|
||||
function login(user) {
|
||||
cy.viewport(1200, 1200);
|
||||
@ -38,6 +46,35 @@ function assertNotification(message) {
|
||||
});
|
||||
}
|
||||
|
||||
function assertColorOn(cssProperty, color, opts) {
|
||||
if (opts.type && opts.type === 'label') {
|
||||
(opts.scope ? opts.scope : cy).contains('label', opts.label).should($el => {
|
||||
expect($el).to.have.css(cssProperty, color);
|
||||
});
|
||||
} else if (opts.type && opts.type === 'field') {
|
||||
const assertion = $el => expect($el).to.have.css(cssProperty, color);
|
||||
if (opts.isMarkdown) {
|
||||
(opts.scope ? opts.scope : cy)
|
||||
.contains('label', opts.label)
|
||||
.next()
|
||||
.children()
|
||||
.eq(0)
|
||||
.children()
|
||||
.eq(1)
|
||||
.should(assertion);
|
||||
} else {
|
||||
(opts.scope ? opts.scope : cy)
|
||||
.contains('label', opts.label)
|
||||
.next()
|
||||
.should(assertion);
|
||||
}
|
||||
} else if (opts.el) {
|
||||
opts.el.should($el => {
|
||||
expect($el).to.have.css(cssProperty, color);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function exitEditor() {
|
||||
cy.contains('a[href^="#/collections/"]', 'Writing in').click();
|
||||
}
|
||||
@ -204,9 +241,6 @@ function flushClockAndSave() {
|
||||
cy.wait(500);
|
||||
}
|
||||
|
||||
cy.get('input')
|
||||
.first()
|
||||
.click();
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.saved);
|
||||
});
|
||||
@ -381,10 +415,12 @@ function validateObjectFields({ limit, author }) {
|
||||
cy.get('input[type=number]').type(limit);
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.error.missingField);
|
||||
assertFieldErrorStatus('Default Author', colorError);
|
||||
cy.contains('label', 'Default Author').click();
|
||||
cy.focused().type(author);
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.saved);
|
||||
assertFieldErrorStatus('Default Author', colorNormal);
|
||||
}
|
||||
|
||||
function validateNestedObjectFields({ limit, author }) {
|
||||
@ -415,13 +451,126 @@ function validateListFields({ name, description }) {
|
||||
cy.contains('button', 'Add').click();
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.error.missingField);
|
||||
assertFieldErrorStatus('Authors', colorError);
|
||||
cy.get('div[class*=ListControl]')
|
||||
.eq(2)
|
||||
.as('listControl');
|
||||
assertFieldErrorStatus('Name', colorError, { scope: cy.get('@listControl') });
|
||||
assertColorOn('background-color', colorError, {
|
||||
type: 'label',
|
||||
label: 'Description',
|
||||
scope: cy.get('@listControl'),
|
||||
isMarkdown: true,
|
||||
});
|
||||
assertListControlErrorStatus([colorError, colorError], '@listControl');
|
||||
cy.get('input')
|
||||
.eq(2)
|
||||
.type(name);
|
||||
cy.getMarkdownEditor()
|
||||
.eq(2)
|
||||
.type(description);
|
||||
flushClockAndSave();
|
||||
assertNotification(notifications.saved);
|
||||
assertFieldErrorStatus('Authors', colorNormal);
|
||||
}
|
||||
|
||||
function validateNestedListFields() {
|
||||
cy.get('a[href^="#/collections/settings"]').click();
|
||||
cy.get('a[href^="#/collections/settings/entries/hotel_locations"]').click();
|
||||
|
||||
// add first city list item
|
||||
cy.contains('button', 'hotel locations').click();
|
||||
cy.contains('button', 'cities').click();
|
||||
cy.contains('label', 'City')
|
||||
.next()
|
||||
.type('Washington DC');
|
||||
cy.contains('label', 'Number of Hotels in City')
|
||||
.next()
|
||||
.type('5');
|
||||
cy.contains('button', 'city locations').click();
|
||||
|
||||
// add second city list item
|
||||
cy.contains('button', 'cities').click();
|
||||
cy.contains('label', 'Cities')
|
||||
.next()
|
||||
.find('div[class*=ListControl]')
|
||||
.eq(2)
|
||||
.as('secondCitiesListControl');
|
||||
cy.get('@secondCitiesListControl')
|
||||
.contains('label', 'City')
|
||||
.next()
|
||||
.type('Boston');
|
||||
cy.get('@secondCitiesListControl')
|
||||
.contains('button', 'city locations')
|
||||
.click();
|
||||
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.error.missingField);
|
||||
|
||||
// assert on fields
|
||||
assertFieldErrorStatus('Hotel Locations', colorError);
|
||||
assertFieldErrorStatus('Cities', colorError);
|
||||
assertFieldErrorStatus('City', colorNormal);
|
||||
assertFieldErrorStatus('City', colorNormal, { scope: cy.get('@secondCitiesListControl') });
|
||||
assertFieldErrorStatus('Number of Hotels in City', colorNormal);
|
||||
assertFieldErrorStatus('Number of Hotels in City', colorError, {
|
||||
scope: cy.get('@secondCitiesListControl'),
|
||||
});
|
||||
assertFieldErrorStatus('City Locations', colorError);
|
||||
assertFieldErrorStatus('City Locations', colorError, {
|
||||
scope: cy.get('@secondCitiesListControl'),
|
||||
});
|
||||
assertFieldErrorStatus('Hotel Name', colorError);
|
||||
assertFieldErrorStatus('Hotel Name', colorError, { scope: cy.get('@secondCitiesListControl') });
|
||||
|
||||
// list control aliases
|
||||
cy.contains('label', 'Hotel Locations')
|
||||
.next()
|
||||
.find('div[class*=ListControl]')
|
||||
.first()
|
||||
.as('hotelLocationsListControl');
|
||||
cy.contains('label', 'Cities')
|
||||
.next()
|
||||
.find('div[class*=ListControl]')
|
||||
.eq(0)
|
||||
.as('firstCitiesListControl');
|
||||
cy.contains('label', 'City Locations')
|
||||
.next()
|
||||
.find('div[class*=ListControl]')
|
||||
.eq(0)
|
||||
.as('firstCityLocationsListControl');
|
||||
cy.contains('label', 'Cities')
|
||||
.next()
|
||||
.find('div[class*=ListControl]')
|
||||
.eq(3)
|
||||
.as('secondCityLocationsListControl');
|
||||
|
||||
// assert on list controls
|
||||
assertListControlErrorStatus([colorError, colorError], '@hotelLocationsListControl');
|
||||
assertListControlErrorStatus([colorError, colorError], '@firstCitiesListControl');
|
||||
assertListControlErrorStatus([colorError, colorError], '@secondCitiesListControl');
|
||||
assertListControlErrorStatus([colorError, colorError], '@firstCityLocationsListControl');
|
||||
assertListControlErrorStatus([colorError, colorError], '@secondCityLocationsListControl');
|
||||
|
||||
cy.contains('label', 'Hotel Name')
|
||||
.next()
|
||||
.type('The Ritz Carlton');
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.error.missingField);
|
||||
assertListControlErrorStatus([colorNormal, textColorNormal], '@firstCitiesListControl');
|
||||
|
||||
// fill out rest of form and save
|
||||
cy.get('@secondCitiesListControl')
|
||||
.contains('label', 'Number of Hotels in City')
|
||||
.type(3);
|
||||
cy.get('@secondCitiesListControl')
|
||||
.contains('label', 'Hotel Name')
|
||||
.type('Grand Hyatt');
|
||||
cy.contains('label', 'Country')
|
||||
.next()
|
||||
.type('United States');
|
||||
flushClockAndSave();
|
||||
assertNotification(notifications.saved);
|
||||
}
|
||||
|
||||
function validateObjectFieldsAndExit(setting) {
|
||||
@ -439,10 +588,53 @@ function validateListFieldsAndExit(setting) {
|
||||
exitEditor();
|
||||
}
|
||||
|
||||
function validateNestedListFieldsAndExit(setting) {
|
||||
validateNestedListFields(setting);
|
||||
exitEditor();
|
||||
}
|
||||
|
||||
function assertFieldValidationError({ message, fieldLabel }) {
|
||||
cy.contains('label', fieldLabel)
|
||||
.siblings('ul[class*=ControlErrorsList]')
|
||||
.contains(message);
|
||||
assertFieldErrorStatus(fieldLabel, colorError);
|
||||
}
|
||||
|
||||
function assertFieldErrorStatus(label, color, opts = { isMarkdown: false }) {
|
||||
assertColorOn('background-color', color, {
|
||||
type: 'label',
|
||||
label,
|
||||
scope: opts.scope,
|
||||
isMarkdown: opts.isMarkdown,
|
||||
});
|
||||
assertColorOn('border-color', color, {
|
||||
type: 'field',
|
||||
label,
|
||||
scope: opts.scope,
|
||||
isMarkdown: opts.isMarkdown,
|
||||
});
|
||||
}
|
||||
|
||||
function assertListControlErrorStatus(colors = ['', ''], alias) {
|
||||
cy.get(alias).within(() => {
|
||||
// assert list item border has correct color
|
||||
assertColorOn('border-right-color', colors[0], {
|
||||
el: cy
|
||||
.root()
|
||||
.children()
|
||||
.eq(2),
|
||||
});
|
||||
// collapse list item
|
||||
cy.get('button[class*=TopBarButton-button]')
|
||||
.first()
|
||||
.click();
|
||||
// assert list item label text has correct color
|
||||
assertColorOn('color', colors[1], { el: cy.get('div[class*=NestedObjectLabel]').first() });
|
||||
// uncollapse list item
|
||||
cy.get('button[class*=TopBarButton-button]')
|
||||
.first()
|
||||
.click();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@ -468,6 +660,7 @@ module.exports = {
|
||||
validateObjectFieldsAndExit,
|
||||
validateNestedObjectFieldsAndExit,
|
||||
validateListFieldsAndExit,
|
||||
validateNestedListFieldsAndExit,
|
||||
unpublishEntry,
|
||||
publishEntryInEditor,
|
||||
duplicateEntry,
|
||||
|
Reference in New Issue
Block a user