@keenthemes/ktui 1.0.20 → 1.0.21
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/dist/ktui.js +418 -144
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +139 -31
- package/examples/image-input/file-upload-example.html +189 -0
- package/examples/select/remote-data_.html +5 -0
- package/examples/select/test-optimizations.html +227 -0
- package/examples/select/test-remote-search.html +151 -0
- package/examples/sticky/README.md +158 -0
- package/examples/sticky/debug-sticky.html +144 -0
- package/examples/sticky/test-runner.html +175 -0
- package/examples/sticky/test-sticky-logic.js +369 -0
- package/examples/sticky/test-sticky-positioning.html +386 -0
- package/examples/toast/example.html +52 -0
- package/lib/cjs/components/component.js +5 -3
- package/lib/cjs/components/component.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +4 -0
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +9 -3
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/image-input/image-input.js +10 -2
- package/lib/cjs/components/image-input/image-input.js.map +1 -1
- package/lib/cjs/components/select/combobox.js +50 -20
- package/lib/cjs/components/select/combobox.js.map +1 -1
- package/lib/cjs/components/select/dropdown.js +4 -2
- package/lib/cjs/components/select/dropdown.js.map +1 -1
- package/lib/cjs/components/select/index.js.map +1 -1
- package/lib/cjs/components/select/option.js +2 -1
- package/lib/cjs/components/select/option.js.map +1 -1
- package/lib/cjs/components/select/remote.js +50 -50
- package/lib/cjs/components/select/remote.js.map +1 -1
- package/lib/cjs/components/select/search.js +7 -5
- package/lib/cjs/components/select/search.js.map +1 -1
- package/lib/cjs/components/select/select.js +199 -33
- package/lib/cjs/components/select/select.js.map +1 -1
- package/lib/cjs/components/select/tags.js +3 -1
- package/lib/cjs/components/select/tags.js.map +1 -1
- package/lib/cjs/components/select/templates.js.map +1 -1
- package/lib/cjs/components/select/utils.js +23 -10
- package/lib/cjs/components/select/utils.js.map +1 -1
- package/lib/cjs/components/sticky/sticky.js +52 -14
- package/lib/cjs/components/sticky/sticky.js.map +1 -1
- package/lib/esm/components/component.js +5 -3
- package/lib/esm/components/component.js.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +4 -0
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable.js +9 -3
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/image-input/image-input.js +10 -2
- package/lib/esm/components/image-input/image-input.js.map +1 -1
- package/lib/esm/components/select/combobox.js +50 -20
- package/lib/esm/components/select/combobox.js.map +1 -1
- package/lib/esm/components/select/dropdown.js +4 -2
- package/lib/esm/components/select/dropdown.js.map +1 -1
- package/lib/esm/components/select/index.js +1 -1
- package/lib/esm/components/select/index.js.map +1 -1
- package/lib/esm/components/select/option.js +2 -1
- package/lib/esm/components/select/option.js.map +1 -1
- package/lib/esm/components/select/remote.js +50 -50
- package/lib/esm/components/select/remote.js.map +1 -1
- package/lib/esm/components/select/search.js +8 -6
- package/lib/esm/components/select/search.js.map +1 -1
- package/lib/esm/components/select/select.js +199 -33
- package/lib/esm/components/select/select.js.map +1 -1
- package/lib/esm/components/select/tags.js +3 -1
- package/lib/esm/components/select/tags.js.map +1 -1
- package/lib/esm/components/select/templates.js.map +1 -1
- package/lib/esm/components/select/utils.js +23 -10
- package/lib/esm/components/select/utils.js.map +1 -1
- package/lib/esm/components/sticky/sticky.js +52 -14
- package/lib/esm/components/sticky/sticky.js.map +1 -1
- package/package.json +1 -1
- package/src/components/component.ts +12 -11
- package/src/components/datatable/datatable-sort.ts +6 -0
- package/src/components/datatable/datatable.ts +90 -81
- package/src/components/image-input/image-input.ts +11 -2
- package/src/components/image-input/types.ts +1 -0
- package/src/components/input/input-group.css +1 -1
- package/src/components/input/input.css +1 -1
- package/src/components/scrollable/scrollable.css +3 -3
- package/src/components/select/combobox.ts +84 -34
- package/src/components/select/dropdown.ts +20 -11
- package/src/components/select/index.ts +6 -1
- package/src/components/select/option.ts +7 -6
- package/src/components/select/remote.ts +51 -52
- package/src/components/select/search.ts +51 -45
- package/src/components/select/select.css +12 -11
- package/src/components/select/select.ts +371 -102
- package/src/components/select/tags.ts +9 -3
- package/src/components/select/templates.ts +1 -4
- package/src/components/select/utils.ts +55 -20
- package/src/components/select/variants.css +0 -1
- package/src/components/sticky/sticky.ts +47 -16
- package/src/components/sticky/types.ts +3 -0
- package/src/components/table/table.css +1 -1
- package/src/components/textarea/textarea.css +1 -1
- package/src/components/toast/toast.css +84 -47
- package/src/components/toast/types.ts +3 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>KTUI Sticky Component - Test Runner</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<style>
|
|
9
|
+
.test-result {
|
|
10
|
+
padding: 1rem;
|
|
11
|
+
margin: 0.5rem 0;
|
|
12
|
+
border-radius: 8px;
|
|
13
|
+
border-left: 4px solid;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.test-result.pass {
|
|
17
|
+
background: #f0fdf4;
|
|
18
|
+
border-left-color: #22c55e;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.test-result.fail {
|
|
22
|
+
background: #fef2f2;
|
|
23
|
+
border-left-color: #ef4444;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.test-details {
|
|
27
|
+
font-family: monospace;
|
|
28
|
+
font-size: 0.875rem;
|
|
29
|
+
background: #f8fafc;
|
|
30
|
+
padding: 0.5rem;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
margin-top: 0.5rem;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
</head>
|
|
36
|
+
<body class="bg-gray-50 p-8">
|
|
37
|
+
<div class="max-w-4xl mx-auto">
|
|
38
|
+
<h1 class="text-3xl font-bold mb-8">KTUI Sticky Component - Test Runner</h1>
|
|
39
|
+
<p class="text-gray-600 mb-8">Testing PR #27 changes: middle, center, bottom positioning with improved offset logic</p>
|
|
40
|
+
|
|
41
|
+
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
|
|
42
|
+
<h2 class="text-xl font-semibold mb-4">Test Controls</h2>
|
|
43
|
+
<div class="space-x-4">
|
|
44
|
+
<button id="runTests" class="bg-blue-500 text-white px-6 py-2 rounded hover:bg-blue-600">
|
|
45
|
+
Run All Tests
|
|
46
|
+
</button>
|
|
47
|
+
<button id="clearResults" class="bg-gray-500 text-white px-6 py-2 rounded hover:bg-gray-600">
|
|
48
|
+
Clear Results
|
|
49
|
+
</button>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div id="testResults" class="space-y-4">
|
|
54
|
+
<div class="text-center text-gray-500 py-8">
|
|
55
|
+
Click "Run All Tests" to start testing the sticky component
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<script type="module">
|
|
61
|
+
import StickyTestSuite from './test-sticky-logic.js';
|
|
62
|
+
|
|
63
|
+
const runTestsBtn = document.getElementById('runTests');
|
|
64
|
+
const clearResultsBtn = document.getElementById('clearResults');
|
|
65
|
+
const testResultsDiv = document.getElementById('testResults');
|
|
66
|
+
|
|
67
|
+
let testSuite = null;
|
|
68
|
+
|
|
69
|
+
runTestsBtn.addEventListener('click', async () => {
|
|
70
|
+
// Disable button during testing
|
|
71
|
+
runTestsBtn.disabled = true;
|
|
72
|
+
runTestsBtn.textContent = 'Running Tests...';
|
|
73
|
+
|
|
74
|
+
// Clear previous results
|
|
75
|
+
testResultsDiv.innerHTML = '<div class="text-center text-gray-500 py-8">Running tests...</div>';
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
// Create and run test suite
|
|
79
|
+
testSuite = new StickyTestSuite();
|
|
80
|
+
|
|
81
|
+
// Override console.log to capture test output
|
|
82
|
+
const originalLog = console.log;
|
|
83
|
+
const testOutput = [];
|
|
84
|
+
|
|
85
|
+
console.log = (...args) => {
|
|
86
|
+
originalLog.apply(console, args);
|
|
87
|
+
testOutput.push(args.join(' '));
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Run tests
|
|
91
|
+
await testSuite.runAllTests();
|
|
92
|
+
|
|
93
|
+
// Restore console.log
|
|
94
|
+
console.log = originalLog;
|
|
95
|
+
|
|
96
|
+
// Display results
|
|
97
|
+
displayResults(testSuite.testResults, testOutput);
|
|
98
|
+
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('Test execution failed:', error);
|
|
101
|
+
testResultsDiv.innerHTML = `
|
|
102
|
+
<div class="test-result fail">
|
|
103
|
+
<h3 class="font-semibold">Test Execution Failed</h3>
|
|
104
|
+
<p class="text-red-600">${error.message}</p>
|
|
105
|
+
</div>
|
|
106
|
+
`;
|
|
107
|
+
} finally {
|
|
108
|
+
// Re-enable button
|
|
109
|
+
runTestsBtn.disabled = false;
|
|
110
|
+
runTestsBtn.textContent = 'Run All Tests';
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
clearResultsBtn.addEventListener('click', () => {
|
|
115
|
+
testResultsDiv.innerHTML = '<div class="text-center text-gray-500 py-8">Click "Run All Tests" to start testing the sticky component</div>';
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
function displayResults(results, output) {
|
|
119
|
+
const passed = results.filter(r => r.passed).length;
|
|
120
|
+
const total = results.length;
|
|
121
|
+
|
|
122
|
+
let html = `
|
|
123
|
+
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
|
|
124
|
+
<h2 class="text-xl font-semibold mb-4">Test Summary</h2>
|
|
125
|
+
<div class="flex items-center space-x-4">
|
|
126
|
+
<div class="text-2xl font-bold ${passed === total ? 'text-green-600' : 'text-red-600'}">
|
|
127
|
+
${passed}/${total}
|
|
128
|
+
</div>
|
|
129
|
+
<div class="text-gray-600">
|
|
130
|
+
tests passed
|
|
131
|
+
</div>
|
|
132
|
+
<div class="text-sm text-gray-500">
|
|
133
|
+
${((passed / total) * 100).toFixed(1)}% success rate
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
results.forEach((result, index) => {
|
|
140
|
+
const resultClass = result.passed ? 'pass' : 'fail';
|
|
141
|
+
const statusIcon = result.passed ? '✅' : '❌';
|
|
142
|
+
|
|
143
|
+
html += `
|
|
144
|
+
<div class="test-result ${resultClass}">
|
|
145
|
+
<h3 class="font-semibold">${statusIcon} ${result.test}</h3>
|
|
146
|
+
<p class="text-sm text-gray-600 mt-1">${result.expected}</p>
|
|
147
|
+
<div class="test-details">
|
|
148
|
+
<div><strong>Actual:</strong> ${result.actual}</div>
|
|
149
|
+
<div><strong>Position:</strong> ${result.styles.position}</div>
|
|
150
|
+
<div><strong>InsetBlockStart:</strong> ${result.styles.insetBlockStart}</div>
|
|
151
|
+
<div><strong>InsetBlockEnd:</strong> ${result.styles.insetBlockEnd}</div>
|
|
152
|
+
<div><strong>InsetInlineStart:</strong> ${result.styles.insetInlineStart}</div>
|
|
153
|
+
<div><strong>InsetInlineEnd:</strong> ${result.styles.insetInlineEnd}</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
`;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Add console output
|
|
160
|
+
if (output.length > 0) {
|
|
161
|
+
html += `
|
|
162
|
+
<div class="bg-white rounded-lg shadow-md p-6">
|
|
163
|
+
<h2 class="text-xl font-semibold mb-4">Console Output</h2>
|
|
164
|
+
<div class="bg-gray-900 text-green-400 p-4 rounded font-mono text-sm overflow-auto max-h-96">
|
|
165
|
+
${output.map(line => `<div>${line}</div>`).join('')}
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
testResultsDiv.innerHTML = html;
|
|
172
|
+
}
|
|
173
|
+
</script>
|
|
174
|
+
</body>
|
|
175
|
+
</html>
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI Sticky Component - Logic Test
|
|
3
|
+
* Tests the positioning logic changes from PR #27
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { KTSticky } from '../../lib/esm/components/sticky/index.js';
|
|
7
|
+
|
|
8
|
+
class StickyTestSuite {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.testResults = [];
|
|
11
|
+
this.testElement = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Setup test environment
|
|
15
|
+
setup() {
|
|
16
|
+
// Create a test container
|
|
17
|
+
const container = document.createElement('div');
|
|
18
|
+
container.id = 'sticky-test-container';
|
|
19
|
+
container.style.cssText = `
|
|
20
|
+
position: relative;
|
|
21
|
+
width: 100%;
|
|
22
|
+
height: 200vh;
|
|
23
|
+
background: #f0f0f0;
|
|
24
|
+
padding: 20px;
|
|
25
|
+
`;
|
|
26
|
+
document.body.appendChild(container);
|
|
27
|
+
|
|
28
|
+
// Create test element
|
|
29
|
+
this.testElement = document.createElement('div');
|
|
30
|
+
this.testElement.id = 'sticky-test-element';
|
|
31
|
+
this.testElement.style.cssText = `
|
|
32
|
+
width: 200px;
|
|
33
|
+
height: 100px;
|
|
34
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
35
|
+
color: white;
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
font-weight: bold;
|
|
40
|
+
border-radius: 8px;
|
|
41
|
+
`;
|
|
42
|
+
container.appendChild(this.testElement);
|
|
43
|
+
|
|
44
|
+
console.log('✅ Test environment setup complete');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Cleanup test environment
|
|
48
|
+
cleanup() {
|
|
49
|
+
const container = document.getElementById('sticky-test-container');
|
|
50
|
+
if (container) {
|
|
51
|
+
container.remove();
|
|
52
|
+
}
|
|
53
|
+
this.testResults = [];
|
|
54
|
+
console.log('✅ Test environment cleaned up');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Test helper: create sticky instance with config
|
|
58
|
+
createStickyInstance(config) {
|
|
59
|
+
// Remove existing instance
|
|
60
|
+
if (this.testElement.hasAttribute('data-kt-sticky-initialized')) {
|
|
61
|
+
const existingInstance = KTSticky.getInstance(this.testElement);
|
|
62
|
+
if (existingInstance) {
|
|
63
|
+
// Force cleanup
|
|
64
|
+
this.testElement.classList.remove('active');
|
|
65
|
+
this.testElement.style.position = '';
|
|
66
|
+
this.testElement.style.top = '';
|
|
67
|
+
this.testElement.style.bottom = '';
|
|
68
|
+
this.testElement.style.left = '';
|
|
69
|
+
this.testElement.style.right = '';
|
|
70
|
+
this.testElement.style.insetBlockStart = '';
|
|
71
|
+
this.testElement.style.insetInlineStart = '';
|
|
72
|
+
this.testElement.style.insetInlineEnd = '';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Clear attributes
|
|
77
|
+
this.testElement.removeAttribute('data-kt-sticky');
|
|
78
|
+
this.testElement.removeAttribute('data-kt-sticky-top');
|
|
79
|
+
this.testElement.removeAttribute('data-kt-sticky-bottom');
|
|
80
|
+
this.testElement.removeAttribute('data-kt-sticky-start');
|
|
81
|
+
this.testElement.removeAttribute('data-kt-sticky-end');
|
|
82
|
+
this.testElement.removeAttribute('data-kt-sticky-middle');
|
|
83
|
+
this.testElement.removeAttribute('data-kt-sticky-center');
|
|
84
|
+
this.testElement.removeAttribute('data-kt-sticky-name');
|
|
85
|
+
|
|
86
|
+
// Set new attributes based on config
|
|
87
|
+
this.testElement.setAttribute('data-kt-sticky', 'true');
|
|
88
|
+
this.testElement.setAttribute('data-kt-sticky-name', 'test');
|
|
89
|
+
|
|
90
|
+
if (config.top !== undefined) this.testElement.setAttribute('data-kt-sticky-top', config.top);
|
|
91
|
+
if (config.bottom !== undefined) this.testElement.setAttribute('data-kt-sticky-bottom', config.bottom);
|
|
92
|
+
if (config.start !== undefined) this.testElement.setAttribute('data-kt-sticky-start', config.start);
|
|
93
|
+
if (config.end !== undefined) this.testElement.setAttribute('data-kt-sticky-end', config.end);
|
|
94
|
+
if (config.middle !== undefined) this.testElement.setAttribute('data-kt-sticky-middle', config.middle);
|
|
95
|
+
if (config.center !== undefined) this.testElement.setAttribute('data-kt-sticky-center', config.center);
|
|
96
|
+
|
|
97
|
+
// Create new instance
|
|
98
|
+
return new KTSticky(this.testElement);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Test helper: get computed styles
|
|
102
|
+
getComputedStyles() {
|
|
103
|
+
const computed = window.getComputedStyle(this.testElement);
|
|
104
|
+
return {
|
|
105
|
+
position: computed.position,
|
|
106
|
+
top: computed.top,
|
|
107
|
+
bottom: computed.bottom,
|
|
108
|
+
left: computed.left,
|
|
109
|
+
right: computed.right,
|
|
110
|
+
insetBlockStart: computed.insetBlockStart,
|
|
111
|
+
insetBlockEnd: computed.insetBlockEnd,
|
|
112
|
+
insetInlineStart: computed.insetInlineStart,
|
|
113
|
+
insetInlineEnd: computed.insetInlineEnd,
|
|
114
|
+
transform: computed.transform
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Test helper: simulate scroll to trigger sticky
|
|
119
|
+
simulateScroll() {
|
|
120
|
+
// Simulate scroll by triggering the scroll event
|
|
121
|
+
window.dispatchEvent(new Event('scroll'));
|
|
122
|
+
|
|
123
|
+
// Force a small delay to allow processing
|
|
124
|
+
return new Promise(resolve => setTimeout(resolve, 100));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Test 1: Middle positioning
|
|
128
|
+
async testMiddlePositioning() {
|
|
129
|
+
console.log('🧪 Testing Middle (Vertical Center) Positioning...');
|
|
130
|
+
|
|
131
|
+
const instance = this.createStickyInstance({ middle: true });
|
|
132
|
+
await this.simulateScroll();
|
|
133
|
+
|
|
134
|
+
const styles = this.getComputedStyles();
|
|
135
|
+
const isMiddle = styles.insetBlockStart === '50%';
|
|
136
|
+
|
|
137
|
+
this.testResults.push({
|
|
138
|
+
test: 'Middle Positioning',
|
|
139
|
+
passed: isMiddle,
|
|
140
|
+
expected: 'insetBlockStart: 50%',
|
|
141
|
+
actual: `insetBlockStart: ${styles.insetBlockStart}`,
|
|
142
|
+
styles
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log(isMiddle ? '✅ Middle positioning works' : '❌ Middle positioning failed');
|
|
146
|
+
return isMiddle;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Test 2: Center positioning
|
|
150
|
+
async testCenterPositioning() {
|
|
151
|
+
console.log('🧪 Testing Center (Horizontal Center) Positioning...');
|
|
152
|
+
|
|
153
|
+
const instance = this.createStickyInstance({ center: true });
|
|
154
|
+
await this.simulateScroll();
|
|
155
|
+
|
|
156
|
+
const styles = this.getComputedStyles();
|
|
157
|
+
const isCenter = styles.insetInlineStart === '50%';
|
|
158
|
+
|
|
159
|
+
this.testResults.push({
|
|
160
|
+
test: 'Center Positioning',
|
|
161
|
+
passed: isCenter,
|
|
162
|
+
expected: 'insetInlineStart: 50%',
|
|
163
|
+
actual: `insetInlineStart: ${styles.insetInlineStart}`,
|
|
164
|
+
styles
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
console.log(isCenter ? '✅ Center positioning works' : '❌ Center positioning failed');
|
|
168
|
+
return isCenter;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Test 3: Middle + Center positioning
|
|
172
|
+
async testMiddleCenterPositioning() {
|
|
173
|
+
console.log('🧪 Testing Middle + Center (Fully Centered) Positioning...');
|
|
174
|
+
|
|
175
|
+
const instance = this.createStickyInstance({ middle: true, center: true });
|
|
176
|
+
await this.simulateScroll();
|
|
177
|
+
|
|
178
|
+
const styles = this.getComputedStyles();
|
|
179
|
+
const isMiddleCenter = styles.insetBlockStart === '50%' && styles.insetInlineStart === '50%';
|
|
180
|
+
|
|
181
|
+
this.testResults.push({
|
|
182
|
+
test: 'Middle + Center Positioning',
|
|
183
|
+
passed: isMiddleCenter,
|
|
184
|
+
expected: 'insetBlockStart: 50%, insetInlineStart: 50%',
|
|
185
|
+
actual: `insetBlockStart: ${styles.insetBlockStart}, insetInlineStart: ${styles.insetInlineStart}`,
|
|
186
|
+
styles
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
console.log(isMiddleCenter ? '✅ Middle + Center positioning works' : '❌ Middle + Center positioning failed');
|
|
190
|
+
return isMiddleCenter;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Test 4: Bottom positioning
|
|
194
|
+
async testBottomPositioning() {
|
|
195
|
+
console.log('🧪 Testing Bottom Positioning...');
|
|
196
|
+
|
|
197
|
+
const instance = this.createStickyInstance({ bottom: '20' });
|
|
198
|
+
await this.simulateScroll();
|
|
199
|
+
|
|
200
|
+
const styles = this.getComputedStyles();
|
|
201
|
+
const isBottom = styles.insetBlockEnd === '20px';
|
|
202
|
+
|
|
203
|
+
this.testResults.push({
|
|
204
|
+
test: 'Bottom Positioning',
|
|
205
|
+
passed: isBottom,
|
|
206
|
+
expected: 'insetBlockEnd: 20px',
|
|
207
|
+
actual: `insetBlockEnd: ${styles.insetBlockEnd}`,
|
|
208
|
+
styles
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
console.log(isBottom ? '✅ Bottom positioning works' : '❌ Bottom positioning failed');
|
|
212
|
+
return isBottom;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Test 5: Auto positioning with offset calculation
|
|
216
|
+
async testAutoPositioning() {
|
|
217
|
+
console.log('🧪 Testing Auto Positioning with Offset Calculation...');
|
|
218
|
+
|
|
219
|
+
const instance = this.createStickyInstance({ start: 'auto', top: '50' });
|
|
220
|
+
await this.simulateScroll();
|
|
221
|
+
|
|
222
|
+
const styles = this.getComputedStyles();
|
|
223
|
+
const hasAutoOffset = styles.insetInlineStart !== 'auto' && styles.insetInlineStart !== '';
|
|
224
|
+
|
|
225
|
+
this.testResults.push({
|
|
226
|
+
test: 'Auto Positioning with Offset',
|
|
227
|
+
passed: hasAutoOffset,
|
|
228
|
+
expected: 'insetInlineStart: calculated offset value',
|
|
229
|
+
actual: `insetInlineStart: ${styles.insetInlineStart}`,
|
|
230
|
+
styles
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
console.log(hasAutoOffset ? '✅ Auto positioning with offset works' : '❌ Auto positioning with offset failed');
|
|
234
|
+
return hasAutoOffset;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Test 6: Exclusive positioning logic (top vs bottom)
|
|
238
|
+
async testExclusivePositioning() {
|
|
239
|
+
console.log('🧪 Testing Exclusive Positioning Logic (top vs bottom)...');
|
|
240
|
+
|
|
241
|
+
const instance = this.createStickyInstance({ top: '30', bottom: '30' });
|
|
242
|
+
await this.simulateScroll();
|
|
243
|
+
|
|
244
|
+
const styles = this.getComputedStyles();
|
|
245
|
+
const isExclusive = styles.insetBlockStart === '30px' && styles.insetBlockEnd === 'auto';
|
|
246
|
+
|
|
247
|
+
this.testResults.push({
|
|
248
|
+
test: 'Exclusive Positioning Logic',
|
|
249
|
+
passed: isExclusive,
|
|
250
|
+
expected: 'insetBlockStart: 30px, insetBlockEnd: auto (top takes precedence)',
|
|
251
|
+
actual: `insetBlockStart: ${styles.insetBlockStart}, insetBlockEnd: ${styles.insetBlockEnd}`,
|
|
252
|
+
styles
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
console.log(isExclusive ? '✅ Exclusive positioning logic works' : '❌ Exclusive positioning logic failed');
|
|
256
|
+
return isExclusive;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Test 7: Logical properties for RTL support
|
|
260
|
+
async testLogicalProperties() {
|
|
261
|
+
console.log('🧪 Testing Logical Properties for RTL Support...');
|
|
262
|
+
|
|
263
|
+
const instance = this.createStickyInstance({ end: '20', top: '40' });
|
|
264
|
+
await this.simulateScroll();
|
|
265
|
+
|
|
266
|
+
const styles = this.getComputedStyles();
|
|
267
|
+
const usesLogicalProps = styles.insetInlineEnd === '20px' && styles.insetBlockStart === '40px';
|
|
268
|
+
|
|
269
|
+
this.testResults.push({
|
|
270
|
+
test: 'Logical Properties (RTL Support)',
|
|
271
|
+
passed: usesLogicalProps,
|
|
272
|
+
expected: 'insetInlineEnd: 20px, insetBlockStart: 40px',
|
|
273
|
+
actual: `insetInlineEnd: ${styles.insetInlineEnd}, insetBlockStart: ${styles.insetBlockStart}`,
|
|
274
|
+
styles
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
console.log(usesLogicalProps ? '✅ Logical properties work' : '❌ Logical properties failed');
|
|
278
|
+
return usesLogicalProps;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Test 8: Auto fallback values
|
|
282
|
+
async testAutoFallbackValues() {
|
|
283
|
+
console.log('🧪 Testing Auto Fallback Values...');
|
|
284
|
+
|
|
285
|
+
const instance = this.createStickyInstance({ top: 'auto' });
|
|
286
|
+
await this.simulateScroll();
|
|
287
|
+
|
|
288
|
+
const styles = this.getComputedStyles();
|
|
289
|
+
const hasAutoFallback = styles.insetBlockStart === '0px';
|
|
290
|
+
|
|
291
|
+
this.testResults.push({
|
|
292
|
+
test: 'Auto Fallback Values',
|
|
293
|
+
passed: hasAutoFallback,
|
|
294
|
+
expected: 'insetBlockStart: 0px (auto defaults to 0px)',
|
|
295
|
+
actual: `insetBlockStart: ${styles.insetBlockStart}`,
|
|
296
|
+
styles
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
console.log(hasAutoFallback ? '✅ Auto fallback values work' : '❌ Auto fallback values failed');
|
|
300
|
+
return hasAutoFallback;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Run all tests
|
|
304
|
+
async runAllTests() {
|
|
305
|
+
console.log('🚀 Starting KTUI Sticky Component Tests...');
|
|
306
|
+
console.log('Testing PR #27 changes: middle, center, bottom positioning with improved offset logic\n');
|
|
307
|
+
|
|
308
|
+
this.setup();
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
await this.testMiddlePositioning();
|
|
312
|
+
await this.testCenterPositioning();
|
|
313
|
+
await this.testMiddleCenterPositioning();
|
|
314
|
+
await this.testBottomPositioning();
|
|
315
|
+
await this.testAutoPositioning();
|
|
316
|
+
await this.testExclusivePositioning();
|
|
317
|
+
await this.testLogicalProperties();
|
|
318
|
+
await this.testAutoFallbackValues();
|
|
319
|
+
|
|
320
|
+
this.printResults();
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error('❌ Test suite failed:', error);
|
|
323
|
+
} finally {
|
|
324
|
+
this.cleanup();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Print test results
|
|
329
|
+
printResults() {
|
|
330
|
+
console.log('\n📊 Test Results Summary:');
|
|
331
|
+
console.log('='.repeat(60));
|
|
332
|
+
|
|
333
|
+
const passed = this.testResults.filter(r => r.passed).length;
|
|
334
|
+
const total = this.testResults.length;
|
|
335
|
+
|
|
336
|
+
this.testResults.forEach((result, index) => {
|
|
337
|
+
const status = result.passed ? '✅ PASS' : '❌ FAIL';
|
|
338
|
+
console.log(`${index + 1}. ${status} - ${result.test}`);
|
|
339
|
+
if (!result.passed) {
|
|
340
|
+
console.log(` Expected: ${result.expected}`);
|
|
341
|
+
console.log(` Actual: ${result.actual}`);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
console.log('='.repeat(60));
|
|
346
|
+
console.log(`Overall: ${passed}/${total} tests passed`);
|
|
347
|
+
|
|
348
|
+
if (passed === total) {
|
|
349
|
+
console.log('🎉 All tests passed! PR #27 changes are working correctly.');
|
|
350
|
+
} else {
|
|
351
|
+
console.log('⚠️ Some tests failed. Please review the implementation.');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Export for use in browser
|
|
357
|
+
if (typeof window !== 'undefined') {
|
|
358
|
+
window.StickyTestSuite = StickyTestSuite;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Auto-run tests if this file is loaded directly
|
|
362
|
+
if (typeof document !== 'undefined') {
|
|
363
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
364
|
+
const testSuite = new StickyTestSuite();
|
|
365
|
+
testSuite.runAllTests();
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export default StickyTestSuite;
|