Skip to content

Commit

Permalink
chore: save all files to remote
Browse files Browse the repository at this point in the history
  • Loading branch information
chessurisme committed Aug 9, 2024
1 parent 2494aa4 commit 8ebdf60
Show file tree
Hide file tree
Showing 27 changed files with 4,287 additions and 1,799 deletions.
5,078 changes: 3,352 additions & 1,726 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"license": "MIT",
"dependencies": {
"eslint": "^9.2.0",
"lucide": "^0.390.0",
"vercel": "^34.1.9",
"webpack": "^5.91.0"
"lucide": "^0.424.0",
"vercel": "^32.3.0",
"webpack-dev-server": "^5.0.4"
},
"devDependencies": {
"@babel/core": "^7.24.4",
Expand All @@ -40,8 +40,11 @@
"jest-environment-jsdom": "^29.7.0",
"mini-css-extract-plugin": "^2.9.0",
"prettier": "^3.2.5",
"sass": "^1.77.8",
"sass-loader": "^16.0.0",
"style-loader": "^4.0.0",
"terser-webpack-plugin": "^5.3.10",
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",
"webpack-merge": "^5.10.0"
Expand Down
8 changes: 2 additions & 6 deletions public/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
const button = document.getElementById('learn-more')

button.addEventListener('click', () => {
window.location.href =
'https://github.com/free-bird-operation-project/quiz-manager'
window.location.href = 'https://github.com/free-bird-operation-project/quiz-manager'
})

const cursor = document.querySelector('.cursor')

document.addEventListener('mousemove', (e) => {
cursor.setAttribute(
'style',
'top: ' + (e.pageY - 10) + 'px; left: ' + (e.pageX - 10) + 'px;'
)
cursor.setAttribute('style', 'top: ' + (e.pageY - 10) + 'px; left: ' + (e.pageX - 10) + 'px;')
})

document.addEventListener('click', () => {
Expand Down
3 changes: 3 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { MainPage } from '@pages/main-page'
import { sanitizeValue } from '@utilities/sanitize-value'
import './styles/themes.scss'
import './styles/reset.scss'
import './utilities/theme'

/**
* Class representing an App.
Expand Down
154 changes: 154 additions & 0 deletions src/components/__tests__/base-component.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { JSDOM } from 'jsdom'
import { BaseComponent } from '../base-component'

const dom = new JSDOM('<!DOCTYPE html><div id="test-element"></div>')
global.window = dom.window
global.document = window.document
global.HTMLElement = window.HTMLElement
global.HTMLParagraphElement = window.HTMLParagraphElement

describe('BaseComponent', () => {
let utility

beforeEach(() => {
utility = new BaseComponent()
})

describe('#createContainer', () => {
it('should create a container element with specified tag and attributes', () => {
const attributes = {
id: 'container-div',
class: 'container',
style: 'color: green;'
}
const container = utility._createContainer('div', attributes)

expect(container).toBeInstanceOf(HTMLElement)
expect(container.tagName).toBe('DIV')
expect(container.getAttribute('id')).toBe('container-div')
expect(container.getAttribute('class')).toBe('container')
expect(container.getAttribute('style')).toBe('color: green;')
})

it('should return null if tagName is not a string', () => {
const container = utility._createContainer(123, { id: 'test-div' })

expect(container).toBeNull()
})

it('should return null if attributes is not an object', () => {
const container = utility._createContainer('div', 'invalid-attributes')

expect(container).toBeNull()
})

it('should return null if both tagName and attributes are invalid', () => {
const container = utility._createContainer(null, null)

expect(container).toBeNull()
})

it('should return null if attributes is an empty object', () => {
const container = utility._createContainer('div', {})

expect(container).toBeInstanceOf(HTMLElement)
expect(container.getAttribute('id')).toBeNull()
expect(container.getAttribute('class')).toBeNull()
expect(container.getAttribute('style')).toBeNull()
})
})

describe('#createIcon', () => {
it('should create an icon element with a specified icon name', () => {
const iconName = 'star'
const iconElement = utility._createIcon(iconName)

expect(iconElement).toBeInstanceOf(HTMLElement)
expect(iconElement.tagName).toBe('I')
expect(iconElement.getAttribute('data-lucide')).toBe(iconName)
})

it('should return null if iconName is not a string', () => {
const iconElement = utility._createIcon(123)

expect(iconElement).toBeNull()
})

it('should return null if iconName is an empty string', () => {
const iconElement = utility._createIcon('')

expect(iconElement).toBeNull()
})

it('should create an icon element with a specified icon name containing special characters', () => {
const iconName = 'star-1'
const iconElement = utility._createIcon(iconName)

expect(iconElement).toBeInstanceOf(HTMLElement)
expect(iconElement.tagName).toBe('I')
expect(iconElement.getAttribute('data-lucide')).toBe(iconName)
})
})

describe('#createText', () => {
it('should create a text element with specified text', () => {
const text = 'Hello, world!'
const textElement = utility._createText(text)

expect(textElement).toBeInstanceOf(HTMLParagraphElement)
expect(textElement.textContent).toBe(text)
})

it('should return null if text is not a string', () => {
const textElement = utility._createText(123)

expect(textElement).toBeNull()
})

it('should return null if text is an empty string', () => {
const textElement = utility._createText('')

expect(textElement).toBeNull()
})

it('should create a text element with a very long string', () => {
const longText = 'a'.repeat(1000)
const textElement = utility._createText(longText)

expect(textElement).toBeInstanceOf(HTMLParagraphElement)
expect(textElement.textContent).toBe(longText)
})
})

describe('#removeById', () => {
it('should remove an element from the DOM by its ID', () => {
const id = 'test-element'
const element = document.getElementById(id)

expect(element).not.toBeNull()

const result = utility._removeById(id)

expect(result).toBe(true)
expect(document.getElementById(id)).toBeNull()
})

it('should return false if the element with the given ID does not exist', () => {
const result = utility._removeById('non-existent-id')

expect(result).toBe(false)
})

it('should return false if the ID is not a string', () => {
const result = utility._removeById(123)

expect(result).toBe(false)
})

it('should return false if the ID is an empty string', () => {
const result = utility._removeById('')

expect(result).toBe(false)
})
})
})
101 changes: 101 additions & 0 deletions src/components/base-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { setAttributes } from '@components/utilities/set-attributes'
import { sanitizeValue } from '@utilities/sanitize-value'
import { setProperties } from './utilities/set-properties'

/**
* Creates a base component with utility methods for creating container elements, icons, text elements, and removing elements from the DOM.
* Utilizes the `sanitizeValue` function to validate inputs and the `setAttributes` function to set attributes on elements.
*/
class BaseComponent {
constructor(config) {
this.config = sanitizeValue(config, 'object') || {}
}

/**
* Creates a container element with the specified tag name and attributes.
*
* @param {string} tagName - The tag name for the container element.
* @param {object} attributes - The attributes to be set on the container element.
* @param {object} [booleanAttributes] - The boolean attributes to be set on the container element.
* @returns {HTMLElement | null} The created container element or null if invalid inputs.
*/
_createContainer(tagName, attributes, booleanAttributes) {
const isTagName = sanitizeValue(tagName, 'string')
const isAttributes = sanitizeValue(attributes, 'object')

const container = document.createElement(tagName)

if (booleanAttributes) {
sanitizeValue(booleanAttributes, 'object')
setProperties(container, booleanAttributes)
}

if (!isTagName || !isAttributes) {
return null
}

setAttributes(container, attributes)
return container
}

/**
* Creates an icon element with the specified icon name.
*
* @param {string} iconName - The name of the icon to be displayed.
* @returns {HTMLElement | null} The created icon element or null if the icon name is invalid.
*/
_createIcon(iconName) {
const isIconName = sanitizeValue(iconName, 'string')

if (!isIconName) {
return null
}

const iconElement = document.createElement('i')
setAttributes(iconElement, { 'data-lucide': iconName })
return iconElement
}

/**
* Creates a text element with the specified text content.
*
* @param {string} text - The text content for the text element.
* @returns {HTMLParagraphElement | null} The created text element or null if the text content is invalid.
*/
_createText(text) {
const isText = sanitizeValue(text, 'string')

if (!isText) {
return null
}

const textElement = document.createElement('p')
textElement.textContent = text
return textElement
}

/**
* Removes an element from the DOM by its ID.
*
* @param {string} id - The ID of the element to be removed.
* @returns {boolean} Returns true if the element is successfully removed, false otherwise.
*/
_removeById(id) {
const isId = sanitizeValue(id, 'string')

if (!isId) {
return false
}

const element = document.getElementById(id)

if (!element) {
return false
}

element.remove()
return true
}
}

export { BaseComponent }
73 changes: 73 additions & 0 deletions src/components/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import './styles/button.scss'
import { BaseComponent } from './base-component'
import { sanitizeValue } from '@utilities/sanitize-value'
import { addEventListeners } from './utilities/add-event-listeners'
import { removeEventListeners } from './utilities/remove-event-listeners'

/**
* @typedef {Object} ButtonConfig
* @property {string} [id] - The ID of the button. If not provided, a default ID will be used.
* @property {string} [className] - The class name(s) to be applied to the button.
* @property {string} [icon] - The name of the icon to be displayed on the button.
* @property {string} [text] - The text content of the button.
* @property {string} [type='transparent'] - The type of the button, which affects its styling. Default is 'transparent'.
* @property {Array<{type: string, func: Function}>} [events] - An array of event listener objects with `type` and `func` properties.
*/

/**
* Represents a Button component.
*/
class Button extends BaseComponent {
/**
* Initializes the button instance with the provided configuration.
*
* @param {ButtonConfig} config - The configuration object for the button.
*/
constructor(config) {
super(config)
this.config = sanitizeValue(config, 'object')
}

/**
* Creates and returns the button element based on the provided configuration.
* If either an icon or text is present in the configuration, it adds them to the button.
* Attaches event listeners to the button based on the events provided in the configuration.
*
* @returns {Element|null} The created button element or null if neither icon nor text is provided.
*/
create() {
const { className, events, icon, id, text, type = 'transparent' } = this.config
const button = this._createContainer('div', {
id: `button-${id}`,
class: `${className} button ${type}-button`
})

if (!icon && !text) return null
if (icon) button.appendChild(this._createIcon(icon))
if (text) button.appendChild(this._createText(text))
addEventListeners(button, events)

return button
}

/**
* Removes the button element from the DOM along with its event listeners based on the provided configuration.
*
* @returns {boolean} Returns true if the button element is successfully removed, otherwise false.
*/
remove() {
const { events, id } = this.config
const elementId = `button-${id}`
const button = document.getElementById(elementId)

if (button) {
removeEventListeners(button, events)
button.remove()
return true
}

return false
}
}

export { Button }
Loading

0 comments on commit 8ebdf60

Please sign in to comment.