@playpilot/tpi 8.27.1 → 8.28.0-beta.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.27.1",
3
+ "version": "8.28.0-beta.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -23,7 +23,7 @@ export async function fetchLinkInjections(
23
23
 
24
24
  if (!apiToken) throw new Error('No token was provided')
25
25
 
26
- const apiUrl = `/external-pages/?api-token=${apiToken}&include_title_details=true${isEditorialMode ? '&editorial_mode_enabled=true' : ''}&language=${language}`
26
+ const apiUrl = `/external-pages/?api-token=${apiToken}&include_title_details=true&include_participant_details=true${isEditorialMode ? '&editorial_mode_enabled=true' : ''}&language=${language}`
27
27
  let response: LinkInjectionResponse
28
28
 
29
29
  if (method === 'POST') {
@@ -1,160 +1,160 @@
1
- <script lang="ts">
2
- import { onMount } from 'svelte'
3
- import { heading } from '$lib/actions/heading'
4
- import { fetchTitlesForParticipant } from '$lib/api/participants'
5
- import { openModal } from '$lib/modal'
6
- import type { ParticipantData } from '$lib/types/participant'
7
- import type { TitleData } from '$lib/types/title'
8
- import ListTitle from './ListTitle.svelte'
9
- import { t } from '$lib/localization'
10
- import { isRetargetingAllowed } from '$lib/retargeting'
11
- import { trackViaPixel } from '@playpilot/retargeting-tracking'
12
- import { MetaEvent } from '$lib/enums/TrackingEvent'
13
-
14
- interface Props {
15
- participant: ParticipantData
16
- small?: boolean
17
- }
18
-
19
- const { participant, small = false }: Props = $props()
20
-
21
- const { name, birth_date, death_date } = $derived(participant)
22
-
23
- const pageSize = 30
24
-
25
- let titles: TitleData[] = $state([])
26
- let page = $state(1)
27
- let hasMorePages = $state(true)
28
- let loading = $state(true)
29
-
30
- onMount(loadMore)
31
-
32
- if (isRetargetingAllowed()) trackViaPixel(MetaEvent.ParticipantView, { participant: participant.name })
33
-
34
- function formatDate(dateString: string): string {
35
- const date = new Date(dateString)
36
- return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
37
- }
38
-
39
- async function loadMore(): Promise<void> {
40
- loading = true
41
-
42
- try {
43
- const results = await fetchTitlesForParticipant(participant, { page })
44
-
45
- titles = [...titles, ...results]
46
- hasMorePages = results?.length >= pageSize
47
- } catch {
48
- hasMorePages = false
49
- } finally {
50
- loading = false
51
- page += 1
52
- }
53
- }
54
- </script>
55
-
56
- <div class="header" class:small>
57
- <div class="heading" use:heading={2} id="heading">{name}</div>
58
-
59
- {#if birth_date}
60
- <p class="dates">
61
- {t('Born On')} <strong>{formatDate(birth_date)}</strong>{#if death_date}, {t('Died On')} <strong>{formatDate(death_date)}</strong>{/if}
62
- </p>
63
- {/if}
64
- </div>
65
-
66
- <div class="content">
67
- {#if !small}
68
- <div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
69
- {/if}
70
-
71
- <div class="list">
72
- {#each titles as title}
73
- <ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
74
- {/each}
75
-
76
- {#if loading}
77
- {#each { length: 6 }}
78
- <div class="skeleton" data-testid="skeleton"></div>
79
- {/each}
80
- {:else if hasMorePages}
81
- <button class="more" onclick={loadMore}>{t('Show More')}</button>
82
- {/if}
83
- </div>
84
- </div>
85
-
86
- <style lang="scss">
87
- .header {
88
- padding: margin(4) margin(1) margin(2);
89
- font-family: theme(detail-font-family, font-family);
90
- font-weight: theme(detail-font-weight, normal);
91
- font-size: theme(detail-font-size, font-size-base);
92
- line-height: theme(participant-description-line-height, normal);
93
- color: theme(detail-text-color, text-color);
94
-
95
- &.small {
96
- padding: margin(1);
97
- }
98
- }
99
-
100
- .heading {
101
- margin: 0;
102
- font-family: theme(detail-title-font-family, font-family);
103
- font-weight: theme(detail-title-font-weight, lighter);
104
- font-size: theme(detail-title-font-size, margin(1.5));
105
- line-height: normal;
106
- font-style: theme(detail-title-font-style, normal);
107
-
108
- &.subheading {
109
- margin: 0 0 margin(0.5);
110
- font-size: theme(detail-title-small-font-size, margin(1.25));
111
- }
112
- }
113
-
114
- .dates {
115
- margin: margin(0.5) 0 0;
116
- }
117
-
118
- .content {
119
- padding: 0 margin(1) margin(1);
120
- color: theme(detail-text-color, text-color);
121
- font-family: theme(detail-font-family, font-family);
122
- font-weight: theme(detail-font-weight, normal);
123
- font-size: theme(detail-font-size, font-size-base);
124
- line-height: normal;
125
- font-style: normal;
126
-
127
- ::view-transition-old(content),
128
- ::view-transition-new(content) {
129
- height: 100%;
130
- }
131
- }
132
-
133
- .list {
134
- display: flex;
135
- flex-direction: column;
136
- gap: margin(0.5);
137
- }
138
-
139
- .skeleton {
140
- min-height: margin(7.25);
141
- border-radius: theme(playlink-border-radius, border-radius);
142
- background: theme(detail-background-light, lighter);
143
- }
144
-
145
- .more {
146
- cursor: pointer;
147
- appearance: none;
148
- padding: margin(0.5) margin(1);
149
- border: 0;
150
- border-radius: theme(participants-load-more-border-radius, border-radius);
151
- background: theme(participants-load-more-background, content);
152
- color: inherit;
153
- font-size: inherit;
154
- font-family: inherit;
155
-
156
- &:hover {
157
- filter: brightness(1.2);
158
- }
159
- }
160
- </style>
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte'
3
+ import { heading } from '$lib/actions/heading'
4
+ import { fetchTitlesForParticipant } from '$lib/api/participants'
5
+ import { openModal } from '$lib/modal'
6
+ import type { ParticipantData } from '$lib/types/participant'
7
+ import type { TitleData } from '$lib/types/title'
8
+ import ListTitle from './ListTitle.svelte'
9
+ import { t } from '$lib/localization'
10
+ import { isRetargetingAllowed } from '$lib/retargeting'
11
+ import { trackViaPixel } from '@playpilot/retargeting-tracking'
12
+ import { MetaEvent } from '$lib/enums/TrackingEvent'
13
+
14
+ interface Props {
15
+ participant: ParticipantData
16
+ small?: boolean
17
+ }
18
+
19
+ const { participant, small = false }: Props = $props()
20
+
21
+ const { name, birth_date, death_date } = $derived(participant)
22
+
23
+ const pageSize = 30
24
+
25
+ let titles: TitleData[] = $state([])
26
+ let page = $state(1)
27
+ let hasMorePages = $state(true)
28
+ let loading = $state(true)
29
+
30
+ onMount(loadMore)
31
+
32
+ if (isRetargetingAllowed()) trackViaPixel(MetaEvent.ParticipantView, { participant: participant.name })
33
+
34
+ function formatDate(dateString: string): string {
35
+ const date = new Date(dateString)
36
+ return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
37
+ }
38
+
39
+ async function loadMore(): Promise<void> {
40
+ loading = true
41
+
42
+ try {
43
+ const results = await fetchTitlesForParticipant(participant, { page })
44
+
45
+ titles = [...titles, ...results]
46
+ hasMorePages = results?.length >= pageSize
47
+ } catch {
48
+ hasMorePages = false
49
+ } finally {
50
+ loading = false
51
+ page += 1
52
+ }
53
+ }
54
+ </script>
55
+
56
+ <div class="header" class:small>
57
+ <div class="heading" use:heading={2} id="heading">{name}</div>
58
+
59
+ {#if birth_date}
60
+ <p class="dates">
61
+ {t('Born On')} <strong>{formatDate(birth_date)}</strong>{#if death_date}, {t('Died On')} <strong>{formatDate(death_date)}</strong>{/if}
62
+ </p>
63
+ {/if}
64
+ </div>
65
+
66
+ <div class="content">
67
+ {#if !small}
68
+ <div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
69
+ {/if}
70
+
71
+ <div class="list">
72
+ {#each titles as title}
73
+ <ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
74
+ {/each}
75
+
76
+ {#if loading}
77
+ {#each { length: 6 }}
78
+ <div class="skeleton" data-testid="skeleton"></div>
79
+ {/each}
80
+ {:else if hasMorePages}
81
+ <button class="more" onclick={loadMore}>{t('Show More')}</button>
82
+ {/if}
83
+ </div>
84
+ </div>
85
+
86
+ <style lang="scss">
87
+ .header {
88
+ padding: margin(4) margin(1) margin(2);
89
+ font-family: theme(detail-font-family, font-family);
90
+ font-weight: theme(detail-font-weight, normal);
91
+ font-size: theme(detail-font-size, font-size-base);
92
+ line-height: theme(participant-description-line-height, normal);
93
+ color: theme(detail-text-color, text-color);
94
+
95
+ &.small {
96
+ padding: margin(1);
97
+ }
98
+ }
99
+
100
+ .heading {
101
+ margin: 0;
102
+ font-family: theme(detail-title-font-family, font-family);
103
+ font-weight: theme(detail-title-font-weight, lighter);
104
+ font-size: theme(detail-title-font-size, margin(1.5));
105
+ line-height: normal;
106
+ font-style: theme(detail-title-font-style, normal);
107
+
108
+ &.subheading {
109
+ margin: 0 0 margin(0.5);
110
+ font-size: theme(detail-title-small-font-size, margin(1.25));
111
+ }
112
+ }
113
+
114
+ .dates {
115
+ margin: margin(0.5) 0 0;
116
+ }
117
+
118
+ .content {
119
+ padding: 0 margin(1) margin(1);
120
+ color: theme(detail-text-color, text-color);
121
+ font-family: theme(detail-font-family, font-family);
122
+ font-weight: theme(detail-font-weight, normal);
123
+ font-size: theme(detail-font-size, font-size-base);
124
+ line-height: normal;
125
+ font-style: normal;
126
+
127
+ ::view-transition-old(content),
128
+ ::view-transition-new(content) {
129
+ height: 100%;
130
+ }
131
+ }
132
+
133
+ .list {
134
+ display: flex;
135
+ flex-direction: column;
136
+ gap: margin(0.5);
137
+ }
138
+
139
+ .skeleton {
140
+ min-height: margin(7.25);
141
+ border-radius: theme(playlink-border-radius, border-radius);
142
+ background: theme(detail-background-light, lighter);
143
+ }
144
+
145
+ .more {
146
+ cursor: pointer;
147
+ appearance: none;
148
+ padding: margin(0.5) margin(1);
149
+ border: 0;
150
+ border-radius: theme(participants-load-more-border-radius, border-radius);
151
+ background: theme(participants-load-more-background, content);
152
+ color: inherit;
153
+ font-size: inherit;
154
+ font-family: inherit;
155
+
156
+ &:hover {
157
+ filter: brightness(1.2);
158
+ }
159
+ }
160
+ </style>
@@ -40,7 +40,7 @@ describe('$lib/api/externalPages', () => {
40
40
 
41
41
  expect(response).toBe('Some response')
42
42
  expect(api).toHaveBeenCalledWith(
43
- '/external-pages/?api-token=a&include_title_details=true&language=en-US&url=https://some-url',
43
+ '/external-pages/?api-token=a&include_title_details=true&include_participant_details=true&language=en-US&url=https://some-url',
44
44
  expect.objectContaining({
45
45
  method: 'GET',
46
46
  }),
@@ -1,108 +1,108 @@
1
- import { cleanup } from '@testing-library/svelte'
2
- import { afterEach, beforeAll, beforeEach, vi } from 'vitest'
3
- import { fakeFetch } from './helpers'
4
-
5
- vi.mock('svelte/transition')
6
-
7
- // https://github.com/jsdom/jsdom/issues/3429#issuecomment-1936128876
8
- const mockAnimations = () => {
9
- Element.prototype.animate ??= vi.fn().mockReturnValue({
10
- onfinish: null,
11
- finished: Promise.resolve(),
12
- cancel: vi.fn(),
13
- startTime: null,
14
- currentTime: null,
15
- })
16
-
17
- Element.prototype.getAnimations ??= vi.fn().mockReturnValue([])
18
- }
19
-
20
- const mockLocalStorage = () => {
21
- /** @type {Record<string, string>} */
22
- let store = {}
23
-
24
- // @ts-ignore
25
- ;(window).localStorage = {
26
- getItem: function (key) {
27
- return store[key] || null
28
- },
29
- setItem: function (key, value) {
30
- store[key] = value.toString()
31
- },
32
- removeItem: function (key) {
33
- delete store[key]
34
- },
35
- clear: function () {
36
- store = {}
37
- },
38
- }
39
- }
40
-
41
- const mockSessionStorage = () => {
42
- /** @type {Record<string, string>} */
43
- let store = {}
44
-
45
- // @ts-ignore
46
- ;(window).sessionStorage = {
47
- getItem: function (key) {
48
- return store[key] || null
49
- },
50
- setItem: function (key, value) {
51
- store[key] = value.toString()
52
- },
53
- removeItem: function (key) {
54
- delete store[key]
55
- },
56
- clear: function () {
57
- store = {}
58
- },
59
- }
60
- }
61
-
62
- export const mockIntersectionObserver = () => {
63
- // @ts-ignore
64
- global.IntersectionObserver = vi.fn((callback) => ({
65
- disconnect: vi.fn(),
66
- observe: vi.fn(() => callback([{ isIntersecting: true }])),
67
- unobserve: vi.fn(),
68
- }))
69
- }
70
-
71
- beforeAll(() => {
72
- fakeFetch()
73
- mockLocalStorage()
74
- mockSessionStorage()
75
- mockIntersectionObserver()
76
- })
77
-
78
- beforeEach(() => {
79
- // @ts-ignore
80
- window.PlayPilotLinkInjections = { token: 'some-token', require_consent: false }
81
-
82
- // Enable all consent
83
- // @ts-ignore
84
- window.__tcfapi = (command, _version, callback) => {
85
- if (command !== 'addEventListener') return
86
-
87
- setTimeout(() => callback({
88
- purpose: { consents: { 1: true, 2: true, 7: true, 8: true, 9: true, 10: true } },
89
- eventStatus: 'tcloaded',
90
- }, true), 0)
91
-
92
- return 1
93
- }
94
-
95
- // Reset cookies
96
- document.cookie.split(';').forEach((cookie) => document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, '=;'))
97
-
98
- window.HTMLElement.prototype.scrollIntoView = vi.fn()
99
-
100
- localStorage.clear()
101
- sessionStorage.clear()
102
-
103
- mockAnimations()
104
- })
105
-
106
- afterEach(() => {
107
- cleanup()
108
- })
1
+ import { cleanup } from '@testing-library/svelte'
2
+ import { afterEach, beforeAll, beforeEach, vi } from 'vitest'
3
+ import { fakeFetch } from './helpers'
4
+
5
+ vi.mock('svelte/transition')
6
+
7
+ // https://github.com/jsdom/jsdom/issues/3429#issuecomment-1936128876
8
+ const mockAnimations = () => {
9
+ Element.prototype.animate ??= vi.fn().mockReturnValue({
10
+ onfinish: null,
11
+ finished: Promise.resolve(),
12
+ cancel: vi.fn(),
13
+ startTime: null,
14
+ currentTime: null,
15
+ })
16
+
17
+ Element.prototype.getAnimations ??= vi.fn().mockReturnValue([])
18
+ }
19
+
20
+ const mockLocalStorage = () => {
21
+ /** @type {Record<string, string>} */
22
+ let store = {}
23
+
24
+ // @ts-ignore
25
+ ;(window).localStorage = {
26
+ getItem: function (key) {
27
+ return store[key] || null
28
+ },
29
+ setItem: function (key, value) {
30
+ store[key] = value.toString()
31
+ },
32
+ removeItem: function (key) {
33
+ delete store[key]
34
+ },
35
+ clear: function () {
36
+ store = {}
37
+ },
38
+ }
39
+ }
40
+
41
+ const mockSessionStorage = () => {
42
+ /** @type {Record<string, string>} */
43
+ let store = {}
44
+
45
+ // @ts-ignore
46
+ ;(window).sessionStorage = {
47
+ getItem: function (key) {
48
+ return store[key] || null
49
+ },
50
+ setItem: function (key, value) {
51
+ store[key] = value.toString()
52
+ },
53
+ removeItem: function (key) {
54
+ delete store[key]
55
+ },
56
+ clear: function () {
57
+ store = {}
58
+ },
59
+ }
60
+ }
61
+
62
+ export const mockIntersectionObserver = () => {
63
+ // @ts-ignore
64
+ global.IntersectionObserver = vi.fn((callback) => ({
65
+ disconnect: vi.fn(),
66
+ observe: vi.fn(() => callback([{ isIntersecting: true }])),
67
+ unobserve: vi.fn(),
68
+ }))
69
+ }
70
+
71
+ beforeAll(() => {
72
+ fakeFetch()
73
+ mockLocalStorage()
74
+ mockSessionStorage()
75
+ mockIntersectionObserver()
76
+ })
77
+
78
+ beforeEach(() => {
79
+ // @ts-ignore
80
+ window.PlayPilotLinkInjections = { token: 'some-token', require_consent: false }
81
+
82
+ // Enable all consent
83
+ // @ts-ignore
84
+ window.__tcfapi = (command, _version, callback) => {
85
+ if (command !== 'addEventListener') return
86
+
87
+ setTimeout(() => callback({
88
+ purpose: { consents: { 1: true, 2: true, 7: true, 8: true, 9: true, 10: true } },
89
+ eventStatus: 'tcloaded',
90
+ }, true), 0)
91
+
92
+ return 1
93
+ }
94
+
95
+ // Reset cookies
96
+ document.cookie.split(';').forEach((cookie) => document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, '=;'))
97
+
98
+ window.HTMLElement.prototype.scrollIntoView = vi.fn()
99
+
100
+ localStorage.clear()
101
+ sessionStorage.clear()
102
+
103
+ mockAnimations()
104
+ })
105
+
106
+ afterEach(() => {
107
+ cleanup()
108
+ })