Skip to content
Open
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
9 changes: 9 additions & 0 deletions projects/dnd/dnd.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<style>
.draggable-div {
position: absolute;
}
</style>

<div id="app">
<button id="addDiv">добавить div</button>
</div>
90 changes: 90 additions & 0 deletions projects/dnd/functions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* ДЗ 6 - DOM Events */

/*
Задание 1:

Функция должна добавлять обработчик fn события eventName к элементу target

Пример:
addListener('click', document.querySelector('a'), () => console.log('...')) // должна добавить указанный обработчик кликов на указанный элемент
*/
function addListener(eventName, target, fn) {
target.addEventListener(eventName, fn);
}

/*
Задание 2:

Функция должна удалять у элемента target обработчик fn события eventName

Пример:
removeListener('click', document.querySelector('a'), someHandler) // должна удалить указанный обработчик кликов на указанный элемент
*/
function removeListener(eventName, target, fn) {
target.removeEventListener(eventName, fn);
}

/*
Задание 3:

Функция должна добавить к элементу target такой обработчик на события eventName, чтобы он отменял действия по умолчанию

Пример:
skipDefault('click', document.querySelector('a')) // после вызова функции, клики на указанную ссылку не должны приводить к переходу на другую страницу
*/
function skipDefault(eventName, target) {
target.addEventListener(eventName, (e) => {
e.preventDefault();
});
}

/*
Задание 4:

Функция должна эмулировать событие click для элемента target

Пример:
emulateClick(document.querySelector('a')) // для указанного элемента должно быть симулировано события click
*/
function emulateClick(target) {
const event = new Event('click');
target.dispatchEvent(event);
}

/*
Задание 6:

Функция должна добавить такой обработчик кликов к элементу target,
который реагирует (вызывает fn) только на клики по элементам BUTTON внутри target

Пример:
delegate(document.body, () => console.log('кликнули на button')) // добавит такой обработчик кликов для body, который будет вызывать указанную функцию только если кликнули на кнопку (элемент с тегом button)
*/
function delegate(target, fn) {
target.addEventListener('click', (e) => {
if (e.target.tagName === 'BUTTON') {
fn();
}
});
}

/*
Задание 7:

Функция должна добавить такой обработчик кликов к элементу target,
который сработает только один раз и удалится (перестанет срабатывать для последующих кликов по указанному элементу)

Пример:
once(document.querySelector('button'), () => console.log('обработчик выполнился!')) // добавит такой обработчик кликов для указанного элемента, который вызовется только один раз и затем удалится
*/
function once(target, fn) {
let done = false;
target.addEventListener('click', () => {
if (!done) {
fn();
done = true;
}
});
}

export { addListener, removeListener, skipDefault, emulateClick, delegate, once };
106 changes: 106 additions & 0 deletions projects/dnd/functions.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
addListener,
delegate,
emulateClick,
once,
removeListener,
skipDefault,
} from './functions';

describe('ДЗ 5.1 - DOM Events', () => {
describe('addListener', () => {
it('должна добавлять обработчик событий элемента', () => {
const target = document.createElement('div');
const eventName = 'click';
const fn = jest.fn();

addListener(eventName, target, fn);

target.dispatchEvent(new CustomEvent(eventName));
expect(fn).toBeCalled();
});
});

describe('removeListener', () => {
it('должна удалять обработчик событий элемента', () => {
const target = document.createElement('div');
const eventName = 'click';
const fn = jest.fn();

target.addEventListener(eventName, fn);

removeListener(eventName, target, fn);

target.dispatchEvent(new CustomEvent(eventName));
expect(fn).not.toBeCalled();
});
});

describe('skipDefault', () => {
it('должна добавлять такой обработчик, который предотвращает действие по умолчанию', () => {
const target = document.createElement('div');
const eventName = 'click';

skipDefault(eventName, target);

const result = target.dispatchEvent(
new CustomEvent(eventName, { cancelable: true })
);
expect(!result);
});
});

describe('emulateClick', () => {
it('должна эмулировать клик по элементу', () => {
const target = document.createElement('div');
const eventName = 'click';
const fn = jest.fn();

target.addEventListener(eventName, fn);

emulateClick(target);

expect(fn).toBeCalled();
});
});

describe('delegate', () => {
it('должна добавлять обработчик кликов, который реагирует только на клики по кнопкам', () => {
const target = document.createElement('div');
const eventName = 'click';
const fn = jest.fn();

target.innerHTML = '<div></div><a href="#"></a><p></p><button></button>';

delegate(target, fn);

expect(fn).not.toBeCalled();
target.children[0].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
expect(fn).not.toBeCalled();
target.children[1].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
expect(fn).not.toBeCalled();
target.children[2].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
expect(fn).not.toBeCalled();
target.children[3].dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
expect(fn).toBeCalled();
});
});

describe('once', () => {
it('должна добавлять обработчик кликов, который сработает только один раз и удалится', () => {
const target = document.createElement('div');
const eventName = 'click';
let passed = 0;
const fn = () => passed++;

once(target, fn);

expect(passed).toBe(0);
target.dispatchEvent(new CustomEvent(eventName));
expect(passed).toBe(1);
target.dispatchEvent(new CustomEvent(eventName));
expect(passed).toBe(1);
target.dispatchEvent(new CustomEvent(eventName));
});
});
});
64 changes: 64 additions & 0 deletions projects/dnd/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Задание со звездочкой */

/*
Создайте страницу с кнопкой.
При нажатии на кнопку должен создаваться div со случайными размерами, цветом и позицией на экране
Необходимо предоставить возможность перетаскивать созданные div при помощи drag and drop
Запрещено использовать сторонние библиотеки. Разрешено пользоваться только тем, что встроено в браузер
*/

/*
homeworkContainer - это контейнер для всех ваших домашних заданий
Если вы создаете новые html-элементы и добавляете их на страницу, то добавляйте их только в этот контейнер

Пример:
const newDiv = document.createElement('div');
homeworkContainer.appendChild(newDiv);
*/
import './dnd.html';

const homeworkContainer = document.querySelector('#app');

function random(from, to) {
return parseInt(from + Math.random() * to - from);
}

let isDrag;
let firstpos_x = 0;
let firstpos_y = 0;

document.addEventListener('mousemove', (e) => {
if (isDrag) {
isDrag.style.top = e.clientY - firstpos_x + 'px';
isDrag.style.left = e.clientX - firstpos_y + 'px';
}
});

export function createDiv() {
const div = document.createElement('div');
const minSize = 20;
const maxSize = 200;
const maxColor = 0xffffff;

div.className = 'draggable-div';
div.style.background = '#' + random(0, maxColor).toString(16);
div.style.top = random(0, window.innerHeight) + 'px';
div.style.left = random(0, window.innerWidth) + 'px';
div.style.width = random(minSize, maxSize) + 'px';
div.style.height = random(minSize, maxSize) + 'px';

div.addEventListener('mousedown', (e) => {
isDrag = div;
firstpos_x = e.offsetX;
firstpos_y = e.offsetY;
});
div.addEventListener('mouseup', () => (isDrag = false));
return div;
}

const addDivButton = homeworkContainer.querySelector('#addDiv');

addDivButton.addEventListener('click', function () {
const div = createDiv();
homeworkContainer.append(div);
});
38 changes: 38 additions & 0 deletions projects/dnd/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
describe('ДЗ 5.2 - Div D&D', () => {
const dndPage = require('./index');
const homeworkContainer = document.querySelector('#app');
let addDivButton;

describe('Функциональное тестирование', () => {
describe('createDiv', () => {
it('должна создавать div с произвольными размерами/позицией/цветом', () => {
const result = dndPage.createDiv();

expect(result).toBeInstanceOf(Element);
expect(result.tagName).toBe('DIV');
expect(result.style.backgroundColor || result.style.background).not.toBe('');
expect(result.style.top).not.toBe('');
expect(result.style.left).not.toBe('');
expect(result.style.width).not.toBe('');
expect(result.style.height).not.toBe('');
});
});
});

describe('Интеграционное тестирование', () => {
it('на старнице должна быть кнопка с id addDiv', () => {
addDivButton = homeworkContainer.querySelector('#addDiv');
expect(addDivButton).toBeInstanceOf(Element);
expect(addDivButton.tagName).toBe('BUTTON');
});

it('создавать div с классом draggable-div при клике на кнопку', () => {
const divsCount = homeworkContainer.querySelectorAll('.draggable-div').length;

addDivButton.dispatchEvent(new MouseEvent('click', { view: window }));
const newDivsCount = homeworkContainer.querySelectorAll('.draggable-div').length;

expect(newDivsCount - divsCount).toBe(1);
});
});
});