@operato/event-view 10.0.0 → 10.11.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 +37 -0
  2. package/coverage/lcov-report/base.css +224 -0
  3. package/coverage/lcov-report/block-navigation.js +87 -0
  4. package/coverage/lcov-report/calendar-monthly-styles.ts.html +466 -0
  5. package/coverage/lcov-report/calendar-monthly.ts.html +769 -0
  6. package/coverage/lcov-report/calendar-weekly-styles.ts.html +508 -0
  7. package/coverage/lcov-report/calendar-weekly.ts.html +739 -0
  8. package/coverage/lcov-report/favicon.png +0 -0
  9. package/coverage/lcov-report/index.html +176 -0
  10. package/coverage/lcov-report/ox-event-view.ts.html +280 -0
  11. package/coverage/lcov-report/prettify.css +1 -0
  12. package/coverage/lcov-report/prettify.js +2 -0
  13. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  14. package/coverage/lcov-report/sorter.js +210 -0
  15. package/coverage/lcov-report/types.ts.html +199 -0
  16. package/coverage/lcov.info +6577 -0
  17. package/dist/src/calendar-monthly-styles.js +260 -16
  18. package/dist/src/calendar-monthly-styles.js.map +1 -1
  19. package/dist/src/calendar-monthly.d.ts +8 -1
  20. package/dist/src/calendar-monthly.js +90 -17
  21. package/dist/src/calendar-monthly.js.map +1 -1
  22. package/dist/src/calendar-weekly-styles.js +172 -21
  23. package/dist/src/calendar-weekly-styles.js.map +1 -1
  24. package/dist/src/calendar-weekly.d.ts +8 -1
  25. package/dist/src/calendar-weekly.js +92 -12
  26. package/dist/src/calendar-weekly.js.map +1 -1
  27. package/dist/src/day-overflow.d.ts +42 -0
  28. package/dist/src/day-overflow.js +71 -0
  29. package/dist/src/day-overflow.js.map +1 -0
  30. package/dist/src/index.d.ts +1 -0
  31. package/dist/src/index.js +1 -0
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/ox-event-view.d.ts +18 -1
  34. package/dist/src/ox-event-view.js +73 -15
  35. package/dist/src/ox-event-view.js.map +1 -1
  36. package/dist/src/types.d.ts +66 -1
  37. package/dist/src/types.js +64 -1
  38. package/dist/src/types.js.map +1 -1
  39. package/dist/test/calendar-layout.test.d.ts +1 -0
  40. package/dist/test/calendar-layout.test.js +188 -0
  41. package/dist/test/calendar-layout.test.js.map +1 -0
  42. package/dist/test/day-overflow.test.d.ts +1 -0
  43. package/dist/test/day-overflow.test.js +81 -0
  44. package/dist/test/day-overflow.test.js.map +1 -0
  45. package/dist/test/day-state.test.d.ts +1 -0
  46. package/dist/test/day-state.test.js +115 -0
  47. package/dist/test/day-state.test.js.map +1 -0
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/package.json +4 -2
  50. package/test/calendar-layout.test.ts +238 -0
  51. package/test/day-overflow.test.ts +103 -0
  52. package/test/day-state.test.ts +139 -0
@@ -0,0 +1,103 @@
1
+ import { html, fixture, expect } from '@open-wc/testing'
2
+
3
+ import '../src/ox-event-view.js'
4
+ import { OxEventView } from '../src/ox-event-view.js'
5
+ import { BizEvent, CALENDAR, EventProvider } from '../src/types.js'
6
+ import { DayOverflow } from '../src/day-overflow.js'
7
+
8
+ /**
9
+ * A cell takes a ceiling so one busy day does not set the height of every day beside it, and
10
+ * says what the ceiling cut so the host can fold harder.
11
+ *
12
+ * The ceiling on its own would be the defect it replaces - the old "+763 more" hid rows the same
13
+ * way. What makes it different is that somebody is told.
14
+ */
15
+
16
+ const ROW_HEIGHT = 20
17
+
18
+ function providerWith(rowsOnFirstDay: number): EventProvider {
19
+ return {
20
+ async fetchEventsForCalendar(calendar: CALENDAR) {
21
+ const events: BizEvent[] = Array.from({ length: rowsOnFirstDay }, (unused, index) => ({
22
+ title: `row ${index}`,
23
+ color: 'rgb(0, 0, 0)',
24
+ template: html`<div style="height: ${ROW_HEIGHT}px">row ${index}</div>`
25
+ }))
26
+
27
+ return new Map([[calendar[0].date, events]])
28
+ }
29
+ }
30
+ }
31
+
32
+ async function calendarWith(rows: number, cap?: string) {
33
+ const el = (await fixture(html`
34
+ <ox-event-view mode="monthly" style=${cap ? `--calendar-monthly-date-max-height:${cap}` : ''}></ox-event-view>
35
+ `)) as OxEventView
36
+
37
+ const seen: DayOverflow[][] = []
38
+ el.addEventListener('day-overflow', event => seen.push((event as CustomEvent<DayOverflow[]>).detail))
39
+
40
+ el.eventProvider = providerWith(rows)
41
+ await el.updateComplete
42
+ await new Promise(resolve => setTimeout(resolve, 80))
43
+
44
+ const root = el.renderRoot.querySelector('calendar-monthly')!.shadowRoot!
45
+ return { root, seen }
46
+ }
47
+
48
+ describe('a day cell that cannot hold everything says so', () => {
49
+ it('stays uncapped, and silent, when no theme sets a ceiling', async () => {
50
+ /*
51
+ * The default has to be none. A ceiling is only safe once the host folds its rows, so a
52
+ * component that capped by default would start hiding rows in every app that upgraded.
53
+ */
54
+ const { root, seen } = await calendarWith(40)
55
+ const cell = root.querySelector('li[date]') as HTMLElement
56
+
57
+ expect(getComputedStyle(cell).maxHeight).to.equal('none')
58
+ expect(cell.getBoundingClientRect().height, 'the cell grew to hold all forty').to.be.greaterThan(40 * ROW_HEIGHT)
59
+ expect(seen, 'nothing was cut, so nothing is reported').to.deep.equal([])
60
+ })
61
+
62
+ it('reports the rows a ceiling cut, and which day lost them', async () => {
63
+ const { root, seen } = await calendarWith(40, '160px')
64
+ const cell = root.querySelector('li[date]') as HTMLElement
65
+
66
+ expect(cell.getBoundingClientRect().height, 'held to the ceiling').to.be.at.most(161)
67
+
68
+ const last = seen[seen.length - 1]
69
+ expect(last, 'the host was told').to.not.be.undefined
70
+ expect(last.length).to.equal(1)
71
+ expect(last[0].hidden, 'roughly the rows past 160px, not all forty').to.be.greaterThan(20)
72
+ expect(last[0].date, 'named by day, so the host knows where to fold').to.be.instanceOf(Date)
73
+ })
74
+
75
+ it('keeps every other cell off the busy day\'s height', async () => {
76
+ /*
77
+ * The measured failure: a grid row is as tall as its tallest cell, so 767 events on the 8th
78
+ * made all seven of the week view's cells 670px, the two empty ones included.
79
+ */
80
+ const { root } = await calendarWith(40, '160px')
81
+ const cells = Array.from(root.querySelectorAll('li[date]')) as HTMLElement[]
82
+ const heights = cells.map(cell => Math.round(cell.getBoundingClientRect().height))
83
+
84
+ expect(Math.max(...heights)).to.be.at.most(161)
85
+ })
86
+
87
+ it('does not repeat itself when nothing changed', async () => {
88
+ /*
89
+ * This measurement runs after every render, and a host that re-renders in response would
90
+ * otherwise be told the same thing forever.
91
+ */
92
+ const { seen } = await calendarWith(40, '160px')
93
+ const signatures = seen.map(one => one.map(day => `${day.date?.getTime()}:${day.hidden}`).join(','))
94
+
95
+ expect(new Set(signatures).size, 'one distinct report, however many renders').to.equal(signatures.length)
96
+ })
97
+
98
+ it('says nothing for a day that fits under the ceiling', async () => {
99
+ const { seen } = await calendarWith(2, '160px')
100
+
101
+ expect(seen.flat()).to.deep.equal([])
102
+ })
103
+ })
@@ -0,0 +1,139 @@
1
+ import { html, fixture, expect } from '@open-wc/testing'
2
+
3
+ import '../src/ox-event-view.js'
4
+ import { OxEventView } from '../src/ox-event-view.js'
5
+ import { CALENDAR, DayState, EventProvider } from '../src/types.js'
6
+
7
+ /**
8
+ * A host can say something about a day itself, not only about what happened in it.
9
+ *
10
+ * Before this there was no such channel: CALENDAR carried only what the component works out
11
+ * (which month a date is in, whether it is today), so a screen wanting to mark a holiday had to
12
+ * send an event, and it drew as one more 6px dot among the day's actual work. That was the
13
+ * component's shape, not the screen's choice.
14
+ */
15
+
16
+ function providerMarking(pick: (calendar: CALENDAR) => Date[], state: DayState): EventProvider {
17
+ return {
18
+ async fetchEventsForCalendar() {
19
+ return new Map()
20
+ },
21
+ async fetchDayStatesForCalendar(calendar: CALENDAR) {
22
+ /*
23
+ * Key by the very Date objects the component handed over. The component looks the day up
24
+ * by identity, the way it already does for events - a fresh Date with the same instant
25
+ * would not match, and a host that built its own would silently mark nothing.
26
+ */
27
+ return new Map(pick(calendar).map(date => [date, state]))
28
+ }
29
+ }
30
+ }
31
+
32
+ const HOLIDAY: DayState = { name: 'non-working', color: 'rgb(1, 2, 3)' }
33
+
34
+ /*
35
+ * A bare test page carries no theme, and every surface in this component is a var() onto one.
36
+ * Without these the month cells compute to transparent and "the state did not paint" looks the
37
+ * same as "nothing paints here at all" — which is precisely the confusion these tests exist to
38
+ * rule out. Only the tokens the cell surfaces read.
39
+ */
40
+ const THEME: Record<string, string> = {
41
+ '--md-sys-color-surface-container-lowest': 'rgb(255, 255, 255)',
42
+ '--md-sys-color-surface-variant': 'rgb(232, 235, 238)',
43
+ '--md-sys-color-surface': 'rgb(240, 242, 244)',
44
+ '--md-sys-color-tertiary-container': 'rgb(200, 220, 255)',
45
+ '--md-sys-color-primary': 'rgb(38, 46, 56)'
46
+ }
47
+
48
+ before(() => Object.entries(THEME).forEach(([name, value]) => document.documentElement.style.setProperty(name, value)))
49
+ after(() => Object.keys(THEME).forEach(name => document.documentElement.style.removeProperty(name)))
50
+
51
+ async function calendarWith(provider: EventProvider | undefined, mode: 'monthly' | 'weekly') {
52
+ const el = (await fixture(html`<ox-event-view mode=${mode}></ox-event-view>`)) as OxEventView
53
+ if (provider) el.eventProvider = provider
54
+ await el.updateComplete
55
+ /* The grid is filled in updated(), after the provider has answered. */
56
+ await new Promise(resolve => setTimeout(resolve, 50))
57
+
58
+ const view = el.renderRoot.querySelector(mode === 'monthly' ? 'calendar-monthly' : 'calendar-weekly')!
59
+ return view.shadowRoot!
60
+ }
61
+
62
+ describe('a host can mark the day itself', () => {
63
+ it('paints the marked day and leaves the rest alone', async () => {
64
+ const root = await calendarWith(providerMarking(calendar => [calendar[10].date], HOLIDAY), 'monthly')
65
+
66
+ const marked = root.querySelectorAll('li[day-state]')
67
+ expect(marked.length, 'exactly the day the host named').to.equal(1)
68
+ expect(getComputedStyle(marked[0] as HTMLElement).backgroundColor).to.equal('rgb(1, 2, 3)')
69
+ expect(marked[0].getAttribute('day-state'), 'the host word reaches the dom').to.equal('non-working')
70
+ })
71
+
72
+ it('leaves every cell unmarked when the host does not answer', async () => {
73
+ /* The old EventProvider shape, which must keep working - the method is optional. */
74
+ const root = await calendarWith({ async fetchEventsForCalendar() { return new Map() } }, 'monthly')
75
+
76
+ expect(root.querySelectorAll('li[day-state]').length).to.equal(0)
77
+ })
78
+
79
+ it('does not blank a cell when there is no state to draw', async () => {
80
+ /*
81
+ * The failure this guards. `background-color: var(--day-state-color)` with the property
82
+ * unset is invalid at computed-value time, which resolves to transparent - so an attribute
83
+ * written without a colour would erase the month surface underneath instead of adding to
84
+ * it. Unmarked days must keep the surface the month view gave them.
85
+ */
86
+ const root = await calendarWith(providerMarking(calendar => [calendar[10].date], HOLIDAY), 'monthly')
87
+
88
+ const plain = root.querySelector('li[current-month]:not([day-state])') as HTMLElement
89
+ expect(getComputedStyle(plain).backgroundColor, 'the month surface, untouched').to.equal('rgb(255, 255, 255)')
90
+ })
91
+
92
+ it('marks both cells of a day in the week view', async () => {
93
+ /*
94
+ * A day is two cells stacked there - the date band and the events under it. Marking only
95
+ * the band would tint a 37px strip and leave the 600px body untouched.
96
+ */
97
+ const root = await calendarWith(providerMarking(calendar => [calendar[3].date], HOLIDAY), 'weekly')
98
+
99
+ const marked = Array.from(root.querySelectorAll('li[day-state]')) as HTMLElement[]
100
+ expect(marked.length).to.equal(2)
101
+ expect(marked.some(li => li.hasAttribute('day-of-week')), 'the date band').to.be.true
102
+ expect(marked.some(li => !li.hasAttribute('day-of-week')), 'the events under it').to.be.true
103
+ marked.forEach(li => expect(getComputedStyle(li).backgroundColor).to.equal('rgb(1, 2, 3)'))
104
+ })
105
+
106
+ it('lets a selection win over the day state', async () => {
107
+ /*
108
+ * Both rules are (0,1,1), so the order they are written in is what decides. A day you have
109
+ * selected should look selected even if it is a holiday; a holiday you have not touched
110
+ * should look like a holiday. Get the order wrong and dragging across a holiday leaves a
111
+ * hole in the selection.
112
+ */
113
+ const root = await calendarWith(providerMarking(calendar => [calendar[10].date], HOLIDAY), 'monthly')
114
+ const cell = root.querySelector('li[day-state]') as HTMLElement
115
+
116
+ cell.setAttribute('selected', '')
117
+
118
+ expect(getComputedStyle(cell).backgroundColor).to.not.equal('rgb(1, 2, 3)')
119
+ })
120
+
121
+ it('keeps today readable on a marked day', async () => {
122
+ /*
123
+ * Today rides on a background-image and an outline, precisely so it survives whatever claims
124
+ * the background colour. A holiday that is also today has to show both.
125
+ */
126
+ const root = await calendarWith(
127
+ providerMarking(calendar => calendar.filter(one => one.today).map(one => one.date), HOLIDAY),
128
+ 'monthly'
129
+ )
130
+ const today = root.querySelector('li[today]') as HTMLElement
131
+ const style = getComputedStyle(today)
132
+
133
+ expect(today.hasAttribute('day-state'), 'today was the day marked').to.be.true
134
+ expect(style.backgroundColor, 'the holiday surface').to.equal('rgb(1, 2, 3)')
135
+ expect(style.backgroundImage, 'today, still drawn on top').to.not.equal('none')
136
+ expect(style.outlineStyle, 'and the ring, which is an outline so it can sit inset').to.equal('solid')
137
+ expect(parseFloat(style.outlineWidth)).to.be.greaterThan(0)
138
+ })
139
+ })