Skip to content

Commit

Permalink
feat: form validation
Browse files Browse the repository at this point in the history
  • Loading branch information
mikhailmogilnikov committed Jan 9, 2024
1 parent a3b7743 commit f1e8737
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 22 deletions.
79 changes: 75 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
"@popperjs/core": "^2.11.8",
"ajv": "^8.12.0",
"bootstrap": "^5.3.2",
"npm-check-updates": "^16.6.2"
"i18next": "^23.7.16",
"npm-check-updates": "^16.6.2",
"on-change": "^5.0.0",
"yup": "^1.3.3"
}
}
12 changes: 0 additions & 12 deletions src/Example.js

This file was deleted.

45 changes: 42 additions & 3 deletions src/init.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,45 @@
// @ts-check
import * as yup from 'yup';
import view from './scripts/view.js';
import strings from './utils/strings.js';
import { normalizeUrl } from './scripts/utilities.js';
import domElements from './utils/domElements.js';

const schema = yup.string().required('this is a required field').url();

export default () => {
const element = document.getElementById('body');
console.log(element);
const state = {
formState: strings.formStates.init,
feedList: [],
feedback: null,
};

const watchedState = view(state);

domElements.form.submit.addEventListener('click', (e) => {
e.preventDefault();

const normalizedValue = normalizeUrl(domElements.form.input.value);

if (state.feedList.includes(normalizedValue)) {
watchedState.formState = strings.formStates.invalid;
watchedState.feedback = strings.feedback.exists;
} else {
schema
.validate(normalizedValue)
.then(() => {
watchedState.formState = strings.formStates.sending;

watchedState.feedList.push(domElements.form.input?.value);
watchedState.feedback = strings.feedback.loaded;
watchedState.formState = strings.formStates.init;

domElements.form.input.value = '';
domElements.form.input?.focus();
})
.catch(() => {
watchedState.formState = strings.formStates.invalid;
watchedState.feedback = strings.feedback.invalidValidation;
});
}
});
};
5 changes: 5 additions & 0 deletions src/scripts/utilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const normalizeUrl = (url) => url.trim().toLowerCase();

const normalizeError = (error) => error.toString().split(': ')[1];

export { normalizeUrl, normalizeError };
68 changes: 68 additions & 0 deletions src/scripts/view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// @ts-nocheck
import onChange from 'on-change';
import strings from '../utils/strings.js';
import domElements from '../utils/domElements.js';

const renderFormState = (value) => {
if (value === strings.formStates.sending) {
domElements.form.submit?.setAttribute('disabled', '');
domElements.form.input?.setAttribute('disabled', '');
} else {
domElements.form.submit?.removeAttribute('disabled');
domElements.form.input?.removeAttribute('disabled');
}

if (value === strings.formStates.invalid) {
domElements.form.input?.classList.add('is-invalid');
} else {
domElements.form.input?.classList.remove('is-invalid');
}
};

const renderFeedback = (value) => {
switch (value) {
case 'invalidValidation':
domElements.form.feedback.textContent = 'Ссылка должна быть валидным URL';
break;
case 'invalidRss':
domElements.form.feedback.textContent = 'Ресурс не содержит валидный RSS';
break;
case 'exists':
domElements.form.feedback.textContent = 'RSS уже существует';
break;
case 'loaded':
domElements.form.feedback.textContent = 'RSS успешно загружен';
break;
case null:
domElements.form.feedback.textContent = '';
break;
default:
throw new Error(value);
}

if (value === 'loaded') {
domElements.form.feedback.classList.replace('text-danger', 'text-success');
} else {
domElements.form.feedback.classList.replace('text-success', 'text-danger');
}
};

const renderFeedList = () => {};

const render = () => (path, value) => {
switch (path) {
case 'formState':
renderFormState(value);
break;
case 'feedback':
renderFeedback(value);
break;
case 'feedList':
renderFeedList();
break;
default:
throw new Error(`Error: unresolved path: ${path}`);
}
};

export default (state) => onChange(state, render());
7 changes: 7 additions & 0 deletions src/utils/domElements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
form: {
input: document.getElementById('url-input'),
submit: document.querySelector('button[type="submit"]'),
feedback: document.getElementById('form-feedback'),
},
};
14 changes: 14 additions & 0 deletions src/utils/strings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default {
formStates: {
init: 'init',
invalid: 'invalid',
sending: 'sending',
valid: 'valid',
},
feedback: {
invalidValidation: 'invalidValidation',
invalidRss: 'invalidRss',
exists: 'exists',
loaded: 'loaded',
},
};
4 changes: 2 additions & 2 deletions template.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ <h1 class="display-3 mb-0">RSS агрегатор</h1>
</div>
</div>
</form>
<p class="mt-2 mb-0 text-muted">
<p class="mt-2 mb-0 text-white-50">
Пример: https://lorem-rss.hexlet.app/feed
</p>
<p class="feedback m-0 position-absolute small text-danger"></p>
<p id="form-feedback" class="feedback m-0 position-absolute small text-danger"></p>
</div>
</div>
</section>
Expand Down

0 comments on commit f1e8737

Please sign in to comment.