-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2494aa4
commit 8ebdf60
Showing
27 changed files
with
4,287 additions
and
1,799 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } |
Oops, something went wrong.