@vanilla-bean/components 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Component/Component.js +598 -0
- package/Component/Component.scenarios.js +88 -0
- package/Component/Component.test.js +717 -0
- package/Component/README.md +455 -0
- package/Component/index.js +3 -0
- package/Component/observeElementConnection.js +52 -0
- package/Component/observeElementConnection.test.js +121 -0
- package/Elem/Elem.js +304 -0
- package/Elem/Elem.test.js +679 -0
- package/Elem/README.md +373 -0
- package/Elem/index.js +1 -0
- package/FontWithASyntaxHighlighter-Regular.woff2 +0 -0
- package/LICENSE +21 -0
- package/README.md +413 -0
- package/components/BottomSheet/BottomSheet.js +192 -0
- package/components/BottomSheet/BottomSheet.lld.md +25 -0
- package/components/BottomSheet/README.md +66 -0
- package/components/BottomSheet/index.js +1 -0
- package/components/Button/Button.js +53 -0
- package/components/Button/Button.lld.md +21 -0
- package/components/Button/index.js +1 -0
- package/components/Calendar/Calendar.js +720 -0
- package/components/Calendar/Calendar.lld.md +22 -0
- package/components/Calendar/CalendarEvent.js +102 -0
- package/components/Calendar/Toolbar.js +78 -0
- package/components/Calendar/index.js +2 -0
- package/components/Calendar/utils.js +56 -0
- package/components/Code/Code.js +84 -0
- package/components/Code/Code.lld.md +21 -0
- package/components/Code/index.js +1 -0
- package/components/ColorPicker/ColorPicker.js +445 -0
- package/components/ColorPicker/ColorPicker.lld.md +21 -0
- package/components/ColorPicker/index.js +1 -0
- package/components/ColorPicker/svg.js +5 -0
- package/components/Dialog/Dialog.js +278 -0
- package/components/Dialog/Dialog.lld.md +20 -0
- package/components/Dialog/README.md +96 -0
- package/components/Dialog/index.js +1 -0
- package/components/Form/Form.js +257 -0
- package/components/Form/Form.lld.md +21 -0
- package/components/Form/README.md +87 -0
- package/components/Form/index.js +1 -0
- package/components/Icon/Icon.js +54 -0
- package/components/Icon/Icon.lld.md +21 -0
- package/components/Icon/index.js +1 -0
- package/components/Input/Input.js +173 -0
- package/components/Input/Input.lld.md +28 -0
- package/components/Input/README.md +97 -0
- package/components/Input/index.js +2 -0
- package/components/Input/utils.js +122 -0
- package/components/Keyboard/Key.js +38 -0
- package/components/Keyboard/Keyboard.js +173 -0
- package/components/Keyboard/Keyboard.lld.md +21 -0
- package/components/Keyboard/index.js +1 -0
- package/components/Label/Label.js +214 -0
- package/components/Label/Label.lld.md +20 -0
- package/components/Label/index.js +1 -0
- package/components/Link/Link.js +43 -0
- package/components/Link/Link.lld.md +15 -0
- package/components/Link/index.js +1 -0
- package/components/List/List.js +82 -0
- package/components/List/List.lld.md +19 -0
- package/components/List/index.js +1 -0
- package/components/Menu/Menu.js +93 -0
- package/components/Menu/Menu.lld.md +15 -0
- package/components/Menu/index.js +1 -0
- package/components/Notify/Notify.js +96 -0
- package/components/Notify/Notify.lld.md +20 -0
- package/components/Notify/index.js +1 -0
- package/components/Page/Page.js +67 -0
- package/components/Page/Page.lld.md +20 -0
- package/components/Page/index.js +1 -0
- package/components/Popover/Popover.js +175 -0
- package/components/Popover/Popover.lld.md +19 -0
- package/components/Popover/index.js +1 -0
- package/components/RadioButton/RadioButton.js +108 -0
- package/components/RadioButton/RadioButton.lld.md +15 -0
- package/components/RadioButton/index.js +1 -0
- package/components/Router/README.md +160 -0
- package/components/Router/Router.js +150 -0
- package/components/Router/Router.lld.md +31 -0
- package/components/Router/View.js +15 -0
- package/components/Router/index.js +2 -0
- package/components/Router/utils.js +17 -0
- package/components/Select/README.md +88 -0
- package/components/Select/Select.js +74 -0
- package/components/Select/Select.lld.md +20 -0
- package/components/Select/index.js +1 -0
- package/components/Table/README.md +94 -0
- package/components/Table/Table.js +171 -0
- package/components/Table/Table.lld.md +21 -0
- package/components/Table/index.js +1 -0
- package/components/TagList/Tag.js +84 -0
- package/components/TagList/TagList.js +118 -0
- package/components/TagList/TagList.lld.md +30 -0
- package/components/TagList/design.excalidraw.png +0 -0
- package/components/TagList/index.js +2 -0
- package/components/Tooltip/Tooltip.js +139 -0
- package/components/Tooltip/Tooltip.lld.md +22 -0
- package/components/Tooltip/index.js +1 -0
- package/components/TooltipWrapper/TooltipWrapper.js +89 -0
- package/components/TooltipWrapper/TooltipWrapper.lld.md +21 -0
- package/components/TooltipWrapper/index.js +1 -0
- package/components/Whiteboard/Whiteboard.js +198 -0
- package/components/Whiteboard/Whiteboard.lld.md +35 -0
- package/components/Whiteboard/index.js +1 -0
- package/components/index.js +27 -0
- package/eslint.config.cjs +118 -0
- package/index.d.ts +635 -0
- package/index.js +19 -0
- package/package.json +123 -0
- package/plugins/asText.js +38 -0
- package/plugins/loadPlugins.js +5 -0
- package/plugins/markdownLoader.js +121 -0
- package/prettier.config.cjs +7 -0
- package/spellcheck.config.cjs +227 -0
- package/styled/README.md +329 -0
- package/styled/appendStyles.js +26 -0
- package/styled/appendStyles.test.js +45 -0
- package/styled/index.js +4 -0
- package/styled/shimCSS.js +31 -0
- package/styled/shimCSS.test.js +103 -0
- package/styled/styled.js +91 -0
- package/styled/styled.test.js +586 -0
- package/styled/themeStyles.js +36 -0
- package/styled/themeStyles.test.js +135 -0
- package/test-setup.js +123 -0
- package/theme/.test.js +69 -0
- package/theme/README.md +607 -0
- package/theme/button.js +100 -0
- package/theme/code.js +123 -0
- package/theme/colors.js +42 -0
- package/theme/fonts.js +42 -0
- package/theme/index.js +33 -0
- package/theme/input.js +64 -0
- package/theme/page.js +208 -0
- package/theme/scrollbar.js +24 -0
- package/theme/table.js +53 -0
- package/utils/README.md +176 -0
- package/utils/browser.js +92 -0
- package/utils/class.js +30 -0
- package/utils/color.js +81 -0
- package/utils/data.js +164 -0
- package/utils/element.js +55 -0
- package/utils/index.js +7 -0
- package/utils/rand.js +12 -0
- package/utils/string.js +72 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
|
|
3
|
+
import theme from '../theme';
|
|
4
|
+
import { themeStyles } from './themeStyles';
|
|
5
|
+
|
|
6
|
+
describe('themeStyles', () => {
|
|
7
|
+
test('processes theme function returning CSS string', () => {
|
|
8
|
+
const styleConfig = {
|
|
9
|
+
styles: ({ colors }) => `color: ${colors.red}; background: ${colors.blue};`,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const result = themeStyles(styleConfig);
|
|
13
|
+
|
|
14
|
+
expect(result).toContain(theme.colors.red.toString());
|
|
15
|
+
expect(result).toContain(theme.colors.blue.toString());
|
|
16
|
+
expect(typeof result).toBe('string');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('wraps styles with scope when provided', () => {
|
|
20
|
+
const styleConfig = {
|
|
21
|
+
styles: () => 'color: red; font-size: 16px;',
|
|
22
|
+
scope: '.test-scope',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const result = themeStyles(styleConfig);
|
|
26
|
+
|
|
27
|
+
expect(result).toBe('.test-scope { color: red; font-size: 16px; }');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('returns style object directly when function returns object', () => {
|
|
31
|
+
const styleObject = { color: 'red', fontSize: '16px' };
|
|
32
|
+
const styleConfig = {
|
|
33
|
+
styles: () => styleObject,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const result = themeStyles(styleConfig);
|
|
37
|
+
|
|
38
|
+
expect(result).toBe(styleObject);
|
|
39
|
+
expect(typeof result).toBe('object');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('applies scope to object-returned styles', () => {
|
|
43
|
+
const styleObject = { color: 'blue', padding: '8px' };
|
|
44
|
+
const styleConfig = {
|
|
45
|
+
styles: () => styleObject,
|
|
46
|
+
scope: '.scoped',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const result = themeStyles(styleConfig);
|
|
50
|
+
|
|
51
|
+
expect(result).toBe(styleObject);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('returns empty string for empty styles', () => {
|
|
55
|
+
expect(themeStyles({ styles: () => '' })).toBe('');
|
|
56
|
+
expect(themeStyles({ styles: () => ' ' })).toBe('');
|
|
57
|
+
expect(themeStyles({ styles: () => '\n\t ' })).toBe('');
|
|
58
|
+
expect(themeStyles({ styles: () => '\r\n \t ' })).toBe('');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('passes complete theme object to styles function', () => {
|
|
62
|
+
let receivedTheme;
|
|
63
|
+
const styleConfig = {
|
|
64
|
+
styles: themeObj => {
|
|
65
|
+
receivedTheme = themeObj;
|
|
66
|
+
return 'color: red;';
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
themeStyles(styleConfig);
|
|
71
|
+
|
|
72
|
+
expect(receivedTheme).toBe(theme);
|
|
73
|
+
expect(receivedTheme).toHaveProperty('colors');
|
|
74
|
+
expect(receivedTheme).toHaveProperty('fonts');
|
|
75
|
+
expect(receivedTheme).toHaveProperty('button');
|
|
76
|
+
expect(receivedTheme).toHaveProperty('input');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('handles complex theme function usage', () => {
|
|
80
|
+
const styleConfig = {
|
|
81
|
+
styles: ({ colors, fonts, button }) => `
|
|
82
|
+
${button}
|
|
83
|
+
${fonts.kodeMono}
|
|
84
|
+
color: ${colors.mostReadable(colors.blue, [colors.white, colors.black])};
|
|
85
|
+
background: ${colors.darker(colors.blue)};
|
|
86
|
+
`,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const result = themeStyles(styleConfig);
|
|
90
|
+
|
|
91
|
+
expect(result).toContain('display: inline-block');
|
|
92
|
+
expect(result).toContain('font-family');
|
|
93
|
+
expect(typeof result).toBe('string');
|
|
94
|
+
expect(result.length).toBeGreaterThan(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('handles default empty styles function', () => {
|
|
98
|
+
const result = themeStyles({ styles: undefined });
|
|
99
|
+
expect(result).toBe('');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('strips excess indentation from scoped output', () => {
|
|
103
|
+
const styleConfig = {
|
|
104
|
+
styles: () => `
|
|
105
|
+
color: red;
|
|
106
|
+
background: blue;
|
|
107
|
+
`,
|
|
108
|
+
scope: '.test',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const result = themeStyles(styleConfig);
|
|
112
|
+
|
|
113
|
+
expect(result).toContain('.test');
|
|
114
|
+
expect(result).toContain('color: red');
|
|
115
|
+
expect(result).toContain('background: blue');
|
|
116
|
+
expect(result).not.toMatch(/\t{4}/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('handles whitespace-only strings correctly', () => {
|
|
120
|
+
const testCases = [
|
|
121
|
+
{ input: '', expected: '' },
|
|
122
|
+
{ input: ' ', expected: '' },
|
|
123
|
+
{ input: '\n', expected: '' },
|
|
124
|
+
{ input: '\t', expected: '' },
|
|
125
|
+
{ input: '\r', expected: '' },
|
|
126
|
+
{ input: ' \n\t ', expected: '' },
|
|
127
|
+
{ input: 'color:red;', expected: 'color:red;' },
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
testCases.forEach(({ input, expected }) => {
|
|
131
|
+
const result = themeStyles({ styles: () => input });
|
|
132
|
+
expect(result).toBe(expected);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
package/test-setup.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
|
|
3
|
+
import { beforeEach, mock, spyOn, expect, setSystemTime } from 'bun:test';
|
|
4
|
+
|
|
5
|
+
import * as matchers from '@testing-library/jest-dom/matchers';
|
|
6
|
+
|
|
7
|
+
import { GlobalRegistrator } from '@happy-dom/global-registrator';
|
|
8
|
+
|
|
9
|
+
// expose globals
|
|
10
|
+
// - some weren't available otherwise and importing in the test files breaks the rest of the file
|
|
11
|
+
global.mock = mock;
|
|
12
|
+
global.spyOn = spyOn;
|
|
13
|
+
global.setSystemTime = setSystemTime;
|
|
14
|
+
|
|
15
|
+
GlobalRegistrator.register({ width: 1920, height: 1080 });
|
|
16
|
+
|
|
17
|
+
global.EventTarget = (await import('happy-dom')).EventTarget;
|
|
18
|
+
|
|
19
|
+
expect.extend(matchers);
|
|
20
|
+
|
|
21
|
+
// happy dom doesn't support popovers yet
|
|
22
|
+
HTMLElement.prototype.showPopover = function () {
|
|
23
|
+
this.style.display = 'block';
|
|
24
|
+
};
|
|
25
|
+
HTMLElement.prototype.hidePopover = function () {
|
|
26
|
+
this.style.display = 'none';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// happy dom doesn't support dialogs yet
|
|
30
|
+
HTMLDialogElement.prototype.show = function () {
|
|
31
|
+
this.open = true;
|
|
32
|
+
this.style.display = 'block';
|
|
33
|
+
};
|
|
34
|
+
HTMLDialogElement.prototype.showModal = function () {
|
|
35
|
+
this.open = true;
|
|
36
|
+
this.style.display = 'block';
|
|
37
|
+
};
|
|
38
|
+
HTMLDialogElement.prototype.close = function () {
|
|
39
|
+
this.open = false;
|
|
40
|
+
this.style.display = 'none';
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// happy dom doesn't support canvas yet
|
|
44
|
+
HTMLCanvasElement.prototype.getContext = function () {
|
|
45
|
+
return {
|
|
46
|
+
closePath: () => {},
|
|
47
|
+
lineTo: () => {},
|
|
48
|
+
clearRect: () => {},
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// happy dom doesn't support XPathResult yet
|
|
53
|
+
global.XPathResult = {
|
|
54
|
+
ANY_TYPE: 0,
|
|
55
|
+
NUMBER_TYPE: 1,
|
|
56
|
+
STRING_TYPE: 2,
|
|
57
|
+
BOOLEAN_TYPE: 3,
|
|
58
|
+
UNORDERED_NODE_ITERATOR_TYPE: 4,
|
|
59
|
+
ORDERED_NODE_ITERATOR_TYPE: 5,
|
|
60
|
+
UNORDERED_NODE_SNAPSHOT_TYPE: 6,
|
|
61
|
+
ORDERED_NODE_SNAPSHOT_TYPE: 7,
|
|
62
|
+
ANY_UNORDERED_NODE_TYPE: 8,
|
|
63
|
+
FIRST_ORDERED_NODE_TYPE: 9,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// happy dom doesn't support document.evaluate yet
|
|
67
|
+
document.evaluate = function (xPath, contextNode) {
|
|
68
|
+
// Mock implementation for getElementsContainingText
|
|
69
|
+
const elements = [];
|
|
70
|
+
|
|
71
|
+
// Extract element selector from XPath (e.g., ".//*" or ".//p")
|
|
72
|
+
const elementMatch = xPath.match(/\/\/([^[]+)/);
|
|
73
|
+
const elementSelector = elementMatch ? elementMatch[1] : '*';
|
|
74
|
+
|
|
75
|
+
// Simple text search implementation for testing
|
|
76
|
+
if (xPath.includes('contains(') && xPath.includes('translate(')) {
|
|
77
|
+
// Case insensitive search
|
|
78
|
+
const textMatch = xPath.match(/'([^']+)'/g);
|
|
79
|
+
if (textMatch && textMatch.length > 0) {
|
|
80
|
+
const searchText = textMatch[textMatch.length - 1].slice(1, -1); // Remove quotes
|
|
81
|
+
const selector = elementSelector === '*' ? '*' : elementSelector;
|
|
82
|
+
const allElements = Array.from((contextNode || document).querySelectorAll(selector));
|
|
83
|
+
|
|
84
|
+
for (const element of allElements) {
|
|
85
|
+
if (element.textContent && element.textContent.toLowerCase().includes(searchText.toLowerCase())) {
|
|
86
|
+
elements.push(element);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else if (xPath.includes('contains(') && xPath.includes('text(),')) {
|
|
91
|
+
// Case sensitive search
|
|
92
|
+
const match = xPath.match(/text\(\),'([^']+)'/);
|
|
93
|
+
if (match) {
|
|
94
|
+
const searchText = match[1];
|
|
95
|
+
const selector = elementSelector === '*' ? '*' : elementSelector;
|
|
96
|
+
const allElements = Array.from((contextNode || document).querySelectorAll(selector));
|
|
97
|
+
|
|
98
|
+
for (const element of allElements) {
|
|
99
|
+
if (element.textContent && element.textContent.includes(searchText)) {
|
|
100
|
+
elements.push(element);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let currentIndex = 0;
|
|
107
|
+
return {
|
|
108
|
+
iterateNext: () => {
|
|
109
|
+
return currentIndex < elements.length ? elements[currentIndex++] : null;
|
|
110
|
+
},
|
|
111
|
+
snapshotLength: elements.length,
|
|
112
|
+
snapshotItem: index => {
|
|
113
|
+
return index < elements.length ? elements[index] : null;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
global.container = document.body;
|
|
119
|
+
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
mock.restore();
|
|
122
|
+
document.body.replaceChildren();
|
|
123
|
+
});
|
package/theme/.test.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { TinyColor } from '@ctrl/tinycolor';
|
|
2
|
+
import theme from '.';
|
|
3
|
+
|
|
4
|
+
const ORIGINAL_BLUE = 'hsl(209, 55%, 45%)';
|
|
5
|
+
const ORIGINAL_GRAY = 'hsl(0, 0%, 45%)';
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
theme.colors.blue = new TinyColor(ORIGINAL_BLUE);
|
|
9
|
+
theme.colors.gray = new TinyColor(ORIGINAL_GRAY);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('theme colors', () => {
|
|
13
|
+
test('base colors are writable', () => {
|
|
14
|
+
const before = theme.colors.blue.toString();
|
|
15
|
+
theme.colors.blue = new TinyColor('hsl(120, 55%, 45%)');
|
|
16
|
+
expect(theme.colors.blue.toString()).not.toBe(before);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('white derives from gray — updates when gray is reassigned', () => {
|
|
20
|
+
const before = theme.colors.white.toString();
|
|
21
|
+
theme.colors.gray = new TinyColor('hsl(200, 30%, 45%)');
|
|
22
|
+
expect(theme.colors.white.toString()).not.toBe(before);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('theme lazy strings', () => {
|
|
27
|
+
test('theme.button reflects a color mutation', () => {
|
|
28
|
+
const custom = new TinyColor('hsl(120, 55%, 45%)');
|
|
29
|
+
theme.colors.blue = custom;
|
|
30
|
+
expect(theme.button).toContain(custom.toString());
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('theme.table reflects a color mutation', () => {
|
|
34
|
+
const before = theme.table;
|
|
35
|
+
theme.colors.blue = new TinyColor('hsl(120, 55%, 45%)');
|
|
36
|
+
expect(theme.table).not.toBe(before);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('theme.scrollbar reflects a color mutation', () => {
|
|
40
|
+
const custom = new TinyColor('hsl(120, 55%, 45%)');
|
|
41
|
+
theme.colors.blue = custom;
|
|
42
|
+
expect(theme.scrollbar).toContain(custom.toString());
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('theme.input reflects a color mutation', () => {
|
|
46
|
+
const custom = new TinyColor('hsl(120, 55%, 45%)');
|
|
47
|
+
theme.colors.blue = custom;
|
|
48
|
+
expect(theme.input).toContain(custom.toString());
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('theme.code reflects a color mutation', () => {
|
|
52
|
+
const before = theme.code;
|
|
53
|
+
theme.colors.blue = new TinyColor('hsl(120, 55%, 45%)');
|
|
54
|
+
expect(theme.code).not.toBe(before);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('theme.page reflects a color mutation', () => {
|
|
58
|
+
const before = theme.page;
|
|
59
|
+
theme.colors.blue = new TinyColor('hsl(120, 55%, 45%)');
|
|
60
|
+
expect(theme.page).not.toBe(before);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('two accesses with a mutation between return different strings', () => {
|
|
64
|
+
const before = theme.button;
|
|
65
|
+
theme.colors.blue = new TinyColor('hsl(120, 55%, 45%)');
|
|
66
|
+
const after = theme.button;
|
|
67
|
+
expect(before).not.toBe(after);
|
|
68
|
+
});
|
|
69
|
+
});
|