@times-components/ts-components 1.113.1 → 1.115.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 +22 -0
- package/dist/components/affiliate-link-disclaimer/AffiliateLinkDisclaimer.d.ts +12 -0
- package/dist/components/affiliate-link-disclaimer/AffiliateLinkDisclaimer.js +24 -0
- package/dist/components/affiliate-link-disclaimer/__tests__/AffiliateLinkDisclaimer.test.d.ts +1 -0
- package/dist/components/affiliate-link-disclaimer/__tests__/AffiliateLinkDisclaimer.test.js +47 -0
- package/dist/components/affiliate-link-disclaimer/styles.d.ts +3 -0
- package/dist/components/affiliate-link-disclaimer/styles.js +75 -0
- package/dist/components/cta-button/CtaButton.d.ts +11 -0
- package/dist/components/cta-button/CtaButton.js +7 -0
- package/dist/components/cta-button/__tests__/CtaButton.test.d.ts +1 -0
- package/dist/components/cta-button/__tests__/CtaButton.test.js +16 -0
- package/dist/components/cta-button/styles.d.ts +1 -0
- package/dist/components/cta-button/styles.js +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/package.json +3 -3
- package/rnw.js +1 -1
- package/src/components/affiliate-link-disclaimer/AffiliateLinkDisclaimer.tsx +52 -0
- package/src/components/affiliate-link-disclaimer/__tests__/AffiliateLinkDisclaimer.test.tsx +70 -0
- package/src/components/affiliate-link-disclaimer/styles.ts +77 -0
- package/src/components/cta-button/CtaButton.tsx +26 -0
- package/src/components/cta-button/__tests__/CtaButton.test.tsx +17 -0
- package/src/components/cta-button/styles.ts +17 -0
- package/src/index.ts +6 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React, { useRef, useState } from 'react';
|
|
2
|
+
import { Container, Disclaimer, TextContainer } from './styles';
|
|
3
|
+
|
|
4
|
+
interface AttributesProps {
|
|
5
|
+
disclaimer_text: string;
|
|
6
|
+
toggle_active_text: string;
|
|
7
|
+
toggle_inactive_text: string;
|
|
8
|
+
disclaimer_full_text: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface RootProps {
|
|
12
|
+
attributes: AttributesProps;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const AffiliateLinkDisclaimer: React.FC<RootProps> = props => {
|
|
16
|
+
const attributes = props.attributes;
|
|
17
|
+
|
|
18
|
+
const [open, setOpen] = useState<boolean>(false);
|
|
19
|
+
const ref = useRef<HTMLParagraphElement>(null);
|
|
20
|
+
|
|
21
|
+
const getHeight = (): string => {
|
|
22
|
+
return ref.current
|
|
23
|
+
? `${ref.current.getBoundingClientRect().height}px`
|
|
24
|
+
: '0px';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Container>
|
|
29
|
+
<Disclaimer>
|
|
30
|
+
<p className="shortcode-disclaimer_text">
|
|
31
|
+
{decodeURIComponent(attributes.disclaimer_text)}
|
|
32
|
+
</p>
|
|
33
|
+
<a
|
|
34
|
+
className="shortcode-disclaimer__toggle"
|
|
35
|
+
href="#"
|
|
36
|
+
onClick={e => {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
setOpen(!open);
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
{open
|
|
42
|
+
? decodeURIComponent(attributes.toggle_active_text)
|
|
43
|
+
: decodeURIComponent(attributes.toggle_inactive_text)}
|
|
44
|
+
</a>
|
|
45
|
+
</Disclaimer>
|
|
46
|
+
|
|
47
|
+
<TextContainer style={{ height: open ? getHeight() : '0px' }}>
|
|
48
|
+
<p ref={ref}>{decodeURIComponent(attributes.disclaimer_full_text)}</p>
|
|
49
|
+
</TextContainer>
|
|
50
|
+
</Container>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import { AffiliateLinkDisclaimer } from '../AffiliateLinkDisclaimer';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
5
|
+
|
|
6
|
+
describe('AffiliateLinkDisclaimer Component', () => {
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
attributes: {
|
|
9
|
+
disclaimer_text: 'This%20article%20contains%20affiliate%20links.',
|
|
10
|
+
toggle_active_text: 'Show%20less',
|
|
11
|
+
toggle_inactive_text: 'Show%20more',
|
|
12
|
+
disclaimer_full_text:
|
|
13
|
+
'Our%20travel%20journalism%20is%20written%20and%20edited%20by%20independent%20experts%20to%20inform%20and%20advise.'
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
it('renders the disclaimer text and toggle link initially', () => {
|
|
18
|
+
render(<AffiliateLinkDisclaimer attributes={defaultProps.attributes} />);
|
|
19
|
+
|
|
20
|
+
// Check the disclaimer text
|
|
21
|
+
const disclaimerText = screen.getByText(
|
|
22
|
+
'This article contains affiliate links.'
|
|
23
|
+
);
|
|
24
|
+
expect(disclaimerText).toBeInTheDocument();
|
|
25
|
+
|
|
26
|
+
// Check the initial toggle text
|
|
27
|
+
const toggleLink = screen.getByText('Show more');
|
|
28
|
+
expect(toggleLink).toBeInTheDocument();
|
|
29
|
+
expect(toggleLink).toHaveAttribute('href', '#');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('expands and collapses full text when the toggle link is clicked', () => {
|
|
33
|
+
render(<AffiliateLinkDisclaimer attributes={defaultProps.attributes} />);
|
|
34
|
+
|
|
35
|
+
const toggleLink = screen.getByText('Show more');
|
|
36
|
+
const fullText = screen.getByText(
|
|
37
|
+
'Our travel journalism is written and edited by independent experts to inform and advise.'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Click to expand
|
|
41
|
+
fireEvent.click(toggleLink);
|
|
42
|
+
|
|
43
|
+
// Expect the toggle text to change to 'Show less'
|
|
44
|
+
expect(screen.getByText('Show less')).toBeInTheDocument();
|
|
45
|
+
|
|
46
|
+
// Click to collapse
|
|
47
|
+
fireEvent.click(screen.getByText('Show less'));
|
|
48
|
+
|
|
49
|
+
// Expect toggle text to change back to 'Show more'
|
|
50
|
+
expect(screen.getByText('Show more')).toBeInTheDocument();
|
|
51
|
+
|
|
52
|
+
// Expect the full text container to collapse
|
|
53
|
+
expect(fullText.parentElement).toHaveStyle('height: 0px');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('decodes URI-encoded text correctly', () => {
|
|
57
|
+
render(<AffiliateLinkDisclaimer attributes={defaultProps.attributes} />);
|
|
58
|
+
|
|
59
|
+
// Check all decoded content
|
|
60
|
+
expect(
|
|
61
|
+
screen.getByText('This article contains affiliate links.')
|
|
62
|
+
).toBeInTheDocument();
|
|
63
|
+
expect(screen.getByText('Show more')).toBeInTheDocument();
|
|
64
|
+
expect(
|
|
65
|
+
screen.getByText(
|
|
66
|
+
'Our travel journalism is written and edited by independent experts to inform and advise.'
|
|
67
|
+
)
|
|
68
|
+
).toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Container = styled.div`
|
|
4
|
+
height: auto;
|
|
5
|
+
border-top: 1px solid #ccc;
|
|
6
|
+
border-bottom: 1px solid #ccc;
|
|
7
|
+
margin: 30px 0 60px 0;
|
|
8
|
+
padding: 1px 1px 10px;
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
export const Disclaimer = styled.div`
|
|
12
|
+
text-align: center;
|
|
13
|
+
padding: 14px 0;
|
|
14
|
+
margin: 5px 0;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
align-items: center;
|
|
18
|
+
|
|
19
|
+
.shortcode-disclaimer_text {
|
|
20
|
+
display: inline;
|
|
21
|
+
clear: both;
|
|
22
|
+
font-size: 1.8rem;
|
|
23
|
+
line-height: 1.5;
|
|
24
|
+
color: #1d1d1b;
|
|
25
|
+
padding: 0;
|
|
26
|
+
font-weight: 400;
|
|
27
|
+
margin: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.shortcode-disclaimer__toggle {
|
|
31
|
+
font-weight: 400;
|
|
32
|
+
word-wrap: break-word;
|
|
33
|
+
margin-left: 8px;
|
|
34
|
+
font-family: Roboto, sans-serif;
|
|
35
|
+
font-size: 12px;
|
|
36
|
+
line-height: 1.5;
|
|
37
|
+
font-weight: 400;
|
|
38
|
+
color: rgb(105, 105, 105) !important;
|
|
39
|
+
font-style: normal;
|
|
40
|
+
text-decoration: none !important;
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
margin-top: 5px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.shortcode-disclaimer__toggle:after {
|
|
46
|
+
transition: transform 0.2s linear;
|
|
47
|
+
content: '';
|
|
48
|
+
display: inline-block;
|
|
49
|
+
background-image: url('https://www.thetimes.co.uk/travel/wp-content/themes/tnl-travel-refactor/dist/assets/css/svg/arrow-affiliate.svg');
|
|
50
|
+
background-repeat: no-repeat;
|
|
51
|
+
background-position: 50%;
|
|
52
|
+
background-size: cover;
|
|
53
|
+
height: 16px;
|
|
54
|
+
width: 16px;
|
|
55
|
+
margin-left: 2px;
|
|
56
|
+
margin-top: 2px;
|
|
57
|
+
-webkit-transform: rotate(180deg);
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
export const TextContainer = styled.div`
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
transition: height 0.3s ease-in-out;
|
|
64
|
+
|
|
65
|
+
p {
|
|
66
|
+
text-align: center;
|
|
67
|
+
margin: 0 !important;
|
|
68
|
+
clear: both;
|
|
69
|
+
font-size: 1.8rem;
|
|
70
|
+
line-height: 1.6;
|
|
71
|
+
color: rgb(105, 105, 105) !important;
|
|
72
|
+
padding: 0 5px 30px;
|
|
73
|
+
font-weight: 400;
|
|
74
|
+
font-family: TimesDigitalW04-Regular, TimesDigitalW04-Regular-fallback,
|
|
75
|
+
serif;
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Link } from './styles';
|
|
3
|
+
|
|
4
|
+
interface AttributesProps {
|
|
5
|
+
url: string;
|
|
6
|
+
target?: string;
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface RootProps {
|
|
11
|
+
attributes?: AttributesProps;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const CtaButton: React.FC<RootProps> = props => {
|
|
15
|
+
const attributes = props.attributes;
|
|
16
|
+
|
|
17
|
+
return attributes ? (
|
|
18
|
+
<Link
|
|
19
|
+
href={attributes.url}
|
|
20
|
+
target={attributes.target || '_blank'}
|
|
21
|
+
rel="nofollow"
|
|
22
|
+
>
|
|
23
|
+
{attributes.text}
|
|
24
|
+
</Link>
|
|
25
|
+
) : null;
|
|
26
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { CtaButton } from '../CtaButton';
|
|
4
|
+
|
|
5
|
+
describe('CtaButton Component', () => {
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
url: 'https://www.example.com',
|
|
8
|
+
target: '_blank',
|
|
9
|
+
text: 'Book a Stay'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
it('does not render a button if attributes are not provided', () => {
|
|
13
|
+
const { container } = render(<CtaButton attributes={defaultProps} />);
|
|
14
|
+
const button = container.querySelector('button');
|
|
15
|
+
expect(button).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Link = styled.a`
|
|
4
|
+
width: fit-content;
|
|
5
|
+
height: 48px;
|
|
6
|
+
font-family: Roboto, sans-serif;
|
|
7
|
+
font-size: 16px;
|
|
8
|
+
font-weight: 500;
|
|
9
|
+
border: none;
|
|
10
|
+
border-radius: 0;
|
|
11
|
+
padding: 12px 16px;
|
|
12
|
+
background-color: #005c8a;
|
|
13
|
+
color: #ffffff;
|
|
14
|
+
display: flex;
|
|
15
|
+
text-decoration: none;
|
|
16
|
+
align-items: center;
|
|
17
|
+
`;
|
package/src/index.ts
CHANGED
|
@@ -123,6 +123,8 @@ export {
|
|
|
123
123
|
FeaturesCarousel
|
|
124
124
|
} from './components/features-carousel/FeaturesCarousel';
|
|
125
125
|
|
|
126
|
+
// Button Components
|
|
127
|
+
export { CtaButton } from './components/cta-button/CtaButton';
|
|
126
128
|
export { SocialMediaEmbed } from './components/social-embed/SocialMediaEmbed';
|
|
127
129
|
|
|
128
130
|
// Contexts
|
|
@@ -130,3 +132,7 @@ export {
|
|
|
130
132
|
useSocialEmbedsContext,
|
|
131
133
|
SocialEmbedsProvider
|
|
132
134
|
} from './contexts/SocialEmbedsProvider';
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
AffiliateLinkDisclaimer
|
|
138
|
+
} from './components/affiliate-link-disclaimer/AffiliateLinkDisclaimer';
|