Skip to content

Commit

Permalink
feat: add modal and drawer components and examples (#4229)
Browse files Browse the repository at this point in the history
* feat: add modal component

* feat: add drawer component

* feat: apply new modal and drawer components to the layout

* chore: typo

* feat: add some unit tests
  • Loading branch information
anncwb authored Aug 25, 2024
1 parent edb55b1 commit 20a3868
Show file tree
Hide file tree
Showing 96 changed files with 2,701 additions and 744 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,10 @@

"i18n-ally.localesPaths": [
"packages/locales/src/langs",
"playground/src/langs",
"playground/src/locales/langs",
"apps/*/src/locales/langs"
],
"i18n-ally.pathMatcher": "{locale}.json",
"i18n-ally.enabledParsers": ["json", "ts", "js", "yaml"],
"i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN",
Expand Down
2 changes: 1 addition & 1 deletion apps/backend-mock/utils/jwt-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface UserPayload extends UserInfo {
}

export function generateAccessToken(user: UserInfo) {
return jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '1d' });
return jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '7d' });
}

export function generateRefreshToken(user: UserInfo) {
Expand Down
6 changes: 5 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"astro",
"ui-kit",
"styl",
"vnode",
"nocheck",
"prefixs",
"vitepress",
Expand All @@ -53,6 +54,9 @@
"**/*-dist/**",
"**/icons/**",
"pnpm-lock.yaml",
"**/*.log"
"**/*.log",
"**/*.test.ts",
"**/*.spec.ts",
"**/__tests__/**"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';

import { describe, expect, it } from 'vitest';

import { generatorContentHash } from './hash';
import { generatorContentHash } from '../hash';

describe('generatorContentHash', () => {
it('should generate an MD5 hash for the content', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { describe, expect, it } from 'vitest';

import { toPosixPath } from './path';
import { toPosixPath } from '../path';

describe('toPosixPath', () => {
// 测试 Windows 风格路径到 POSIX 风格路径的转换
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@
transition: all 0.6s ease-out;
}

.loading .dots {
display: flex;
align-items: center;
justify-content: center;
padding: 98px;
}

.loading .title {
margin-top: 36px;
font-size: 30px;
Expand Down Expand Up @@ -109,6 +102,6 @@
}
</style>
<div class="loading" id="__app-loading__">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
<span class="dot"><i></i><i></i><i></i><i></i></span>
<div class="title"><%= VITE_APP_TITLE %></div>
</div>
3 changes: 3 additions & 0 deletions packages/@core/base/icons/src/lucide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ export {
CornerDownLeft,
Disc as IconDefault,
Ellipsis,
Expand,
ExternalLink,
Eye,
EyeOff,
FoldHorizontal,
Fullscreen,
Github,
Info,
InspectionPanel,
Languages,
LoaderCircle,
Expand All @@ -46,6 +48,7 @@ export {
Search,
SearchX,
Settings,
Shrink,
Sun,
SunMoon,
SwatchBook,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { StorageManager } from './storage-manager';
import { StorageManager } from '../storage-manager';

describe('storageManager', () => {
let storageManager: StorageManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
convertToHslCssVar,
convertToRgb,
isValidColor,
} from './convert';
} from '../convert';

describe('color conversion functions', () => {
it('should correctly convert color to HSL format', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';

import { diff } from './diff';
import { diff } from '../diff';

describe('diff function', () => {
it('should return an empty object when comparing identical objects', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { getElementVisibleRect } from './dom'; // 假设函数位于 utils.ts 中
import { getElementVisibleRect } from '../dom'; // 假设函数位于 utils.ts 中

describe('getElementVisibleRect', () => {
// 设置浏览器视口尺寸的 mock
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { describe, expect, it } from 'vitest';

import {
getFirstNonNullOrUndefined,
isEmpty,
isHttpUrl,
isObject,
isUndefined,
isWindow,
} from './inference';
} from '../inference';

describe('isHttpUrl', () => {
it("should return true when given 'http://example.com'", () => {
Expand Down Expand Up @@ -103,7 +104,6 @@ describe('isObject', () => {

it('should return false for non-objects', () => {
expect(isObject(null)).toBe(false);
expect(isObject()).toBe(false);
expect(isObject(42)).toBe(false);
expect(isObject('string')).toBe(false);
expect(isObject(true)).toBe(false);
Expand All @@ -112,3 +112,56 @@ describe('isObject', () => {
expect(isObject(/regex/)).toBe(true);
});
});

describe('getFirstNonNullOrUndefined', () => {
describe('getFirstNonNullOrUndefined', () => {
it('should return the first non-null and non-undefined value for a number array', () => {
expect(getFirstNonNullOrUndefined<number>(undefined, null, 0, 42)).toBe(
0,
);
expect(getFirstNonNullOrUndefined<number>(null, undefined, 42, 123)).toBe(
42,
);
});

it('should return the first non-null and non-undefined value for a string array', () => {
expect(
getFirstNonNullOrUndefined<string>(undefined, null, '', 'hello'),
).toBe('');
expect(
getFirstNonNullOrUndefined<string>(null, undefined, 'test', 'world'),
).toBe('test');
});

it('should return undefined if all values are null or undefined', () => {
expect(getFirstNonNullOrUndefined(undefined, null)).toBeUndefined();
expect(getFirstNonNullOrUndefined(null)).toBeUndefined();
});

it('should work with a single value', () => {
expect(getFirstNonNullOrUndefined(42)).toBe(42);
expect(getFirstNonNullOrUndefined()).toBeUndefined();
expect(getFirstNonNullOrUndefined(null)).toBeUndefined();
});

it('should handle mixed types correctly', () => {
expect(
getFirstNonNullOrUndefined<number | object | string>(
undefined,
null,
'test',
123,
{ key: 'value' },
),
).toBe('test');
expect(
getFirstNonNullOrUndefined<number | object | string>(
null,
undefined,
[1, 2, 3],
'string',
),
).toEqual([1, 2, 3]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { describe, expect, it } from 'vitest';

import {
capitalizeFirstLetter,
kebabToCamelCase,
toCamelCase,
toLowerCaseFirstLetter,
} from './letter';
} from '../letter';

// 编写测试用例
describe('capitalizeFirstLetter', () => {
Expand Down Expand Up @@ -76,3 +77,41 @@ describe('toCamelCase', () => {
expect(toCamelCase('Child', 'Parent')).toBe('ParentChild');
});
});

describe('kebabToCamelCase', () => {
it('should convert kebab-case to camelCase correctly', () => {
expect(kebabToCamelCase('my-component-name')).toBe('myComponentName');
});

it('should handle multiple consecutive hyphens', () => {
expect(kebabToCamelCase('my--component--name')).toBe('myComponentName');
});

it('should trim leading and trailing hyphens', () => {
expect(kebabToCamelCase('-my-component-name-')).toBe('myComponentName');
});

it('should preserve the case of the first word', () => {
expect(kebabToCamelCase('My-component-name')).toBe('MyComponentName');
});

it('should convert a single word correctly', () => {
expect(kebabToCamelCase('component')).toBe('component');
});

it('should return an empty string if input is empty', () => {
expect(kebabToCamelCase('')).toBe('');
});

it('should handle strings with no hyphens', () => {
expect(kebabToCamelCase('mycomponentname')).toBe('mycomponentname');
});

it('should handle strings with only hyphens', () => {
expect(kebabToCamelCase('---')).toBe('');
});

it('should handle mixed case inputs', () => {
expect(kebabToCamelCase('my-Component-Name')).toBe('myComponentName');
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';

import { filterTree, mapTree, traverseTreeValues } from './tree';
import { filterTree, mapTree, traverseTreeValues } from '../tree';

describe('traverseTreeValues', () => {
interface Node {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';

import { uniqueByField } from './unique';
import { uniqueByField } from '../unique';

describe('uniqueByField', () => {
it('should return an array with unique items based on id field', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, it } from 'vitest';

import { updateCSSVariables } from './update-css-variables';
import { updateCSSVariables } from '../update-css-variables';

it('updateCSSVariables should update CSS variables in :root selector', () => {
// 模拟初始的内联样式表内容
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { openWindow } from './window'; // 假设你的函数在 'openWindow' 文件中
import { openWindow } from '../window'; // 假设你的函数在 'openWindow' 文件中

describe('openWindow', () => {
// 保存原始的 window.open 函数
Expand Down
37 changes: 36 additions & 1 deletion packages/@core/base/shared/src/utils/inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function isUndefined(value?: unknown): value is undefined {
* @param {T} value 要检查的值。
* @returns {boolean} 如果值为空,返回true,否则返回false。
*/
function isEmpty<T = unknown>(value: T): value is T {
function isEmpty<T = unknown>(value?: T): value is T {
if (value === null || value === undefined) {
return true;
}
Expand Down Expand Up @@ -105,7 +105,42 @@ function isNumber(value: any): value is number {
return typeof value === 'number' && Number.isFinite(value);
}

/**
* Returns the first value in the provided list that is neither `null` nor `undefined`.
*
* This function iterates over the input values and returns the first one that is
* not strictly equal to `null` or `undefined`. If all values are either `null` or
* `undefined`, it returns `undefined`.
*
* @template T - The type of the input values.
* @param {...(T | null | undefined)[]} values - A list of values to evaluate.
* @returns {T | undefined} - The first value that is not `null` or `undefined`, or `undefined` if none are found.
*
* @example
* // Returns 42 because it is the first non-null, non-undefined value.
* getFirstNonNullOrUndefined(undefined, null, 42, 'hello'); // 42
*
* @example
* // Returns 'hello' because it is the first non-null, non-undefined value.
* getFirstNonNullOrUndefined(null, undefined, 'hello', 123); // 'hello'
*
* @example
* // Returns undefined because all values are either null or undefined.
* getFirstNonNullOrUndefined(undefined, null); // undefined
*/
function getFirstNonNullOrUndefined<T>(
...values: (null | T | undefined)[]
): T | undefined {
for (const value of values) {
if (value !== undefined && value !== null) {
return value;
}
}
return undefined;
}

export {
getFirstNonNullOrUndefined,
isEmpty,
isFunction,
isHttpUrl,
Expand Down
17 changes: 16 additions & 1 deletion packages/@core/base/shared/src/utils/letter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,19 @@ function toCamelCase(key: string, parentKey: string): string {
return parentKey + key.charAt(0).toUpperCase() + key.slice(1);
}

export { capitalizeFirstLetter, toCamelCase, toLowerCaseFirstLetter };
function kebabToCamelCase(str: string): string {
return str
.split('-')
.filter(Boolean)
.map((word, index) =>
index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1),
)
.join('');
}

export {
capitalizeFirstLetter,
kebabToCamelCase,
toCamelCase,
toLowerCaseFirstLetter,
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { SortableOptions } from 'sortablejs';

import { beforeEach, describe, expect, it, vi } from 'vitest';

import { useSortable } from './use-sortable';
import { useSortable } from '../use-sortable';

describe('useSortable', () => {
beforeEach(() => {
Expand Down Expand Up @@ -30,7 +30,6 @@ describe('useSortable', () => {

// Import sortablejs to access the mocked create function
const Sortable = await import(
// @ts-expect-error - This is a dynamic import
'sortablejs/modular/sortable.complete.esm.js'
);

Expand Down
1 change: 1 addition & 0 deletions packages/@core/composables/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './use-content-style';
export * from './use-namespace';
export * from './use-priority-value';
export * from './use-sortable';
export {
useEmitAsProps,
Expand Down
Loading

0 comments on commit 20a3868

Please sign in to comment.