Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Adds validation for create tag dialog #241

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 { 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 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 @@
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 @@
key="upload"
style="success"
hoverStyle="success"
disabled={!createPossible}
disabled={!dialogState.validation?.valid}
onClick={handleCreate}
>
{translate('general.create', 'Create')}
Expand All @@ -58,11 +94,22 @@
<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
Loading