@nyaruka/temba-components 0.128.0 → 0.129.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/CHANGELOG.md +12 -0
- package/demo/chart/horizontal-demo.html +81 -0
- package/demo/components/datepicker/example.html +63 -0
- package/demo/components/datepicker/range-picker-demo.html +161 -0
- package/demo/index.html +8 -0
- package/demo/static/css/prism.css +2 -0
- package/demo/static/js/prism-loader.js +12 -0
- package/demo/styles.css +71 -1
- package/dist/temba-components.js +172 -10
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/chart/TembaChart.js +116 -59
- package/out-tsc/src/chart/TembaChart.js.map +1 -1
- package/out-tsc/src/datepicker/DatePicker.js +11 -1
- package/out-tsc/src/datepicker/DatePicker.js.map +1 -1
- package/out-tsc/src/datepicker/RangePicker.js +595 -0
- package/out-tsc/src/datepicker/RangePicker.js.map +1 -0
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/temba-modules.js +3 -1
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-chart.test.js +36 -0
- package/out-tsc/test/temba-chart.test.js.map +1 -1
- package/out-tsc/test/temba-datepicker.test.js +1 -1
- package/out-tsc/test/temba-datepicker.test.js.map +1 -1
- package/out-tsc/test/temba-range-picker.test.js +123 -0
- package/out-tsc/test/temba-range-picker.test.js.map +1 -0
- package/out-tsc/test/temba-select.test.js +1 -1
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-webchat.test.js +4 -0
- package/out-tsc/test/temba-webchat.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/datepicker/range-picker-all.png +0 -0
- package/screenshots/truth/datepicker/range-picker-button-states.png +0 -0
- package/screenshots/truth/datepicker/range-picker-default.png +0 -0
- package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
- package/screenshots/truth/datepicker/range-picker-initial-values.png +0 -0
- package/screenshots/truth/datepicker/range-picker-min-max.png +0 -0
- package/screenshots/truth/datepicker/range-picker-week.png +0 -0
- package/screenshots/truth/datepicker/range-picker-year.png +0 -0
- package/screenshots/truth/webchat/connected-state.png +0 -0
- package/src/chart/TembaChart.ts +124 -63
- package/src/datepicker/DatePicker.ts +9 -1
- package/src/datepicker/RangePicker.ts +602 -0
- package/src/interfaces.ts +2 -1
- package/temba-modules.ts +3 -1
- package/test/temba-chart.test.ts +47 -0
- package/test/temba-datepicker.test.ts +1 -1
- package/test/temba-range-picker.test.ts +193 -0
- package/test/temba-select.test.ts +1 -1
- package/test/temba-webchat.test.ts +7 -0
- package/web-test-runner.config.mjs +2 -0
- package/demo/datepicker/example.html +0 -69
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { fixture, expect, assert } from '@open-wc/testing';
|
|
2
|
+
import { RangePicker } from '../src/datepicker/RangePicker';
|
|
3
|
+
import { assertScreenshot, getAttributes, getClip } from './utils.test';
|
|
4
|
+
import { DateTime } from 'luxon';
|
|
5
|
+
|
|
6
|
+
export const getRangePickerHTML = (attrs: any = {}) => {
|
|
7
|
+
return `<temba-range-picker ${getAttributes(attrs)}></temba-range-picker>`;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const createRangePicker = async (def: string) => {
|
|
11
|
+
const parentNode = document.createElement('div');
|
|
12
|
+
parentNode.setAttribute('style', 'width: 600px;');
|
|
13
|
+
parentNode.id = 'parent';
|
|
14
|
+
const picker: RangePicker = await fixture(def, { parentNode });
|
|
15
|
+
return picker;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe('temba-range-picker', () => {
|
|
19
|
+
it('can create a range picker', async () => {
|
|
20
|
+
const picker: RangePicker = await createRangePicker(getRangePickerHTML());
|
|
21
|
+
assert.instanceOf(picker, RangePicker);
|
|
22
|
+
|
|
23
|
+
// Should have default range (last month)
|
|
24
|
+
expect(picker.selectedRange).to.equal('M');
|
|
25
|
+
expect(picker.startDate).to.not.be.empty;
|
|
26
|
+
expect(picker.endDate).to.not.be.empty;
|
|
27
|
+
|
|
28
|
+
await assertScreenshot('datepicker/range-picker-default', getClip(picker));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('can be initialized with start and end dates', async () => {
|
|
32
|
+
const picker: RangePicker = await createRangePicker(
|
|
33
|
+
getRangePickerHTML({ start: '2024-01-01', end: '2024-01-31' })
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(picker.startDate).to.equal('2024-01-01');
|
|
37
|
+
expect(picker.endDate).to.equal('2024-01-31');
|
|
38
|
+
expect(picker.selectedRange).to.equal('');
|
|
39
|
+
|
|
40
|
+
await assertScreenshot(
|
|
41
|
+
'datepicker/range-picker-initial-values',
|
|
42
|
+
getClip(picker)
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('can set min and max dates', async () => {
|
|
47
|
+
const picker: RangePicker = await createRangePicker(
|
|
48
|
+
getRangePickerHTML({
|
|
49
|
+
start: '2024-06-01',
|
|
50
|
+
end: '2024-06-30',
|
|
51
|
+
min: '2024-01-01',
|
|
52
|
+
max: '2024-12-31'
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(picker.minDate).to.equal('2024-01-01');
|
|
57
|
+
expect(picker.maxDate).to.equal('2024-12-31');
|
|
58
|
+
|
|
59
|
+
await assertScreenshot('datepicker/range-picker-min-max', getClip(picker));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('can set range using buttons', async () => {
|
|
63
|
+
const picker: RangePicker = await createRangePicker(getRangePickerHTML());
|
|
64
|
+
|
|
65
|
+
// Click Week button
|
|
66
|
+
const weekBtn = picker.shadowRoot?.querySelector(
|
|
67
|
+
'.range-btn'
|
|
68
|
+
) as HTMLButtonElement;
|
|
69
|
+
weekBtn.click();
|
|
70
|
+
await picker.updateComplete;
|
|
71
|
+
|
|
72
|
+
expect(picker.selectedRange).to.equal('W');
|
|
73
|
+
expect(picker.startDate).to.equal(
|
|
74
|
+
DateTime.now().minus({ days: 6 }).toISODate()
|
|
75
|
+
);
|
|
76
|
+
expect(picker.endDate).to.equal(DateTime.now().toISODate());
|
|
77
|
+
|
|
78
|
+
await assertScreenshot('datepicker/range-picker-week', getClip(picker));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('can set year range using button', async () => {
|
|
82
|
+
const picker: RangePicker = await createRangePicker(getRangePickerHTML());
|
|
83
|
+
|
|
84
|
+
// Click Year button (3rd button)
|
|
85
|
+
const yearBtn = picker.shadowRoot?.querySelectorAll(
|
|
86
|
+
'.range-btn'
|
|
87
|
+
)[2] as HTMLButtonElement;
|
|
88
|
+
yearBtn.click();
|
|
89
|
+
await picker.updateComplete;
|
|
90
|
+
|
|
91
|
+
expect(picker.selectedRange).to.equal('Y');
|
|
92
|
+
expect(picker.startDate).to.equal(
|
|
93
|
+
DateTime.now().minus({ years: 1 }).plus({ days: 1 }).toISODate()
|
|
94
|
+
);
|
|
95
|
+
expect(picker.endDate).to.equal(DateTime.now().toISODate());
|
|
96
|
+
|
|
97
|
+
await assertScreenshot('datepicker/range-picker-year', getClip(picker));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('can set all range using button', async () => {
|
|
101
|
+
const picker: RangePicker = await createRangePicker(getRangePickerHTML());
|
|
102
|
+
|
|
103
|
+
// Click All button (4th button)
|
|
104
|
+
const allBtn = picker.shadowRoot?.querySelectorAll(
|
|
105
|
+
'.range-btn'
|
|
106
|
+
)[3] as HTMLButtonElement;
|
|
107
|
+
allBtn.click();
|
|
108
|
+
await picker.updateComplete;
|
|
109
|
+
|
|
110
|
+
expect(picker.selectedRange).to.equal('ALL');
|
|
111
|
+
expect(picker.startDate).to.equal('2012-01-01');
|
|
112
|
+
expect(picker.endDate).to.equal(DateTime.now().toISODate());
|
|
113
|
+
|
|
114
|
+
await assertScreenshot('datepicker/range-picker-all', getClip(picker));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('enforces valid date ranges', async () => {
|
|
118
|
+
const picker: RangePicker = await createRangePicker(
|
|
119
|
+
getRangePickerHTML({ start: '2024-06-01', end: '2024-06-30' })
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Verify initial state is valid
|
|
123
|
+
expect(
|
|
124
|
+
DateTime.fromISO(picker.endDate) >= DateTime.fromISO(picker.startDate)
|
|
125
|
+
).to.be.true;
|
|
126
|
+
|
|
127
|
+
// The validation logic is internal and triggered through user interaction
|
|
128
|
+
// We can verify the component has the correct min/max constraints
|
|
129
|
+
expect(picker.startDate).to.equal('2024-06-01');
|
|
130
|
+
expect(picker.endDate).to.equal('2024-06-30');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('enforces min/max date constraints', async () => {
|
|
134
|
+
const picker: RangePicker = await createRangePicker(
|
|
135
|
+
getRangePickerHTML({ min: '2024-01-01', max: '2024-12-31' })
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
expect(picker.minDate).to.equal('2024-01-01');
|
|
139
|
+
expect(picker.maxDate).to.equal('2024-12-31');
|
|
140
|
+
|
|
141
|
+
// Min/max are enforced through the temba-datepicker components
|
|
142
|
+
// when user interacts with the date inputs
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('shows correct button selection states', async () => {
|
|
146
|
+
const picker: RangePicker = await createRangePicker(getRangePickerHTML());
|
|
147
|
+
|
|
148
|
+
// Initially should have M selected
|
|
149
|
+
const monthBtn = picker.shadowRoot?.querySelectorAll(
|
|
150
|
+
'.range-btn'
|
|
151
|
+
)[1] as HTMLButtonElement;
|
|
152
|
+
expect(monthBtn.classList.contains('selected')).to.be.true;
|
|
153
|
+
|
|
154
|
+
// Click week button
|
|
155
|
+
const weekBtn = picker.shadowRoot?.querySelector(
|
|
156
|
+
'.range-btn'
|
|
157
|
+
) as HTMLButtonElement;
|
|
158
|
+
weekBtn.click();
|
|
159
|
+
await picker.updateComplete;
|
|
160
|
+
|
|
161
|
+
expect(weekBtn.classList.contains('selected')).to.be.true;
|
|
162
|
+
expect(monthBtn.classList.contains('selected')).to.be.false;
|
|
163
|
+
|
|
164
|
+
await assertScreenshot(
|
|
165
|
+
'datepicker/range-picker-button-states',
|
|
166
|
+
getClip(picker)
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('can click to edit dates', async () => {
|
|
171
|
+
const picker: RangePicker = await createRangePicker(getRangePickerHTML());
|
|
172
|
+
|
|
173
|
+
// Click on start date display
|
|
174
|
+
const startDisplay = picker.shadowRoot?.querySelector(
|
|
175
|
+
'.date-display'
|
|
176
|
+
) as HTMLElement;
|
|
177
|
+
startDisplay.click();
|
|
178
|
+
await picker.updateComplete;
|
|
179
|
+
|
|
180
|
+
expect(picker.editingStart).to.be.true;
|
|
181
|
+
|
|
182
|
+
// Should show temba-datepicker for start
|
|
183
|
+
const startPicker = picker.shadowRoot?.querySelector(
|
|
184
|
+
'temba-datepicker.start-picker'
|
|
185
|
+
);
|
|
186
|
+
expect(startPicker).to.not.be.null;
|
|
187
|
+
|
|
188
|
+
await assertScreenshot(
|
|
189
|
+
'datepicker/range-picker-editing-start',
|
|
190
|
+
getClip(picker)
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
@@ -649,7 +649,7 @@ describe('temba-select', () => {
|
|
|
649
649
|
// assert.equal(select.visibleOptions.length, 2);
|
|
650
650
|
});
|
|
651
651
|
|
|
652
|
-
|
|
652
|
+
xit('pages through cursor results', async () => {
|
|
653
653
|
const select = await createSelect(
|
|
654
654
|
clock,
|
|
655
655
|
getSelectHTML([], {
|
|
@@ -210,6 +210,13 @@ describe('temba-webchat', () => {
|
|
|
210
210
|
status: 'connected'
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
+
// the cursor is blinking, we need to account for it in our screenshot by making it transparent
|
|
214
|
+
const inputField = webChat.shadowRoot.querySelector(
|
|
215
|
+
'.input'
|
|
216
|
+
) as HTMLInputElement;
|
|
217
|
+
expect(inputField).to.exist;
|
|
218
|
+
inputField.style.caretColor = 'transparent';
|
|
219
|
+
|
|
213
220
|
await assertScreenshot('webchat/connected-state', getClip(webChat));
|
|
214
221
|
});
|
|
215
222
|
});
|
|
@@ -152,6 +152,8 @@ const wireScreenshots = async (page, context, wait, replaceScreenshots) => {
|
|
|
152
152
|
|
|
153
153
|
// Only wait for network idle if explicitly requested
|
|
154
154
|
if (wait) {
|
|
155
|
+
await page.waitForNetworkIdle();
|
|
156
|
+
} else {
|
|
155
157
|
await page.waitForNetworkIdle({ idleTime: 100, timeout: 1000 });
|
|
156
158
|
}
|
|
157
159
|
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en-GB">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<title>Date Picker Examples</title>
|
|
6
|
-
<link
|
|
7
|
-
href="/static/css/temba-components.css"
|
|
8
|
-
rel="stylesheet"
|
|
9
|
-
type="text/css"
|
|
10
|
-
/>
|
|
11
|
-
<link
|
|
12
|
-
href="https://fonts.googleapis.com/css?family=Roboto+Mono:300|Roboto:300,400,500"
|
|
13
|
-
rel="stylesheet"
|
|
14
|
-
/>
|
|
15
|
-
<link
|
|
16
|
-
href="../styles.css"
|
|
17
|
-
rel="stylesheet"
|
|
18
|
-
type="text/css"
|
|
19
|
-
/>
|
|
20
|
-
</head>
|
|
21
|
-
<body>
|
|
22
|
-
<h1>Date Picker Examples</h1>
|
|
23
|
-
<p><a href="../index.html">← Back to main demo</a></p>
|
|
24
|
-
|
|
25
|
-
<div class="example">
|
|
26
|
-
<h3>Date and Time Picker</h3>
|
|
27
|
-
<p>A date picker that includes time selection</p>
|
|
28
|
-
<temba-datepicker
|
|
29
|
-
time
|
|
30
|
-
value="2020-01-20T14:00+00:00"
|
|
31
|
-
timezone="UTC"
|
|
32
|
-
></temba-datepicker>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
<div class="example">
|
|
36
|
-
<h3>Date Only Picker</h3>
|
|
37
|
-
<p>A date picker without time selection</p>
|
|
38
|
-
<temba-datepicker
|
|
39
|
-
value="2022-10-09"
|
|
40
|
-
></temba-datepicker>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<div class="example">
|
|
44
|
-
<h3>Date Picker with Timezone</h3>
|
|
45
|
-
<p>A date picker with a specific timezone</p>
|
|
46
|
-
<temba-datepicker
|
|
47
|
-
time
|
|
48
|
-
value="2022-10-02 01:00:00+00:00"
|
|
49
|
-
timezone="Africa/Lagos"
|
|
50
|
-
></temba-datepicker>
|
|
51
|
-
</div>
|
|
52
|
-
|
|
53
|
-
<div class="example">
|
|
54
|
-
<h3>Empty Date Picker</h3>
|
|
55
|
-
<p>A date picker with no initial value</p>
|
|
56
|
-
<temba-datepicker></temba-datepicker>
|
|
57
|
-
</div>
|
|
58
|
-
|
|
59
|
-
<script>
|
|
60
|
-
function handleDateChange(event) {
|
|
61
|
-
console.log('Date changed:', event.target.value);
|
|
62
|
-
}
|
|
63
|
-
</script>
|
|
64
|
-
|
|
65
|
-
<script type="module">
|
|
66
|
-
import '../../out-tsc/temba-modules.js';
|
|
67
|
-
</script>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|