Skip to content

Commit

Permalink
FEATURE: Display number of validation errors for each tab in inspector (
Browse files Browse the repository at this point in the history
  • Loading branch information
markusguenther authored and dimaip committed Dec 2, 2019
1 parent 2bd0523 commit d2e1c94
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 2 deletions.
14 changes: 14 additions & 0 deletions Resources/Private/Translations/da/Main.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,20 @@
<trans-unit id="Shortcut__UI.RightSideBar.toggle" xml:space="preserve" approved="yes">
<source>Toggle inspector</source>
<target xml:lang="da">Skjul/vis inspektør</target></trans-unit>
<trans-unit id="UI.RightSideBar.tabs.validationErrorTooltip" xml:space="preserve">
<source>{tabName} - amount of properties with validation issues: {amountOfErrors}</source>
<target xml:lang="da">{tabName} - Antal egenskaber med valideringsproblemer: {amountOfErrors}</target></trans-unit>
</trans-unit>
<group id="UI.RightSideBar.tabs.validationErrorTooltip" restype="x-gettext-plurals">
<trans-unit id="UI.RightSideBar.tabs.validationErrorTooltip[0]" xml:space="preserve">
<source>{tabName} – {amountOfErrors} validation issue</source>
<target xml:lang="de">{tabName} – {amountOfErrors} valideringsfejl</target>
</trans-unit>
<trans-unit id="UI.RightSideBar.tabs.validationErrorTooltip[1]" xml:space="preserve">
<source>{tabName} – {amountOfErrors} validation issues</source>
<target xml:lang="de">{tabName} – {amountOfErrors} valideringsfejl</target>
</trans-unit>
</group>
<trans-unit id="Shortcut__UI.FullScreen.toggle" xml:space="preserve">
<source>Toggle full screen</source>
<target xml:lang="da" state="needs-translation">Toggle full screen</target></trans-unit>
Expand Down
10 changes: 10 additions & 0 deletions Resources/Private/Translations/de/Main.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,16 @@
<trans-unit id="Shortcut__UI.RightSideBar.toggle" xml:space="preserve" approved="yes">
<source>Toggle inspector</source>
<target xml:lang="de">Inspector auf- / zuklappen</target></trans-unit>
<group id="UI.RightSideBar.tabs.validationErrorTooltip" restype="x-gettext-plurals">
<trans-unit id="UI.RightSideBar.tabs.validationErrorTooltip[0]" xml:space="preserve">
<source>{tabName} – {amountOfErrors} validation issue</source>
<target xml:lang="de">{tabName} – {amountOfErrors} Validierungsfehler</target>
</trans-unit>
<trans-unit id="UI.RightSideBar.tabs.validationErrorTooltip[1]" xml:space="preserve">
<source>{tabName} – {amountOfErrors} validation issues</source>
<target xml:lang="de">{tabName} – {amountOfErrors} Validierungsfehler</target>
</trans-unit>
</group>
<trans-unit id="Shortcut__UI.FullScreen.toggle" xml:space="preserve" approved="yes">
<source>Toggle full screen</source>
<target xml:lang="de">Vollbildmodus umschalten</target></trans-unit>
Expand Down
8 changes: 8 additions & 0 deletions Resources/Private/Translations/en/Main.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@
<trans-unit id="Shortcut__UI.RightSideBar.toggle" xml:space="preserve">
<source>Toggle inspector</source>
</trans-unit>
<group id="UI.RightSideBar.tabs.validationErrorTooltip" restype="x-gettext-plurals">
<trans-unit id="UI.RightSideBar.tabs.validationErrorTooltip[0]" xml:space="preserve">
<source>{tabName} – {amountOfErrors} validation issue</source>
</trans-unit>
<trans-unit id="UI.RightSideBar.tabs.validationErrorTooltip[1]" xml:space="preserve">
<source>{tabName} – {amountOfErrors} validation issues</source>
</trans-unit>
</group>
<trans-unit id="Shortcut__UI.FullScreen.toggle" xml:space="preserve">
<source>Toggle full screen</source>
</trans-unit>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {Selector} from 'testcafe';
import {ReactSelector} from 'testcafe-react-selectors';
import {beforeEach, subSection, checkPropTypes} from './../../utils.js';
import {Page} from './../../pageModel';

/* global fixture:true */

fixture`InspectorValidation`.beforeEach(beforeEach).afterEach(() => checkPropTypes());

const InspectorTitleProperty = Selector(
'#__neos__editor__property---title'
);
const InspectorUriPathSegmentProperty = Selector(
'#__neos__editor__property---uriPathSegment'
);
const activeTabMenuItem = ReactSelector('TabMenuItem').withProps('isActive', true);

test('Remove homepage title to get one error', async t => {
await Page.waitForIframeLoading(t);

subSection('Remove homepage title');
await t
.typeText(InspectorTitleProperty, ' ', {replace: true})
.pressKey('backspace')
.expect(InspectorTitleProperty.value).eql('');

subSection('Check error badge for one error');
const badge = await activeTabMenuItem.findReact('Badge');
await t
.expect(await badge.getReact(({props}) => props.label))
.eql('1', 'The badge shows one validation error in Props');
});

test('Remove homepage title and URI segment to get two errors', async t => {
subSection('Clean title and uri path segment field');
await t
.typeText(InspectorTitleProperty, ' ', {replace: true})
.pressKey('backspace')
.typeText(InspectorUriPathSegmentProperty, ' ', {replace: true})
.pressKey('backspace');

subSection('Check error badge for two errors');
const badge = await activeTabMenuItem.findReact('Badge');
await t
.expect(await badge.getReact(({props}) => props.label))
.eql('2', 'The badge shows two validation errors in Props');
});

test('Remove homepage title to get one error and resolve error by new title', async t => {
subSection('Remove homepage title');
await t
.typeText(InspectorTitleProperty, ' ', {replace: true})
.pressKey('backspace')
.expect(InspectorTitleProperty.value).eql('');

subSection('Check error badge for one error');
let badge = await activeTabMenuItem.findReact('Badge');
await t
.expect(await badge.getReact(({props}) => props.label))
.eql('1', 'The badge shows one validation error in Props');

subSection('Enter new title Home');
await t
.typeText(InspectorTitleProperty, 'Home', {replace: true});
badge = await activeTabMenuItem.findReact('Badge');
await t
.expect(await badge.getReact(({props}) => props.label))
.eql(null, 'The badge is not existing');
});
37 changes: 36 additions & 1 deletion packages/neos-ui/src/Containers/RightSideBar/Inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Bar from '@neos-project/react-ui-components/src/Bar/';
import Button from '@neos-project/react-ui-components/src/Button/';
import Tabs from '@neos-project/react-ui-components/src/Tabs/';
import Icon from '@neos-project/react-ui-components/src/Icon/';
import Badge from '@neos-project/react-ui-components/src/Badge/';
import debounce from 'lodash.debounce';
import setIn from 'lodash.set';

Expand All @@ -26,6 +27,7 @@ import style from './style.css';
i18nRegistry: globalRegistry.get('i18n')
}))
@connect((state, {nodeTypesRegistry, validatorRegistry}) => {
const validationErrorsSelector = selectors.UI.Inspector.makeValidationErrorsSelector(nodeTypesRegistry, validatorRegistry);
const isApplyDisabledSelector = selectors.UI.Inspector.makeIsApplyDisabledSelector(nodeTypesRegistry, validatorRegistry);

return state => {
Expand All @@ -36,6 +38,7 @@ import style from './style.css';

return {
focusedNode: selectors.CR.Nodes.focusedSelector(state),
validationErrors: validationErrorsSelector(state),
isApplyDisabled: isApplyDisabledSelector(state),
transientValues: selectors.UI.Inspector.transientValues(state),
isDiscardDisabled: selectors.UI.Inspector.isDiscardDisabledSelector(state),
Expand Down Expand Up @@ -219,10 +222,24 @@ export default class Inspector extends PureComponent {
this.setState({toggledPanels: newState});
};

getAmountOfValidationErrors = (tab, validationErrors) => {
let errors = 0;
tab.groups.forEach(group => {
group.items.forEach(item => {
if (Object.keys(validationErrors).includes(item.id)) {
errors += 1;
}
});
});

return errors;
};

render() {
const {
focusedNode,
commit,
validationErrors,
isApplyDisabled,
isDiscardDisabled,
shouldShowUnappliedChangesOverlay,
Expand Down Expand Up @@ -277,14 +294,32 @@ export default class Inspector extends PureComponent {
// Render each tab as a TabPanel
//
.map(tab => {
const notifications = validationErrors ?
this.getAmountOfValidationErrors(tab, validationErrors) : 0;
const tabLabel = i18nRegistry.translate($get('label', tab));
const notificationTooltipLabelPieces = i18nRegistry.translate(
'UI.RightSideBar.tabs.validationErrorTooltip',
'',
{
tabName: tabLabel,
amountOfErrors: notifications
},
'Neos.Neos.Ui'
);
// @todo remove that when substitutePlaceholders of I18nRegistry returns strings
const notificationTooltipLabel = Array.isArray(notificationTooltipLabelPieces) ?
notificationTooltipLabelPieces.join('') : notificationTooltipLabelPieces;

return (
<TabPanel
key={$get('id', tab)}
id={$get('id', tab)}
icon={$get('icon', tab)}
groups={$get('groups', tab)}
notifications={notifications}
title={Boolean(notifications) && <Badge className={style.tabs__notificationBadge} label={String(notifications)}/>}
toggledPanels={$get($get('id', tab), this.state.toggledPanels)}
tooltip={i18nRegistry.translate($get('label', tab))}
tooltip={notifications ? notificationTooltipLabel : tabLabel}
renderSecondaryInspector={this.renderSecondaryInspector}
node={focusedNode}
commit={augmentedCommit}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
border-left: 1px solid var(--colors-ContrastDark);
background: var(--colors-ContrastDarker);
}
.tabs__notificationBadge {
position: absolute;
right: 1px;
top: 1px;
z-index: 1;
background-color: var(--colors-Error);
color: var(--colors-ContrastBrightest);
transform: scale(.75);
}
.tabsContent > div {
height: 100%;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-ui-components/src/Tabs/tabs.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('<Tabs/>', () => {
'tabNavigation__item--isActive': 'activeTabsNavigationItemClassName',
'tabNavigation__itemBtn': 'baseTabsNavigationItemBtnClassName',
'tabNavigation__itemBtnIcon': 'baseTabsNavigationItemBtnIconClassName',
'tabNavigation__itemBtnIcon--hasLabel': 'baseTabsNavigationItemBtnIconWithLabelClassName'
'tabNavigation__itemBtnIcon--hasLabel': 'baseTabsNavigationItemBtnIconWithLabelClassName',
},
children: [<div key={'foo'}>'Foo children'</div>]
};
Expand Down

0 comments on commit d2e1c94

Please sign in to comment.