Skip to content

Commit

Permalink
Merge pull request #241 from markusguenther/feature/validation
Browse files Browse the repository at this point in the history
FEATURE: Adds validation for create tag dialog
  • Loading branch information
Sebobo authored Nov 4, 2024
2 parents d7e8843 + d66a809 commit 2f211ca
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ const AddTagButton: React.FC = () => {
const selectedTagId = useRecoilValue(selectedTagIdState);

const onClickCreate = useCallback(() => {
setCreateTagDialogState({ label: '', visible: true });
setCreateTagDialogState({
visible: true,
label: '',
validation: {
valid: false,
errors: [],
},
});
}, [setCreateTagDialogState]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as React from 'react';
import { useCallback } from 'react';
import { useRecoilState } from 'recoil';

import { Button, Label, TextInput } from '@neos-project/react-ui-components';
import { Button, Label, TextInput, Tooltip } from '@neos-project/react-ui-components';

import { useIntl, useNotify } from '@media-ui/core';
import { useSelectedAssetCollection } from '@media-ui/feature-asset-collections';
import { useCreateTag } from '@media-ui/feature-asset-tags';
import { useCreateTag, useTagsQuery } from '@media-ui/feature-asset-tags';
import { Dialog } from '@media-ui/core/src/components';

import createTagDialogState from '../state/createTagDialogState';
Expand All @@ -18,10 +18,21 @@ const CreateTagDialog: React.FC = () => {
const Notify = useNotify();
const selectedAssetCollection = useSelectedAssetCollection();
const [dialogState, setDialogState] = useRecoilState(createTagDialogState);
const createPossible = !!(dialogState.label && dialogState.label.trim());
const { createTag } = useCreateTag();
const { tags } = useTagsQuery();

const handleRequestClose = useCallback(() => setDialogState({ visible: false, label: '' }), [setDialogState]);
const handleRequestClose = useCallback(
() =>
setDialogState({
visible: false,
label: '',
validation: {
valid: false,
errors: [],
},
}),
[setDialogState]
);
const handleCreate = useCallback(() => {
setDialogState((state) => ({ ...state, visible: false }));
createTag(dialogState.label, selectedAssetCollection?.id)
Expand All @@ -32,7 +43,32 @@ const CreateTagDialog: React.FC = () => {
return;
});
}, [Notify, setDialogState, createTag, dialogState, translate, selectedAssetCollection]);
const setLabel = useCallback((label) => setDialogState((state) => ({ ...state, label })), [setDialogState]);
const validate = (label) => {
const validationErrors = [];
const trimmedLabel = label.trim();
const tagWithLabelExist = tags?.some((tag) => tag.label === trimmedLabel);

if (trimmedLabel.length === 0) {
validationErrors.push(translate('tagActions.validation.emptyTagLabel', 'Please provide a tag label'));
}

if (tagWithLabelExist) {
validationErrors.push(translate('tagActions.validation.tagExists', 'A tag with this label already exists'));
}

const validation = {
errors: validationErrors,
valid: validationErrors.length === 0,
};
setDialogState((state) => ({ ...state, validation }));
};
const setLabel = useCallback(
(label) => {
validate(label);
setDialogState((state) => ({ ...state, label }));
},
[setDialogState]

Check warning on line 70 in Resources/Private/JavaScript/asset-tags/src/components/CreateTagDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook useCallback has a missing dependency: 'validate'. Either include it or remove the dependency array
);

return (
<Dialog
Expand All @@ -47,7 +83,7 @@ const CreateTagDialog: React.FC = () => {
key="upload"
style="success"
hoverStyle="success"
disabled={!createPossible}
disabled={!dialogState.validation?.valid}
onClick={handleCreate}
>
{translate('general.create', 'Create')}
Expand All @@ -58,11 +94,22 @@ const CreateTagDialog: React.FC = () => {
<Label>{translate('general.label', 'Label')}</Label>
<TextInput
setFocus
validationerrors={dialogState.validation?.valid ? null : ['This input is invalid']}
required={true}
type="text"
value={dialogState.label}
onChange={setLabel}
onEnterKey={createPossible ? handleCreate : null}
onEnterKey={dialogState.validation?.valid ? handleCreate : null}
/>
{dialogState.validation?.errors?.length > 0 && (
<Tooltip renderInline asError>
<ul>
{dialogState.validation.errors.map((error, index) => (
<li key={index}>{error}</li>
))}
</ul>
</Tooltip>
)}
</div>
</Dialog>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ const createTagDialogState = atom({
default: {
visible: false,
label: '',
validation: {
valid: false,
errors: [],
},
},
});

Expand Down
34 changes: 34 additions & 0 deletions Resources/Private/JavaScript/media-module/tests/tags.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import page from './page-model';
import { ReactSelector } from 'testcafe-react-selectors';
import { SERVER_NAME } from './helpers';

fixture('Tags').page(SERVER_NAME);

const subSection = (name) => console.log('\x1b[33m%s\x1b[0m', ' - ' + name);

test('Clicking first tag updates list and only assets should be shown that are assigned to it', async (t) => {
await t
// Uncollapse the tag list
Expand All @@ -19,3 +22,34 @@ test('Clicking first tag updates list and only assets should be shown that are a
.expect(page.assetCount.innerText)
.eql('12 assets');
});

test('Create a new tag and test validation', async (t) => {
subSection('Check existing tag label validation');
await t
.click(page.assetCollections.withText('All'))
.click(page.collectionTree.findReact('AddTagButton'))
.typeText(ReactSelector('CreateTagDialog').findReact('TextInput'), 'Example tag 1')
.expect(
ReactSelector('CreateTagDialog')
.findReact('TextInput')
.withProps({ validationerrors: ['This input is invalid'] }).exists
)
.ok('Text input should have validation errors')
.expect(ReactSelector('CreateTagDialog').findReact('Button').withProps({ disabled: true }).exists)
.ok('Create button should be disabled')
.expect(ReactSelector('CreateTagDialog').find('ul li').textContent)
.eql('A tag with this label already exists')
.typeText(ReactSelector('CreateTagDialog').findReact('TextInput'), '00')
.expect(ReactSelector('CreateTagDialog').find('ul li').exists)
.notOk('The tooltip should not be visible anymore')
.expect(ReactSelector('CreateTagDialog').findReact('Button').withProps({ disabled: false }).exists)
.ok('Create button should be enabled');

subSection('Check empty tag label validation');
await t
.typeText(ReactSelector('CreateTagDialog').findReact('TextInput'), ' ', { replace: true })
.expect(ReactSelector('CreateTagDialog').findReact('Button').withProps({ disabled: true }).exists)
.ok('Create button should be disabled')
.expect(ReactSelector('CreateTagDialog').find('ul li').textContent)
.eql('Please provide a tag label');
});
10 changes: 9 additions & 1 deletion Resources/Private/Translations/de/Main.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,14 @@
<source>Create tag</source>
<target>Tag erstellen</target>
</trans-unit>
<trans-unit id="tagActions.validation.emptyTagLabel" xml:space="preserve" approved="yes">
<source>Please provide a tag label</source>
<target>Bitte geben Sie einen Tag-Namen ein</target>
</trans-unit>
<trans-unit id="tagActions.validation.tagExists" xml:space="preserve" approved="yes">
<source>This tag already exists. Please choose a different one.</source>
<target>Dieser Tag existiert bereits. Bitte wählen Sie einen anderen aus.</target>
</trans-unit>
<trans-unit id="tagActions.create.success" xml:space="preserve" approved="yes">
<source>Tag was created</source>
<target>Tag wurde erstellt</target>
Expand Down Expand Up @@ -876,7 +884,7 @@
<target>Tag-Erstellung fehlgeschlagen</target>
</trans-unit>
<trans-unit id="errors.1603921233.message" xml:space="preserve" approved="yes">
<source>This tag is already exists. Please choose a different one.</source>
<source>This tag already exists. Please choose a different one.</source>
<target>Dieser Tag existiert bereits. Bitte wählen Sie einen anderen aus.</target>
</trans-unit>
<trans-unit id="errors.1509632861.message" xml:space="preserve" approved="yes">
Expand Down
8 changes: 7 additions & 1 deletion Resources/Private/Translations/en/Main.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@
<trans-unit id="tagActions.create.success" xml:space="preserve" approved="yes">
<source>Tag was created</source>
</trans-unit>
<trans-unit id="tagActions.validation.emptyTagLabel" xml:space="preserve" approved="yes">
<source>Please provide a tag label</source>
</trans-unit>
<trans-unit id="tagActions.validation.tagExists" xml:space="preserve" approved="yes">
<source>This tag already exists. Please choose a different one.</source>
</trans-unit>
<trans-unit id="tagActions.create.error" xml:space="preserve" approved="yes">
<source>Failed to create tag</source>
</trans-unit>
Expand Down Expand Up @@ -670,7 +676,7 @@
<source>Failed to create tag</source>
</trans-unit>
<trans-unit id="errors.1603921233.message" xml:space="preserve" approved="yes">
<source>This tag is already exists. Please choose a different one.</source>
<source>This tag already exists. Please choose a different one.</source>
</trans-unit>
<trans-unit id="errors.1509632861.message" xml:space="preserve" approved="yes">
<source>The specified asset was not found.</source>
Expand Down

0 comments on commit 2f211ca

Please sign in to comment.