chore: lint-format-docs-tests-bugfixes (#105)

* docs: add jsdoc

* feat: change enums to as const objects

* chore(test): add themeColors.test.ts

* fix: fix tests and bugs with strings.ts util

* fix: path alias imports and tsconfig file bug

* fix: remove --max-warnings 0
This commit is contained in:
doprz
2024-02-22 22:42:58 -06:00
parent 8ab60c9f01
commit 8a6e9070e0
134 changed files with 986 additions and 623 deletions

View File

@@ -1,11 +1,19 @@
import { theme } from 'unocss/preset-mini';
/**
* Represents the colors for a course.
*/
export interface CourseColors {
primaryColor: string;
secondaryColor: string;
}
// calculates luminance of a hex string
/**
* Calculates the luminance of a given hexadecimal color.
*
* @param hex - The hexadecimal color value.
* @returns The luminance value between 0 and 1.
*/
export function getLuminance(hex: string): number {
let r = parseInt(hex.substring(1, 3), 16);
let g = parseInt(hex.substring(3, 5), 16);

View File

@@ -1,15 +1,18 @@
import React, { SVGProps } from 'react';
import type { StatusType } from '@shared/types/Course';
import { Status } from '@shared/types/Course';
import type { SVGProps } from 'react';
import React from 'react';
import ClosedIcon from '~icons/material-symbols/lock';
import WaitlistIcon from '~icons/material-symbols/timelapse';
import CancelledIcon from '~icons/material-symbols/warning';
import { Status } from '../types/Course';
/**
* Get Icon component based on status
* @param props.status status
* @returns React.ReactElement - the icon component
*/
export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: Status }): React.ReactElement {
export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: StatusType }): React.ReactElement {
const { status, ...rest } = props;
switch (props.status) {

View File

@@ -18,6 +18,8 @@ export function capitalize(input: string): string {
}
capitalized += ' ';
}
capitalized = capitalized.trim(); // Remove extra space
return capitalized;
}
@@ -31,7 +33,7 @@ export function capitalizeFirstLetter(input: string): string {
}
/**
* Cuts the
* Cuts the input string to the specified length and adds an ellipsis if the string is longer than the specified length.
* @param input The string to ellipsify.
* @param length The length of the string to return.
* @returns The ellipsified string.

View File

@@ -1,17 +1,17 @@
import { describe, expect, it } from 'vitest';
import { capitalize } from '../string';
import { capitalize, capitalizeFirstLetter, ellipsify } from '../string';
// TODO: Fix `string.ts` and `string.test.ts` to make the tests pass
// `capitalize` is adding an extra space at the end of the word.
describe('capitalize', () => {
it('should capitalize the first letter of each word', () => {
// Debug
const word = 'hello world';
const capitalized = capitalize(word);
console.log(capitalize(word));
console.log(capitalized.length);
console.log(capitalized.split(''));
// const word = 'hello world';
// const capitalized = capitalize(word);
// console.log(capitalize(word));
// console.log(capitalized.length);
// console.log(capitalized.split(''));
// Test case 1: Single word
expect(capitalize('hello')).toBe('Hello');
@@ -25,15 +25,40 @@ describe('capitalize', () => {
// Test case 4: Words with hyphens and spaces
expect(capitalize('hello-world test')).toBe('Hello-World Test');
});
});
it('should not change the capitalization of the remaining letters', () => {
// Test case 1: All lowercase
expect(capitalize('hello')).toBe('Hello');
describe('capitalizeFirstLetter', () => {
it('should return a string with the first letter capitalized', () => {
// Test case 1: Single word
expect(capitalizeFirstLetter('hello')).toBe('Hello');
// Test case 2: All uppercase
expect(capitalize('WORLD')).toBe('WORLD');
// Test case 2: Word with all lowercase letters
expect(capitalizeFirstLetter('world')).toBe('World');
// Test case 3: Mixed case
expect(capitalize('HeLLo WoRLd')).toBe('Hello World');
// Test case 3: Word with all uppercase letters
expect(capitalizeFirstLetter('EXAMPLE')).toBe('Example');
// Test case 4: Word with mixed case letters
expect(capitalizeFirstLetter('tEsT')).toBe('Test');
});
it('should handle empty string input', () => {
expect(capitalizeFirstLetter('')).toBe('');
});
});
describe('ellipsify', () => {
it('should add ellipsis if the input string exceeds the specified character limit', () => {
// Test case 1: Input string is shorter than the character limit
expect(ellipsify('Hello', 10)).toBe('Hello');
// Test case 2: Input string is equal to the character limit
expect(ellipsify('Hello World', 11)).toBe('Hello World');
// Test case 3: Input string is longer than the character limit
expect(ellipsify('Hello World', 5)).toBe('Hello...');
// Test case 4: Input string is empty
expect(ellipsify('', 5)).toBe('');
});
});

View File

@@ -0,0 +1,51 @@
import { describe, expect, it } from 'vitest';
import { getThemeColorHexByName, getThemeColorRgbByName, hexToRgb } from '../themeColors';
describe('hexToRgb', () => {
it('should convert hex color to RGB', () => {
expect(hexToRgb('#BF5700')).toEqual([191, 87, 0]);
expect(hexToRgb('#333F48')).toEqual([51, 63, 72]);
expect(hexToRgb('#f8971f')).toEqual([248, 151, 31]);
expect(hexToRgb('#ffd600')).toEqual([255, 214, 0]);
expect(hexToRgb('#a6cd57')).toEqual([166, 205, 87]);
expect(hexToRgb('#579d42')).toEqual([87, 157, 66]);
expect(hexToRgb('#00a9b7')).toEqual([0, 169, 183]);
expect(hexToRgb('#005f86')).toEqual([0, 95, 134]);
expect(hexToRgb('#9cadb7')).toEqual([156, 173, 183]);
expect(hexToRgb('#d6d2c4')).toEqual([214, 210, 196]);
expect(hexToRgb('#95a5a6')).toEqual([149, 165, 166]);
expect(hexToRgb('#B91C1C')).toEqual([185, 28, 28]);
expect(hexToRgb('#af2e2d')).toEqual([175, 46, 45]);
expect(hexToRgb('#1a2024')).toEqual([26, 32, 36]);
expect(hexToRgb('#22c55e')).toEqual([34, 197, 94]);
expect(hexToRgb('#a3e635')).toEqual([163, 230, 53]);
expect(hexToRgb('#84CC16')).toEqual([132, 204, 22]);
expect(hexToRgb('#FDE047')).toEqual([253, 224, 71]);
expect(hexToRgb('#FACC15')).toEqual([250, 204, 21]);
expect(hexToRgb('#F59E0B')).toEqual([245, 158, 11]);
expect(hexToRgb('#FB923C')).toEqual([251, 146, 60]);
expect(hexToRgb('#F97316')).toEqual([249, 115, 22]);
expect(hexToRgb('#EA580C')).toEqual([234, 88, 12]);
expect(hexToRgb('#DC2626')).toEqual([220, 38, 38]);
expect(hexToRgb('#B91C1C')).toEqual([185, 28, 28]);
});
});
describe('getThemeColorHexByName', () => {
it('should return the hex color value by name', () => {
expect(getThemeColorHexByName('ut-burntorange')).toEqual('#BF5700');
expect(getThemeColorHexByName('ut-offwhite')).toEqual('#D6D2C4');
expect(getThemeColorHexByName('ut-black')).toEqual('#333F48');
// Add more test cases for other theme color names
});
});
describe('getThemeColorRgbByName', () => {
it('should return the RGB color value by name', () => {
expect(getThemeColorRgbByName('ut-burntorange')).toEqual([191, 87, 0]);
expect(getThemeColorRgbByName('ut-offwhite')).toEqual([214, 210, 196]);
expect(getThemeColorRgbByName('ut-black')).toEqual([51, 63, 72]);
// Add more test cases for other theme color names
});
});

View File

@@ -2,24 +2,24 @@ export const colors = {
ut: {
burntorange: '#BF5700',
black: '#333F48',
orange: '#f8971f',
yellow: '#ffd600',
lightgreen: '#a6cd57',
green: '#579d42',
teal: '#00a9b7',
blue: '#005f86',
gray: '#9cadb7',
offwhite: '#d6d2c4',
concrete: '#95a5a6',
red: '#B91C1C' // Not sure if this should be here, but it's used for remove course, and add course is ut-green
orange: '#F8971F',
yellow: '#FFD600',
lightgreen: '#A6CD57',
green: '#579D42',
teal: '#00A9B7',
blue: '#005F86',
gray: '#9CADB7',
offwhite: '#D6D2C4',
concrete: '#95A5A6',
red: '#B91C1C', // Not sure if this should be here, but it's used for remove course, and add course is ut-green
},
theme: {
red: '#af2e2d',
black: '#1a2024',
red: '#AF2E2D',
black: '#1A2024',
},
gradeDistribution: {
a: '#22c55e',
aminus: '#a3e635',
a: '#22C55E',
aminus: '#A3E635',
bplus: '#84CC16',
b: '#FDE047',
bminus: '#FACC15',
@@ -31,7 +31,7 @@ export const colors = {
dminus: '#B91C1C',
f: '#B91C1C',
},
} as const;
} as const satisfies Record<string, Record<string, string>>;
type NestedKeys<T> = {
[K in keyof T]: T[K] extends Record<string, any> ? `${string & K}-${string & keyof T[K]}` : never;
@@ -42,6 +42,10 @@ type NestedKeys<T> = {
*/
export type ThemeColor = NestedKeys<typeof colors>;
/**
* Flattened colors object.
* @type {Record<ThemeColor, string>}
*/
export const colorsFlattened = Object.entries(colors).reduce(
(acc, [prefix, group]) => {
for (const [name, hex] of Object.entries(group)) {
@@ -52,9 +56,18 @@ export const colorsFlattened = Object.entries(colors).reduce(
{} as Record<ThemeColor, string>
);
const hexToRgb = (hex: string) =>
/**
* Converts a hexadecimal color code to an RGB color array.
* @param hex The hexadecimal color code to convert.
* @returns An array representing the RGB color values.
*/
export const hexToRgb = (hex: string) =>
hex.match(/[0-9a-f]{2}/gi).map(partialHex => parseInt(partialHex, 16)) as [number, number, number];
/**
* Represents the flattened RGB values of the colors.
* @type {Record<ThemeColor, ReturnType<typeof hexToRgb>>}
*/
const colorsFlattenedRgb = Object.fromEntries(
Object.entries(colorsFlattened).map(([name, hex]) => [name, hexToRgb(hex)])
) as Record<ThemeColor, ReturnType<typeof hexToRgb>>;

View File

@@ -5,7 +5,9 @@ export const HOUR = 60 * MINUTE;
export const DAY = 24 * HOUR;
/**
*
* Pauses the execution for the specified number of milliseconds.
* @param milliseconds - The number of milliseconds to sleep.
* @returns A promise that resolves after the specified number of milliseconds.
*/
export const sleep = (milliseconds: number): Promise<void> => new Promise(resolve => setTimeout(resolve, milliseconds));