accessible-menu
Version:
A JavaScript library to help you generate WCAG accessible menus in the DOM.
367 lines (329 loc) • 9.55 kB
JavaScript
/**
* Check to see if the provided elements have a specific contructor.
*
* The values must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* This is essentially just a wrapper function around checking instanceof with
* more descriptive error message to help debugging.
*
* Will return `{ status: true }` if the check is successful.
*
* @param {object} contructor - The constructor to check for.
* @param {object} elements - The element(s) to check.
* @return {Object<boolean, string>} - The result of the check.
*/
export function isValidInstance(contructor, elements) {
try {
if (typeof elements !== "object") {
const elementsType = typeof elements;
throw new TypeError(
`Elements given to isValidInstance() must be inside of an object. "${elementsType}" given.`
);
}
for (const key in elements) {
if (!(elements[key] instanceof contructor)) {
const elementType = typeof elements[key];
throw new TypeError(
`${key} must be an instance of ${contructor.name}. "${elementType}" given.`
);
}
}
return {
status: true,
error: null,
};
} catch (error) {
return {
status: false,
error,
};
}
}
/**
* Check to see if the provided values are of a specific type.
*
* The values must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* This is essentially just a wrapper function around checking typeof with
* more descriptive error message to help debugging.
*
* Will return `{ status: true }` if the check is successful.
*
* @param {string} type - The type to check for.
* @param {object} values - The value(s) to check.
* @return {Object<boolean, string>} - The result of the check.
*/
export function isValidType(type, values) {
try {
if (typeof values !== "object") {
const valuesType = typeof values;
throw new TypeError(
`Values given to isValidType() must be inside of an object. "${valuesType}" given.`
);
}
for (const key in values) {
const valueType = typeof values[key];
if (valueType !== type) {
throw new TypeError(`${key} must be a ${type}. "${valueType}" given.`);
}
}
return {
status: true,
error: null,
};
} catch (error) {
return {
status: false,
error,
};
}
}
/**
* Checks to see if the provided values are valid query selectors.
*
* The values must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* Will return `{ status: true }` if the check is successful.
*
* @param {Object<string>} values - The value(s) to check.
* @return {Object<boolean, string>} - The result of the check.
*/
export function isQuerySelector(values) {
try {
if (typeof values !== "object") {
const type = typeof values;
throw new TypeError(
`Values given to isQuerySelector() must be inside of an object. "${type}" given.`
);
}
for (const key in values) {
try {
if (values[key] === null) {
throw new Error();
}
document.querySelector(values[key]);
} catch {
throw new TypeError(
`${key} must be a valid query selector. "${values[key]}" given.`
);
}
}
return {
status: true,
error: null,
};
} catch (error) {
return {
status: false,
error,
};
}
}
/**
* Checks to see if the provided value is either a string or an array of strings.
*
* The values must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* Will return `{ status: true }` if the check is successful.
*
* @param {Object<string, string[]>} values - The value(s) to check.
* @return {Object<boolean, string>} - The result of the check.
*/
export function isValidClassList(values) {
try {
if (typeof values !== "object" || Array.isArray(values)) {
const type = typeof values;
throw new TypeError(
`Values given to isValidClassList() must be inside of an object. "${type}" given.`
);
}
for (const key in values) {
const type = typeof values[key];
if (type !== "string") {
if (Array.isArray(values[key])) {
values[key].forEach((value) => {
if (typeof value !== "string") {
throw new TypeError(
`${key} must be a string or an array of strings. An array containing non-strings given.`
);
}
});
} else {
throw new TypeError(
`${key} must be a string or an array of strings. "${type}" given.`
);
}
} else {
const obj = {};
obj[key] = values[key];
isQuerySelector(obj);
}
}
return {
status: true,
error: null,
};
} catch (error) {
return {
status: false,
error,
};
}
}
/**
* Check to see if the provided values are valid focus states for a menu.
*
* Available states are: `"none"`, `"self"`, and `"child"`.
*
* The values must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* Will return `{ status: true }` if the check is successful.
*
* @param {Object<string>} values - The value(s) to check.
* @return {Object<boolean, string>} - The result of the check.
*/
export function isValidState(values) {
try {
if (typeof values !== "object") {
const type = typeof values;
throw new TypeError(
`Values given to isValidState() must be inside of an object. "${type}" given.`
);
}
const validStates = ["none", "self", "child"];
for (const key in values) {
if (!validStates.includes(values[key])) {
throw new TypeError(
`${key} must be one of the following values: ${validStates.join(
", "
)}. "${values[key]}" given.`
);
}
}
return {
status: true,
error: null,
};
} catch (error) {
return {
status: false,
error,
};
}
}
/**
* Check to see if the provided values are valid event types for a menu.
*
* Available events are: `"none"`, `"mouse"`, `"keyboard"`, and `"character"`.
*
* The values must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* Will return `{ status: true }` if the check is successful.
*
* @param {Object<string>} values - The value(s) to check.
* @return {Object<boolean, string>} - The result of the check.
*/
export function isValidEvent(values) {
try {
if (typeof values !== "object") {
const type = typeof values;
throw new TypeError(
`Values given to isValidEvent() must be inside of an object. "${type}" given.`
);
}
const validEvents = ["none", "mouse", "keyboard", "character"];
for (const key in values) {
if (!validEvents.includes(values[key])) {
throw new TypeError(
`${key} must be one of the following values: ${validEvents.join(
", "
)}. "${values[key]}" given.`
);
}
}
return {
status: true,
error: null,
};
} catch (error) {
return {
status: false,
error,
};
}
}
/**
* Check to see if the provided values are valid hover types for a menu.
*
* Available types are: `"off"`, `"on"`, and `"dynamic"`.
*
* The values must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* Will return `{ status: true }` if the check is successful.
*
* @param {Object<string>} values - The value(s) to check.
* @return {Object<boolean, string>} - The result of the check.
*/
export function isValidHoverType(values) {
try {
if (typeof values !== "object") {
const type = typeof values;
throw new TypeError(
`Values given to isValidHoverType() must be inside of an object. "${type}" given.`
);
}
const validTypes = ["off", "on", "dynamic"];
for (const key in values) {
if (!validTypes.includes(values[key])) {
throw new TypeError(
`${key} must be one of the following values: ${validTypes.join(
", "
)}. "${values[key]}" given.`
);
}
}
return {
status: true,
error: null,
};
} catch (error) {
return {
status: false,
error,
};
}
}
/**
* Checks to see if the provided elements are using a specific tag.
*
* The elements must be provided inside of an object
* so the variable name can be retrieved in case of errors.
*
* Will return `true` if the check is successful.
*
* @param {string} tagName - The name of the tag.
* @param {Object<HTMLElement>} elements - The element(s) to check.
* @return {boolean} - The result of the check.
*/
export function isTag(tagName, elements) {
if (
isValidType("string", { tagName }).status &&
isValidInstance(HTMLElement, elements).status
) {
const tag = tagName.toLowerCase();
let check = true;
for (const key in elements) {
if (elements[key].tagName.toLowerCase() !== tag) check = false;
}
return check;
} else {
return false;
}
}