Skip to content

Commit 73c0215

Browse files
authored
Merge branch 'main' into ja-tag-page-migration
2 parents 3cf6625 + 2260d3c commit 73c0215

File tree

9 files changed

+192
-162
lines changed

9 files changed

+192
-162
lines changed

dotcom-rendering/cdk/bin/cdk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ new RenderingCDKStack(cdkApp, 'FaciaRendering-PROD', {
6868
stage: 'PROD',
6969
domainName: 'facia-rendering.guardianapis.com',
7070
scaling: {
71-
minimumInstances: 18,
71+
minimumInstances: 30,
7272
maximumInstances: 180,
7373
policies: {
7474
step: {

dotcom-rendering/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@
4747
"@guardian/eslint-config-typescript": "9.0.1",
4848
"@guardian/identity-auth": "6.0.1",
4949
"@guardian/identity-auth-frontend": "8.1.0",
50-
"@guardian/libs": "21.4.0",
50+
"@guardian/libs": "21.6.0",
5151
"@guardian/ophan-tracker-js": "2.2.5",
5252
"@guardian/react-crossword": "2.0.2",
53-
"@guardian/react-crossword-next": "npm:@guardian/[email protected]20250226111729",
53+
"@guardian/react-crossword-next": "npm:@guardian/[email protected]20250303163323",
5454
"@guardian/shimport": "1.0.2",
5555
"@guardian/source": "8.0.0",
5656
"@guardian/source-development-kitchen": "12.0.0",

dotcom-rendering/src/components/CrosswordComponent.importable.tsx

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ import { css } from '@emotion/react';
22
import { Crossword as ReactCrossword } from '@guardian/react-crossword-next';
33
import type { CrosswordProps } from '@guardian/react-crossword-next';
44
import {
5+
between,
56
from,
67
headlineBold17,
78
space,
89
textSans14,
910
textSansItalic12,
1011
} from '@guardian/source/foundations';
1112
import { Hide } from '@guardian/source/react-components';
13+
import libDebounce from 'lodash.debounce';
1214
import type { ReactNode } from 'react';
13-
import { memo } from 'react';
15+
import { memo, useEffect, useRef, useState } from 'react';
16+
import { removeMediaRulePrefix, useMatchMedia } from '../lib/useMatchMedia';
1417
import { palette } from '../palette';
1518
import { AdSlot } from './AdSlot.web';
1619

@@ -51,13 +54,55 @@ const Layout: CrosswordProps['Layout'] = ({
5154
gridWidth,
5255
MobileBannerAd,
5356
}) => {
57+
const cluesRef = useRef<HTMLDivElement>(null);
58+
const [showGradient, setShowGradient] = useState(false);
59+
60+
const betweenTabletAndLeftCol = useMatchMedia(
61+
removeMediaRulePrefix(between.tablet.and.leftCol),
62+
);
63+
64+
const updateGradientVisibility = () => {
65+
const clueList = cluesRef.current;
66+
if (!clueList) return;
67+
const scrollPos = clueList.scrollTop;
68+
const maxScroll = clueList.scrollHeight - clueList.clientHeight;
69+
setShowGradient(scrollPos < maxScroll - 16);
70+
};
71+
72+
useEffect(() => {
73+
const clueList = cluesRef.current;
74+
if (!clueList) return;
75+
76+
updateGradientVisibility();
77+
78+
clueList.addEventListener(
79+
'scroll',
80+
libDebounce(updateGradientVisibility, 100),
81+
);
82+
window.addEventListener(
83+
'resize',
84+
libDebounce(updateGradientVisibility, 100),
85+
);
86+
87+
return () => {
88+
clueList.removeEventListener(
89+
'scroll',
90+
libDebounce(updateGradientVisibility, 100),
91+
);
92+
window.removeEventListener(
93+
'resize',
94+
libDebounce(updateGradientVisibility, 100),
95+
);
96+
};
97+
}, []);
98+
5499
return (
55100
<div
56101
css={css`
57102
display: flex;
58103
flex-direction: column;
59104
gap: ${space[4]}px;
60-
${from.phablet} {
105+
${from.tablet} {
61106
flex-direction: row;
62107
}
63108
`}
@@ -71,7 +116,7 @@ const Layout: CrosswordProps['Layout'] = ({
71116
<FocusedClue
72117
additionalCss={css`
73118
max-width: ${gridWidth}px;
74-
${from.phablet} {
119+
${from.tablet} {
75120
display: none;
76121
}
77122
`}
@@ -85,7 +130,8 @@ const Layout: CrosswordProps['Layout'] = ({
85130
>
86131
<FocusedClue
87132
additionalCss={css`
88-
${from.phablet} {
133+
max-width: ${gridWidth}px;
134+
${from.tablet} {
89135
display: none;
90136
}
91137
`}
@@ -106,22 +152,67 @@ const Layout: CrosswordProps['Layout'] = ({
106152

107153
<div
108154
css={css`
109-
${textSans14};
155+
position: relative;
110156
flex: 1;
111157
display: flex;
112-
flex-direction: column;
113-
gap: ${space[4]}px;
114-
align-items: flex-start;
115-
${from.desktop} {
116-
flex-direction: row;
158+
${from.tablet} {
159+
max-height: ${gridWidth}px;
160+
::after {
161+
display: ${showGradient ? 'block' : 'none'};
162+
position: absolute;
163+
content: '';
164+
bottom: 0;
165+
left: 0;
166+
width: 100%;
167+
height: 64px;
168+
background-image: linear-gradient(
169+
180deg,
170+
transparent,
171+
${palette('--article-background')}
172+
);
173+
}
117174
}
118-
> * {
119-
flex: 1;
175+
${from.leftCol} {
176+
max-height: none;
177+
::after {
178+
background-image: none;
179+
}
120180
}
121181
`}
122182
>
123-
<Clues direction="across" Header={CluesHeader} />
124-
<Clues direction="down" Header={CluesHeader} />
183+
<div
184+
ref={cluesRef}
185+
css={css`
186+
${textSans14};
187+
flex: 1;
188+
display: flex;
189+
flex-direction: column;
190+
gap: ${space[4]}px;
191+
${from.tablet} {
192+
overflow-y: scroll;
193+
}
194+
${from.desktop} {
195+
flex-direction: row;
196+
}
197+
${from.leftCol} {
198+
overflow: visible;
199+
}
200+
> * {
201+
flex: 1;
202+
}
203+
`}
204+
>
205+
<Clues
206+
direction="across"
207+
Header={CluesHeader}
208+
scrollToSelected={betweenTabletAndLeftCol}
209+
/>
210+
<Clues
211+
direction="down"
212+
Header={CluesHeader}
213+
scrollToSelected={betweenTabletAndLeftCol}
214+
/>
215+
</div>
125216
</div>
126217
</div>
127218
);

dotcom-rendering/src/components/SignInGate/displayRule.test.ts

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { incrementDailyArticleCount } from '../../lib/dailyArticleCount';
22
import {
3-
isIOS9,
43
isNPageOrHigherPageView,
54
isValidContentType,
65
isValidSection,
@@ -61,61 +60,6 @@ describe('SignInGate - displayRule methods', () => {
6160
});
6261
});
6362

64-
describe('isIOS9', () => {
65-
// spy on user agent to mock return value
66-
const userAgentGetter = jest.spyOn(
67-
window.navigator,
68-
'userAgent',
69-
'get',
70-
);
71-
72-
test('iphone ios9 is true', () => {
73-
userAgentGetter.mockReturnValueOnce(
74-
'Mozilla/5.0 (iPhone; CPU OS 9_0 like Mac OS X) AppleWebKit/601.1.17 (KHTML, like Gecko) Version/8.0 Mobile/13A175 Safari/600.1.4',
75-
);
76-
expect(isIOS9()).toBe(true);
77-
});
78-
79-
test('ipad ios9 is true', () => {
80-
userAgentGetter.mockReturnValueOnce(
81-
'Mozilla/5.0 (iPad; CPU OS 9_0 like Mac OS X) AppleWebKit/601.1.17 (KHTML, like Gecko) Version/8.0 Mobile/13A175 Safari/600.1.4',
82-
);
83-
expect(isIOS9()).toBe(true);
84-
});
85-
86-
test('ipod ios9 is true', () => {
87-
userAgentGetter.mockReturnValueOnce(
88-
'Mozilla/5.0 (iPod; CPU OS 9_0 like Mac OS X) AppleWebKit/601.1.17 (KHTML, like Gecko) Version/8.0 Mobile/13A175 Safari/600.1.4',
89-
);
90-
expect(isIOS9()).toBe(true);
91-
});
92-
93-
test('iphone not ios9 is false', () => {
94-
userAgentGetter.mockReturnValueOnce(
95-
'Mozilla/5.0 (iPhone; CPU OS 10_3 like Mac OS X) AppleWebKit/601.1.17 (KHTML, like Gecko) Version/8.0 Mobile/13A175 Safari/600.1.4',
96-
);
97-
expect(isIOS9()).toBe(false);
98-
});
99-
100-
test('ipad not ios9 is false', () => {
101-
userAgentGetter.mockReturnValueOnce(
102-
'Mozilla/5.0 (iPad; CPU OS 8_1 like Mac OS X) AppleWebKit/601.1.17 (KHTML, like Gecko) Version/8.0 Mobile/13A175 Safari/600.1.4',
103-
);
104-
expect(isIOS9()).toBe(false);
105-
});
106-
107-
test('ipod not ios9 is false', () => {
108-
userAgentGetter.mockReturnValueOnce(
109-
'Mozilla/5.0 (iPod; CPU OS 7_0 like Mac OS X) AppleWebKit/601.1.17 (KHTML, like Gecko) Version/8.0 Mobile/13A175 Safari/600.1.4',
110-
);
111-
expect(isIOS9()).toBe(false);
112-
});
113-
114-
test('not ios device is false', () => {
115-
expect(isIOS9()).toBe(false);
116-
});
117-
});
118-
11963
describe('isValidContentType', () => {
12064
test('is a valid type - article', () => {
12165
expect(isValidContentType('Article')).toBe(true);

dotcom-rendering/src/components/SignInGate/displayRule.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,6 @@ export const isNPageOrHigherPageView = (n = 2): boolean => {
1717
return count >= n;
1818
};
1919

20-
// determine if the useragent is running iOS 9 (known to be buggy for sign in flow)
21-
export const isIOS9 = (): boolean => {
22-
// get the browser user agent
23-
const ua = navigator.userAgent;
24-
// check useragent if the device is an iOS device
25-
const appleDevice = /(iPhone|iPod|iPad)/i.test(ua);
26-
// check useragent if the os is version 9
27-
const os = /(CPU OS 9_)/i.test(ua);
28-
29-
// if both true, then it's an apple ios 9 device
30-
return appleDevice && os;
31-
};
32-
3320
// hide the sign in gate on article types that are not supported
3421
export const isValidContentType = (contentType: string): boolean => {
3522
// It's safer to definitively *include* types as we
@@ -95,8 +82,7 @@ export const canShowSignInGate = ({
9582
// hide the sign in gate on isPaidContent
9683
!isPaidContent &&
9784
// hide the sign in gate on internal tools preview &&
98-
!isPreview &&
99-
!isIOS9(),
85+
!isPreview,
10086
);
10187

10288
export const canShowSignInGateMandatory: ({
@@ -164,7 +150,6 @@ export const canShowSignInGateWithOffers = ({
164150
!isPaidContent &&
165151
// hide the sign in gate on internal tools preview &&
166152
!isPreview &&
167-
!isIOS9() &&
168153
// hide the sign in gate for AU and US readers
169154
!['AU', ...US_REGION_CODES].includes(currentLocaleCode),
170155
);

dotcom-rendering/src/components/SignInGate/gates/main-control.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { hasUserDismissedGate } from '../dismissGate';
22
import {
3-
isIOS9,
43
isNPageOrHigherPageView,
54
isValidContentType,
65
isValidSection,
@@ -27,8 +26,7 @@ const canShow = ({
2726
// hide the sign in gate on isPaidContent
2827
!isPaidContent &&
2928
// hide the sign in gate on internal tools preview
30-
!isPreview &&
31-
!isIOS9(),
29+
!isPreview,
3230
);
3331

3432
export const signInGateComponent: SignInGateComponent = {

0 commit comments

Comments
 (0)