@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.
Files changed (147) hide show
  1. package/Component/Component.js +598 -0
  2. package/Component/Component.scenarios.js +88 -0
  3. package/Component/Component.test.js +717 -0
  4. package/Component/README.md +455 -0
  5. package/Component/index.js +3 -0
  6. package/Component/observeElementConnection.js +52 -0
  7. package/Component/observeElementConnection.test.js +121 -0
  8. package/Elem/Elem.js +304 -0
  9. package/Elem/Elem.test.js +679 -0
  10. package/Elem/README.md +373 -0
  11. package/Elem/index.js +1 -0
  12. package/FontWithASyntaxHighlighter-Regular.woff2 +0 -0
  13. package/LICENSE +21 -0
  14. package/README.md +413 -0
  15. package/components/BottomSheet/BottomSheet.js +192 -0
  16. package/components/BottomSheet/BottomSheet.lld.md +25 -0
  17. package/components/BottomSheet/README.md +66 -0
  18. package/components/BottomSheet/index.js +1 -0
  19. package/components/Button/Button.js +53 -0
  20. package/components/Button/Button.lld.md +21 -0
  21. package/components/Button/index.js +1 -0
  22. package/components/Calendar/Calendar.js +720 -0
  23. package/components/Calendar/Calendar.lld.md +22 -0
  24. package/components/Calendar/CalendarEvent.js +102 -0
  25. package/components/Calendar/Toolbar.js +78 -0
  26. package/components/Calendar/index.js +2 -0
  27. package/components/Calendar/utils.js +56 -0
  28. package/components/Code/Code.js +84 -0
  29. package/components/Code/Code.lld.md +21 -0
  30. package/components/Code/index.js +1 -0
  31. package/components/ColorPicker/ColorPicker.js +445 -0
  32. package/components/ColorPicker/ColorPicker.lld.md +21 -0
  33. package/components/ColorPicker/index.js +1 -0
  34. package/components/ColorPicker/svg.js +5 -0
  35. package/components/Dialog/Dialog.js +278 -0
  36. package/components/Dialog/Dialog.lld.md +20 -0
  37. package/components/Dialog/README.md +96 -0
  38. package/components/Dialog/index.js +1 -0
  39. package/components/Form/Form.js +257 -0
  40. package/components/Form/Form.lld.md +21 -0
  41. package/components/Form/README.md +87 -0
  42. package/components/Form/index.js +1 -0
  43. package/components/Icon/Icon.js +54 -0
  44. package/components/Icon/Icon.lld.md +21 -0
  45. package/components/Icon/index.js +1 -0
  46. package/components/Input/Input.js +173 -0
  47. package/components/Input/Input.lld.md +28 -0
  48. package/components/Input/README.md +97 -0
  49. package/components/Input/index.js +2 -0
  50. package/components/Input/utils.js +122 -0
  51. package/components/Keyboard/Key.js +38 -0
  52. package/components/Keyboard/Keyboard.js +173 -0
  53. package/components/Keyboard/Keyboard.lld.md +21 -0
  54. package/components/Keyboard/index.js +1 -0
  55. package/components/Label/Label.js +214 -0
  56. package/components/Label/Label.lld.md +20 -0
  57. package/components/Label/index.js +1 -0
  58. package/components/Link/Link.js +43 -0
  59. package/components/Link/Link.lld.md +15 -0
  60. package/components/Link/index.js +1 -0
  61. package/components/List/List.js +82 -0
  62. package/components/List/List.lld.md +19 -0
  63. package/components/List/index.js +1 -0
  64. package/components/Menu/Menu.js +93 -0
  65. package/components/Menu/Menu.lld.md +15 -0
  66. package/components/Menu/index.js +1 -0
  67. package/components/Notify/Notify.js +96 -0
  68. package/components/Notify/Notify.lld.md +20 -0
  69. package/components/Notify/index.js +1 -0
  70. package/components/Page/Page.js +67 -0
  71. package/components/Page/Page.lld.md +20 -0
  72. package/components/Page/index.js +1 -0
  73. package/components/Popover/Popover.js +175 -0
  74. package/components/Popover/Popover.lld.md +19 -0
  75. package/components/Popover/index.js +1 -0
  76. package/components/RadioButton/RadioButton.js +108 -0
  77. package/components/RadioButton/RadioButton.lld.md +15 -0
  78. package/components/RadioButton/index.js +1 -0
  79. package/components/Router/README.md +160 -0
  80. package/components/Router/Router.js +150 -0
  81. package/components/Router/Router.lld.md +31 -0
  82. package/components/Router/View.js +15 -0
  83. package/components/Router/index.js +2 -0
  84. package/components/Router/utils.js +17 -0
  85. package/components/Select/README.md +88 -0
  86. package/components/Select/Select.js +74 -0
  87. package/components/Select/Select.lld.md +20 -0
  88. package/components/Select/index.js +1 -0
  89. package/components/Table/README.md +94 -0
  90. package/components/Table/Table.js +171 -0
  91. package/components/Table/Table.lld.md +21 -0
  92. package/components/Table/index.js +1 -0
  93. package/components/TagList/Tag.js +84 -0
  94. package/components/TagList/TagList.js +118 -0
  95. package/components/TagList/TagList.lld.md +30 -0
  96. package/components/TagList/design.excalidraw.png +0 -0
  97. package/components/TagList/index.js +2 -0
  98. package/components/Tooltip/Tooltip.js +139 -0
  99. package/components/Tooltip/Tooltip.lld.md +22 -0
  100. package/components/Tooltip/index.js +1 -0
  101. package/components/TooltipWrapper/TooltipWrapper.js +89 -0
  102. package/components/TooltipWrapper/TooltipWrapper.lld.md +21 -0
  103. package/components/TooltipWrapper/index.js +1 -0
  104. package/components/Whiteboard/Whiteboard.js +198 -0
  105. package/components/Whiteboard/Whiteboard.lld.md +35 -0
  106. package/components/Whiteboard/index.js +1 -0
  107. package/components/index.js +27 -0
  108. package/eslint.config.cjs +118 -0
  109. package/index.d.ts +635 -0
  110. package/index.js +19 -0
  111. package/package.json +123 -0
  112. package/plugins/asText.js +38 -0
  113. package/plugins/loadPlugins.js +5 -0
  114. package/plugins/markdownLoader.js +121 -0
  115. package/prettier.config.cjs +7 -0
  116. package/spellcheck.config.cjs +227 -0
  117. package/styled/README.md +329 -0
  118. package/styled/appendStyles.js +26 -0
  119. package/styled/appendStyles.test.js +45 -0
  120. package/styled/index.js +4 -0
  121. package/styled/shimCSS.js +31 -0
  122. package/styled/shimCSS.test.js +103 -0
  123. package/styled/styled.js +91 -0
  124. package/styled/styled.test.js +586 -0
  125. package/styled/themeStyles.js +36 -0
  126. package/styled/themeStyles.test.js +135 -0
  127. package/test-setup.js +123 -0
  128. package/theme/.test.js +69 -0
  129. package/theme/README.md +607 -0
  130. package/theme/button.js +100 -0
  131. package/theme/code.js +123 -0
  132. package/theme/colors.js +42 -0
  133. package/theme/fonts.js +42 -0
  134. package/theme/index.js +33 -0
  135. package/theme/input.js +64 -0
  136. package/theme/page.js +208 -0
  137. package/theme/scrollbar.js +24 -0
  138. package/theme/table.js +53 -0
  139. package/utils/README.md +176 -0
  140. package/utils/browser.js +92 -0
  141. package/utils/class.js +30 -0
  142. package/utils/color.js +81 -0
  143. package/utils/data.js +164 -0
  144. package/utils/element.js +55 -0
  145. package/utils/index.js +7 -0
  146. package/utils/rand.js +12 -0
  147. 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
+ });