@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/demo/chart/horizontal-demo.html +81 -0
  3. package/demo/components/datepicker/example.html +63 -0
  4. package/demo/components/datepicker/range-picker-demo.html +161 -0
  5. package/demo/index.html +8 -0
  6. package/demo/static/css/prism.css +2 -0
  7. package/demo/static/js/prism-loader.js +12 -0
  8. package/demo/styles.css +71 -1
  9. package/dist/temba-components.js +172 -10
  10. package/dist/temba-components.js.map +1 -1
  11. package/out-tsc/src/chart/TembaChart.js +116 -59
  12. package/out-tsc/src/chart/TembaChart.js.map +1 -1
  13. package/out-tsc/src/datepicker/DatePicker.js +11 -1
  14. package/out-tsc/src/datepicker/DatePicker.js.map +1 -1
  15. package/out-tsc/src/datepicker/RangePicker.js +595 -0
  16. package/out-tsc/src/datepicker/RangePicker.js.map +1 -0
  17. package/out-tsc/src/interfaces.js +1 -0
  18. package/out-tsc/src/interfaces.js.map +1 -1
  19. package/out-tsc/temba-modules.js +3 -1
  20. package/out-tsc/temba-modules.js.map +1 -1
  21. package/out-tsc/test/temba-chart.test.js +36 -0
  22. package/out-tsc/test/temba-chart.test.js.map +1 -1
  23. package/out-tsc/test/temba-datepicker.test.js +1 -1
  24. package/out-tsc/test/temba-datepicker.test.js.map +1 -1
  25. package/out-tsc/test/temba-range-picker.test.js +123 -0
  26. package/out-tsc/test/temba-range-picker.test.js.map +1 -0
  27. package/out-tsc/test/temba-select.test.js +1 -1
  28. package/out-tsc/test/temba-select.test.js.map +1 -1
  29. package/out-tsc/test/temba-webchat.test.js +4 -0
  30. package/out-tsc/test/temba-webchat.test.js.map +1 -1
  31. package/package.json +1 -1
  32. package/screenshots/truth/datepicker/range-picker-all.png +0 -0
  33. package/screenshots/truth/datepicker/range-picker-button-states.png +0 -0
  34. package/screenshots/truth/datepicker/range-picker-default.png +0 -0
  35. package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
  36. package/screenshots/truth/datepicker/range-picker-initial-values.png +0 -0
  37. package/screenshots/truth/datepicker/range-picker-min-max.png +0 -0
  38. package/screenshots/truth/datepicker/range-picker-week.png +0 -0
  39. package/screenshots/truth/datepicker/range-picker-year.png +0 -0
  40. package/screenshots/truth/webchat/connected-state.png +0 -0
  41. package/src/chart/TembaChart.ts +124 -63
  42. package/src/datepicker/DatePicker.ts +9 -1
  43. package/src/datepicker/RangePicker.ts +602 -0
  44. package/src/interfaces.ts +2 -1
  45. package/temba-modules.ts +3 -1
  46. package/test/temba-chart.test.ts +47 -0
  47. package/test/temba-datepicker.test.ts +1 -1
  48. package/test/temba-range-picker.test.ts +193 -0
  49. package/test/temba-select.test.ts +1 -1
  50. package/test/temba-webchat.test.ts +7 -0
  51. package/web-test-runner.config.mjs +2 -0
  52. 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
- it('pages through cursor results', async () => {
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>