@automattic/jetpack-components 0.39.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/.gitattributes +8 -0
- package/CHANGELOG.md +747 -0
- package/LICENSE.txt +357 -0
- package/README.md +11 -0
- package/SECURITY.md +38 -0
- package/components/action-button/index.jsx +70 -0
- package/components/action-button/stories/index.stories.jsx +38 -0
- package/components/action-button/style.module.scss +11 -0
- package/components/action-button/test/component.jsx +16 -0
- package/components/action-popover/index.tsx +105 -0
- package/components/action-popover/stories/index.stories.jsx +115 -0
- package/components/action-popover/stories/styles.module.scss +7 -0
- package/components/action-popover/styles.module.scss +49 -0
- package/components/action-popover/types.ts +20 -0
- package/components/admin-page/index.tsx +58 -0
- package/components/admin-page/stories/index.stories.tsx +35 -0
- package/components/admin-page/stories/style.module.scss +13 -0
- package/components/admin-page/style.module.scss +11 -0
- package/components/admin-page/types.ts +41 -0
- package/components/admin-section/basic/index.tsx +15 -0
- package/components/admin-section/basic/style.module.scss +3 -0
- package/components/admin-section/hero/index.tsx +15 -0
- package/components/admin-section/hero/style.module.scss +3 -0
- package/components/admin-section/stories/index.stories.tsx +60 -0
- package/components/admin-section/types.ts +6 -0
- package/components/alert/index.tsx +61 -0
- package/components/alert/stories/index.stories.jsx +24 -0
- package/components/alert/style.module.scss +43 -0
- package/components/automattic-byline-logo/README.md +16 -0
- package/components/automattic-byline-logo/index.tsx +38 -0
- package/components/automattic-byline-logo/stories/index.stories.tsx +20 -0
- package/components/automattic-byline-logo/test/component.tsx +28 -0
- package/components/automattic-byline-logo/types.ts +18 -0
- package/components/boost-score-bar/index.tsx +103 -0
- package/components/boost-score-bar/stories/index.stories.tsx +39 -0
- package/components/boost-score-bar/style.scss +184 -0
- package/components/boost-score-bar/test/component.tsx +102 -0
- package/components/boost-score-bar/types.ts +9 -0
- package/components/button/index.tsx +84 -0
- package/components/button/stories/index.mdx +90 -0
- package/components/button/stories/index.stories.tsx +400 -0
- package/components/button/stories/style.module.scss +20 -0
- package/components/button/style.module.scss +212 -0
- package/components/button/types.ts +21 -0
- package/components/contextual-upgrade-trigger/README.md +81 -0
- package/components/contextual-upgrade-trigger/index.tsx +44 -0
- package/components/contextual-upgrade-trigger/stories/index.stories.tsx +25 -0
- package/components/contextual-upgrade-trigger/style.module.scss +75 -0
- package/components/contextual-upgrade-trigger/types.ts +9 -0
- package/components/decorative-card/index.tsx +44 -0
- package/components/decorative-card/stories/index.stories.tsx +18 -0
- package/components/decorative-card/style.scss +116 -0
- package/components/decorative-card/types.ts +16 -0
- package/components/dialog/index.tsx +69 -0
- package/components/dialog/stories/boost.png +0 -0
- package/components/dialog/stories/declaration.d.ts +1 -0
- package/components/dialog/stories/index.stories.tsx +118 -0
- package/components/dialog/stories/js-components.components.dialog.mdx +62 -0
- package/components/dialog/stories/js-components.components.dialog.stories.js +37 -0
- package/components/dialog/stories/style.module.scss +21 -0
- package/components/dialog/style.module.scss +30 -0
- package/components/donut-meter/index.tsx +147 -0
- package/components/donut-meter/stories/index.stories.tsx +51 -0
- package/components/donut-meter/style.scss +11 -0
- package/components/donut-meter/test/component.tsx +24 -0
- package/components/gridicon/index.tsx +298 -0
- package/components/gridicon/stories/index.stories.tsx +18 -0
- package/components/gridicon/style.scss +16 -0
- package/components/gridicon/types.ts +48 -0
- package/components/icon-tooltip/index.tsx +96 -0
- package/components/icon-tooltip/stories/index.stories.tsx +108 -0
- package/components/icon-tooltip/stories/style.scss +36 -0
- package/components/icon-tooltip/style.scss +121 -0
- package/components/icon-tooltip/test/component.tsx +15 -0
- package/components/icon-tooltip/types.ts +76 -0
- package/components/icons/README.md +98 -0
- package/components/icons/index.tsx +369 -0
- package/components/icons/stories/index.stories.tsx +65 -0
- package/components/icons/stories/style.module.scss +33 -0
- package/components/icons/style.module.scss +40 -0
- package/components/icons/types.ts +38 -0
- package/components/indeterminate-progress-bar/index.tsx +28 -0
- package/components/indeterminate-progress-bar/stories/index.stories.tsx +14 -0
- package/components/indeterminate-progress-bar/style.module.scss +34 -0
- package/components/indeterminate-progress-bar/types.ts +6 -0
- package/components/jetpack-footer/README.md +21 -0
- package/components/jetpack-footer/index.tsx +94 -0
- package/components/jetpack-footer/stories/index.stories.tsx +40 -0
- package/components/jetpack-footer/style.scss +116 -0
- package/components/jetpack-footer/test/component.tsx +45 -0
- package/components/jetpack-footer/types.ts +36 -0
- package/components/jetpack-logo/README.md +20 -0
- package/components/jetpack-logo/index.tsx +46 -0
- package/components/jetpack-logo/stories/index.stories.tsx +20 -0
- package/components/jetpack-logo/test/component.tsx +16 -0
- package/components/jetpack-logo/types.ts +7 -0
- package/components/jetpack-search-logo/README.md +20 -0
- package/components/jetpack-search-logo/index.tsx +93 -0
- package/components/jetpack-search-logo/stories/index.stories.tsx +21 -0
- package/components/jetpack-search-logo/test/component.tsx +16 -0
- package/components/jetpack-search-logo/types.ts +7 -0
- package/components/jetpack-vaultpress-backup-logo/README.md +19 -0
- package/components/jetpack-vaultpress-backup-logo/index.tsx +113 -0
- package/components/jetpack-vaultpress-backup-logo/stories/index.stories.tsx +20 -0
- package/components/jetpack-vaultpress-backup-logo/test/component.tsx +18 -0
- package/components/jetpack-vaultpress-backup-logo/types.ts +6 -0
- package/components/jetpack-videopress-logo/README.md +19 -0
- package/components/jetpack-videopress-logo/index.tsx +93 -0
- package/components/jetpack-videopress-logo/stories/index.stories.tsx +20 -0
- package/components/jetpack-videopress-logo/test/component.tsx +16 -0
- package/components/jetpack-videopress-logo/types.tsx +6 -0
- package/components/layout/README.md +9 -0
- package/components/layout/breakpoints.module.scss +20 -0
- package/components/layout/col/README.md +81 -0
- package/components/layout/col/index.tsx +49 -0
- package/components/layout/col/style.module.scss +28 -0
- package/components/layout/container/README.md +77 -0
- package/components/layout/container/index.tsx +42 -0
- package/components/layout/container/style.module.scss +35 -0
- package/components/layout/stories/index.stories.tsx +145 -0
- package/components/layout/stories/styles.module.scss +22 -0
- package/components/layout/types.ts +55 -0
- package/components/layout/use-breakpoint-match/README.md +43 -0
- package/components/layout/use-breakpoint-match/declaration.d.ts +9 -0
- package/components/layout/use-breakpoint-match/index.ts +79 -0
- package/components/loading-placeholder/README.md +22 -0
- package/components/loading-placeholder/index.tsx +23 -0
- package/components/loading-placeholder/stories/index.stories.tsx +78 -0
- package/components/loading-placeholder/style.module.scss +18 -0
- package/components/notice/index.tsx +98 -0
- package/components/notice/stories/index.stories.jsx +90 -0
- package/components/notice/style.module.scss +107 -0
- package/components/number-format/README.md +25 -0
- package/components/number-format/index.ts +16 -0
- package/components/pricing-card/index.tsx +127 -0
- package/components/pricing-card/stories/index.stories.tsx +55 -0
- package/components/pricing-card/style.scss +112 -0
- package/components/pricing-card/test/component.tsx +89 -0
- package/components/pricing-card/types.ts +52 -0
- package/components/pricing-slider/index.tsx +53 -0
- package/components/pricing-slider/stories/index.stories.tsx +44 -0
- package/components/pricing-slider/style.scss +55 -0
- package/components/pricing-slider/test/component.tsx +14 -0
- package/components/pricing-slider/types.ts +58 -0
- package/components/pricing-table/index.tsx +200 -0
- package/components/pricing-table/stories/index.stories.tsx +74 -0
- package/components/pricing-table/styles.module.scss +160 -0
- package/components/pricing-table/test/component.tsx +41 -0
- package/components/pricing-table/types.ts +84 -0
- package/components/product-offer/icons-card.tsx +47 -0
- package/components/product-offer/index.tsx +115 -0
- package/components/product-offer/product-offer-header.tsx +23 -0
- package/components/product-offer/stories/index.stories.tsx +104 -0
- package/components/product-offer/style.module.scss +93 -0
- package/components/product-offer/types.ts +125 -0
- package/components/product-price/index.tsx +90 -0
- package/components/product-price/price.tsx +35 -0
- package/components/product-price/stories/index.stories.tsx +33 -0
- package/components/product-price/style.module.scss +69 -0
- package/components/product-price/test/component.tsx +26 -0
- package/components/product-price/types.ts +73 -0
- package/components/qr-code/Readme.md +5 -0
- package/components/qr-code/index.tsx +78 -0
- package/components/qr-code/stories/index.stories.tsx +32 -0
- package/components/record-meter-bar/index.tsx +135 -0
- package/components/record-meter-bar/stories/index.stories.tsx +39 -0
- package/components/record-meter-bar/style.scss +38 -0
- package/components/record-meter-bar/test/component.tsx +52 -0
- package/components/spinner/README.md +28 -0
- package/components/spinner/index.jsx +45 -0
- package/components/spinner/stories/index.stories.jsx +20 -0
- package/components/spinner/style.scss +31 -0
- package/components/spinner/test/component.jsx +17 -0
- package/components/split-button/index.tsx +39 -0
- package/components/split-button/stories/index.stories.tsx +35 -0
- package/components/split-button/style.module.scss +21 -0
- package/components/split-button/types.ts +14 -0
- package/components/stat-card/index.tsx +49 -0
- package/components/stat-card/stories/index.mdx +35 -0
- package/components/stat-card/stories/index.stories.tsx +41 -0
- package/components/stat-card/style.module.scss +49 -0
- package/components/stat-card/test/component.tsx +24 -0
- package/components/stat-card/types.ts +28 -0
- package/components/status/README.md +42 -0
- package/components/status/index.tsx +40 -0
- package/components/status/stories/index.stories.tsx +44 -0
- package/components/status/style.module.scss +72 -0
- package/components/terms-of-service/index.tsx +63 -0
- package/components/terms-of-service/styles.scss +10 -0
- package/components/terms-of-service/test/index.test.jsx +52 -0
- package/components/terms-of-service/types.ts +30 -0
- package/components/testimonials/index.tsx +58 -0
- package/components/testimonials/style.scss +127 -0
- package/components/testimonials/test/component.tsx +103 -0
- package/components/testimonials/testimonial.tsx +47 -0
- package/components/testimonials/types.ts +17 -0
- package/components/text/constants.ts +30 -0
- package/components/text/index.tsx +91 -0
- package/components/text/stories/headings.index.stories.tsx +80 -0
- package/components/text/stories/index.mdx +195 -0
- package/components/text/stories/index.stories.tsx +69 -0
- package/components/text/stories/style.module.scss +64 -0
- package/components/text/stories/title.index.stories.tsx +34 -0
- package/components/text/style.module.scss +125 -0
- package/components/text/types.ts +53 -0
- package/components/theme-provider/Readme.md +49 -0
- package/components/theme-provider/globals.module.scss +6 -0
- package/components/theme-provider/index.tsx +151 -0
- package/components/theme-provider/stories/index.stories.tsx +164 -0
- package/components/theme-provider/stories/style.module.scss +79 -0
- package/components/theme-provider/types.ts +25 -0
- package/components/toggle-control/README.md +5 -0
- package/components/toggle-control/index.tsx +67 -0
- package/components/toggle-control/stories/index.stories.tsx +83 -0
- package/components/toggle-control/styles.module.scss +41 -0
- package/components/zendesk-chat/README.md +26 -0
- package/components/zendesk-chat/constants.ts +2 -0
- package/components/zendesk-chat/index.tsx +22 -0
- package/components/zendesk-chat/stories/index.stories.tsx +15 -0
- package/components/zendesk-chat/test/component.tsx +16 -0
- package/components/zendesk-chat/types.ts +3 -0
- package/index.ts +69 -0
- package/lib/locale/index.ts +48 -0
- package/lib/locale/test/index.ts +21 -0
- package/package.json +80 -0
- package/tools/get-product-checkout-url/Readme.md +32 -0
- package/tools/get-product-checkout-url/index.ts +31 -0
- package/tools/jp-redirect/README.md +61 -0
- package/tools/jp-redirect/index.ts +62 -0
- package/tools/jp-redirect/test/index.tsx +89 -0
- package/tools/jp-redirect/types.ts +49 -0
- package/tools/pricing-utils/README.md +16 -0
- package/tools/pricing-utils/index.ts +11 -0
- package/tools/pricing-utils/test/index.ts +39 -0
- package/tools/pricing-utils/types.ts +12 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import TermsOfService from '..';
|
|
3
|
+
|
|
4
|
+
describe( 'TermsofService', () => {
|
|
5
|
+
it( "references only 'the buttons above' if multipleButtons is true", () => {
|
|
6
|
+
render( <TermsOfService multipleButtons /> );
|
|
7
|
+
expect(
|
|
8
|
+
screen.getByText(
|
|
9
|
+
( content, { textContent } ) =>
|
|
10
|
+
content !== '' && // filter out parent/wrapper elements
|
|
11
|
+
textContent.startsWith(
|
|
12
|
+
`By clicking the buttons above, you agree to our Terms of Service`
|
|
13
|
+
)
|
|
14
|
+
)
|
|
15
|
+
).toBeInTheDocument();
|
|
16
|
+
} );
|
|
17
|
+
|
|
18
|
+
it( 'references the label of the agreement button if one is defined', () => {
|
|
19
|
+
const buttonLabel = 'The best button ever';
|
|
20
|
+
|
|
21
|
+
render( <TermsOfService agreeButtonLabel={ buttonLabel } /> );
|
|
22
|
+
expect(
|
|
23
|
+
screen.getByText(
|
|
24
|
+
( content, { textContent } ) =>
|
|
25
|
+
content !== '' && // filter out parent/wrapper elements
|
|
26
|
+
textContent.startsWith(
|
|
27
|
+
`By clicking the ${ buttonLabel } button, you agree to our Terms of Service`
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
).toBeInTheDocument();
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
it( 'links to the Terms of Service document (single button)', () => {
|
|
34
|
+
render( <TermsOfService agreeButtonLabel={ 'whatever' } /> );
|
|
35
|
+
expect( screen.getByText( 'Terms of Service', { selector: 'a' } ) ).toBeInTheDocument();
|
|
36
|
+
} );
|
|
37
|
+
|
|
38
|
+
it( 'links to the Terms of Service document (multiple buttons)', () => {
|
|
39
|
+
render( <TermsOfService multipleButtons /> );
|
|
40
|
+
expect( screen.getByText( 'Terms of Service', { selector: 'a' } ) ).toBeInTheDocument();
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
it( 'links to the data sharing document (single button)', () => {
|
|
44
|
+
render( <TermsOfService agreeButtonLabel={ 'whatever' } /> );
|
|
45
|
+
expect( screen.getByText( 'share details', { selector: 'a' } ) ).toBeInTheDocument();
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
it( 'links to the data sharing document (multiple buttons)', () => {
|
|
49
|
+
render( <TermsOfService multipleButtons /> );
|
|
50
|
+
expect( screen.getByText( 'share details', { selector: 'a' } ) ).toBeInTheDocument();
|
|
51
|
+
} );
|
|
52
|
+
} );
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type MultipleButtonsProps = {
|
|
2
|
+
/**
|
|
3
|
+
* Indicates whether there are multiple buttons present that would imply agreement if clicked.
|
|
4
|
+
*/
|
|
5
|
+
multipleButtons: true;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The text label of the button someone would click to agree to the terms.
|
|
9
|
+
*/
|
|
10
|
+
agreeButtonLabel?: undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type SingleButtonProps = {
|
|
14
|
+
/**
|
|
15
|
+
* Indicates whether there are multiple buttons present that would imply agreement if clicked.
|
|
16
|
+
*/
|
|
17
|
+
multipleButtons?: false;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The text label of the button someone would click to agree to the terms.
|
|
21
|
+
*/
|
|
22
|
+
agreeButtonLabel: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type TermsOfServiceProps = {
|
|
26
|
+
/**
|
|
27
|
+
* Represents additional CSS classes to be added to the component's root.
|
|
28
|
+
*/
|
|
29
|
+
className?: string;
|
|
30
|
+
} & ( MultipleButtonsProps | SingleButtonProps );
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useState, useCallback } from '@wordpress/element';
|
|
2
|
+
import Gridicon from '../gridicon';
|
|
3
|
+
import { Testimonial } from './testimonial';
|
|
4
|
+
import { TestimonialsType } from './types';
|
|
5
|
+
|
|
6
|
+
const Testimonials: TestimonialsType = ( { testimonials } ) => {
|
|
7
|
+
const [ currentTestimonialIndex, setcurrentTestimonialIndex ] = useState( 0 );
|
|
8
|
+
|
|
9
|
+
const incrementTestimonial = useCallback( () => {
|
|
10
|
+
const newIndex =
|
|
11
|
+
currentTestimonialIndex === testimonials.length - 1 ? 0 : currentTestimonialIndex + 1;
|
|
12
|
+
|
|
13
|
+
setcurrentTestimonialIndex( newIndex );
|
|
14
|
+
}, [ currentTestimonialIndex, testimonials ] );
|
|
15
|
+
|
|
16
|
+
const decrementTestimonial = useCallback( () => {
|
|
17
|
+
const newIndex =
|
|
18
|
+
currentTestimonialIndex === 0 ? testimonials.length - 1 : currentTestimonialIndex - 1;
|
|
19
|
+
|
|
20
|
+
setcurrentTestimonialIndex( newIndex );
|
|
21
|
+
}, [ currentTestimonialIndex, testimonials ] );
|
|
22
|
+
|
|
23
|
+
const shouldShowArrows = testimonials.length > 1;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="testimonials">
|
|
27
|
+
{ shouldShowArrows && (
|
|
28
|
+
<button
|
|
29
|
+
data-testid="left-arrow"
|
|
30
|
+
className="testimonials__left-arrow"
|
|
31
|
+
onClick={ decrementTestimonial }
|
|
32
|
+
>
|
|
33
|
+
<Gridicon icon="chevron-left" size={ 48 } />
|
|
34
|
+
</button>
|
|
35
|
+
) }
|
|
36
|
+
|
|
37
|
+
{ testimonials.map( ( testimonial, index ) => (
|
|
38
|
+
<Testimonial
|
|
39
|
+
key={ index }
|
|
40
|
+
{ ...testimonial }
|
|
41
|
+
hidden={ currentTestimonialIndex !== index }
|
|
42
|
+
/>
|
|
43
|
+
) ) }
|
|
44
|
+
|
|
45
|
+
{ shouldShowArrows && (
|
|
46
|
+
<button
|
|
47
|
+
data-testid="right-arrow"
|
|
48
|
+
className="testimonials__right-arrow"
|
|
49
|
+
onClick={ incrementTestimonial }
|
|
50
|
+
>
|
|
51
|
+
<Gridicon icon="chevron-right" size={ 48 } />
|
|
52
|
+
</button>
|
|
53
|
+
) }
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default Testimonials;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
.testimonials {
|
|
2
|
+
position: relative;
|
|
3
|
+
|
|
4
|
+
margin-top: 1.5rem;
|
|
5
|
+
padding: calc(var(--spacing-base) * 3);
|
|
6
|
+
padding-bottom: 0;
|
|
7
|
+
|
|
8
|
+
color: var(--jp-black);
|
|
9
|
+
background-color: var(--jp-white);
|
|
10
|
+
border-radius: var(--jp-border-radius);
|
|
11
|
+
box-shadow: 0 0 40px rgba(0, 0, 0, 0.08);
|
|
12
|
+
|
|
13
|
+
&__left-arrow,
|
|
14
|
+
&__right-arrow {
|
|
15
|
+
position: absolute;
|
|
16
|
+
top: 50%;
|
|
17
|
+
transform: translateY(-50%);
|
|
18
|
+
|
|
19
|
+
background: none;
|
|
20
|
+
border: none;
|
|
21
|
+
|
|
22
|
+
svg {
|
|
23
|
+
color: #1e1e1e;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:hover {
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
|
|
29
|
+
svg {
|
|
30
|
+
color: var(--jp-gray-20);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&__left-arrow {
|
|
36
|
+
left: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&__right-arrow {
|
|
40
|
+
right: 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.testimonial {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column-reverse;
|
|
47
|
+
align-items: center;
|
|
48
|
+
|
|
49
|
+
&__author-img {
|
|
50
|
+
min-width: 325px;
|
|
51
|
+
width: 365px;
|
|
52
|
+
max-width: 100%;
|
|
53
|
+
margin-top: 3rem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&__content {
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: 1rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&__quote-icon {
|
|
62
|
+
min-width: 32px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&__quote,
|
|
66
|
+
&__author {
|
|
67
|
+
font-size: var(--font-title-medium);
|
|
68
|
+
font-weight: 500;
|
|
69
|
+
line-height: 1.3;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
&__quote {
|
|
73
|
+
margin: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
&__author {
|
|
77
|
+
display: block;
|
|
78
|
+
margin-top: 2rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&__profession {
|
|
82
|
+
display: block;
|
|
83
|
+
margin-top: 0.5rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@media (min-width: 600px) {
|
|
87
|
+
padding: calc(var(--spacing-base) * 8);
|
|
88
|
+
padding-bottom: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@media (min-width: 1080px) {
|
|
92
|
+
flex-direction: row;
|
|
93
|
+
align-items: flex-end;
|
|
94
|
+
gap: 3rem;
|
|
95
|
+
|
|
96
|
+
padding-top: 0;
|
|
97
|
+
|
|
98
|
+
&__content {
|
|
99
|
+
align-self: center;
|
|
100
|
+
|
|
101
|
+
min-width: 350px;
|
|
102
|
+
max-width: 550px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&__text-block {
|
|
106
|
+
padding-bottom: calc(var(--spacing-base) * 3);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.show {
|
|
112
|
+
animation: showTestimonial 500ms ease-in-out forwards;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.hidden {
|
|
116
|
+
display: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@keyframes showTestimonial {
|
|
120
|
+
0% {
|
|
121
|
+
opacity: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
100% {
|
|
125
|
+
opacity: 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import Testimonials from '../index';
|
|
4
|
+
import { Testimonial } from '../testimonial';
|
|
5
|
+
|
|
6
|
+
const testimonials = [
|
|
7
|
+
{
|
|
8
|
+
quote: 'This is a quote',
|
|
9
|
+
author: 'John Doe',
|
|
10
|
+
profession: 'CEO',
|
|
11
|
+
img: 'https://via.placeholder.com/150',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
quote: 'This is another quote',
|
|
15
|
+
author: 'Jane Doe',
|
|
16
|
+
profession: 'CTO',
|
|
17
|
+
img: 'https://via.placeholder.com/149',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
quote: 'This is yet another quote',
|
|
21
|
+
author: 'Mary Doe',
|
|
22
|
+
profession: 'CFO',
|
|
23
|
+
img: 'https://via.placeholder.com/148',
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
describe( 'Testimonial', () => {
|
|
28
|
+
it( 'should render the testimonial', () => {
|
|
29
|
+
const { quote, author, profession } = testimonials[ 0 ];
|
|
30
|
+
|
|
31
|
+
render( <Testimonial { ...testimonials[ 0 ] } hidden={ false } /> );
|
|
32
|
+
|
|
33
|
+
expect( screen.getByText( `“${ quote }”` ) ).toBeInTheDocument();
|
|
34
|
+
expect( screen.getByText( author ) ).toBeInTheDocument();
|
|
35
|
+
expect( screen.getByText( profession ) ).toBeInTheDocument();
|
|
36
|
+
expect( screen.getByAltText( author ) ).toBeInTheDocument();
|
|
37
|
+
} );
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
describe( 'Testimonials', () => {
|
|
41
|
+
it( 'should render the first testimonial on load', () => {
|
|
42
|
+
const { quote, author, profession } = testimonials[ 0 ];
|
|
43
|
+
render( <Testimonials testimonials={ testimonials } /> );
|
|
44
|
+
|
|
45
|
+
expect( screen.getByText( `“${ quote }”` ) ).toBeInTheDocument();
|
|
46
|
+
expect( screen.getByText( author ) ).toBeInTheDocument();
|
|
47
|
+
expect( screen.getByText( profession ) ).toBeInTheDocument();
|
|
48
|
+
expect( screen.getByAltText( author ) ).toBeInTheDocument();
|
|
49
|
+
} );
|
|
50
|
+
|
|
51
|
+
it( 'should render the next testimonial when the right arrow is clicked', () => {
|
|
52
|
+
const { author: secondAuthor } = testimonials[ 1 ];
|
|
53
|
+
|
|
54
|
+
render( <Testimonials testimonials={ testimonials } /> );
|
|
55
|
+
|
|
56
|
+
const rightArrow = screen.getByTestId( 'right-arrow' );
|
|
57
|
+
userEvent.click( rightArrow );
|
|
58
|
+
|
|
59
|
+
expect( screen.getByText( secondAuthor ) ).toBeInTheDocument();
|
|
60
|
+
|
|
61
|
+
userEvent.click( rightArrow );
|
|
62
|
+
|
|
63
|
+
const { author: thirdAuthor } = testimonials[ 2 ];
|
|
64
|
+
|
|
65
|
+
expect( screen.getByText( thirdAuthor ) ).toBeInTheDocument();
|
|
66
|
+
|
|
67
|
+
userEvent.click( rightArrow );
|
|
68
|
+
|
|
69
|
+
const { author } = testimonials[ 0 ];
|
|
70
|
+
|
|
71
|
+
expect( screen.getByText( author ) ).toBeInTheDocument();
|
|
72
|
+
} );
|
|
73
|
+
|
|
74
|
+
it( 'should render the previous testimonial when the left arrow is clicked', () => {
|
|
75
|
+
const { author: thirdAuthor } = testimonials[ 2 ];
|
|
76
|
+
|
|
77
|
+
render( <Testimonials testimonials={ testimonials } /> );
|
|
78
|
+
|
|
79
|
+
const leftArrow = screen.getByTestId( 'left-arrow' );
|
|
80
|
+
userEvent.click( leftArrow );
|
|
81
|
+
|
|
82
|
+
expect( screen.getByText( thirdAuthor ) ).toBeInTheDocument();
|
|
83
|
+
|
|
84
|
+
userEvent.click( leftArrow );
|
|
85
|
+
|
|
86
|
+
const { author: secondAuthor } = testimonials[ 1 ];
|
|
87
|
+
|
|
88
|
+
expect( screen.getByText( secondAuthor ) ).toBeInTheDocument();
|
|
89
|
+
|
|
90
|
+
userEvent.click( leftArrow );
|
|
91
|
+
|
|
92
|
+
const { author } = testimonials[ 0 ];
|
|
93
|
+
|
|
94
|
+
expect( screen.getByText( author ) ).toBeInTheDocument();
|
|
95
|
+
} );
|
|
96
|
+
|
|
97
|
+
it( 'should not render the arrows if there is only one testimonial', () => {
|
|
98
|
+
render( <Testimonials testimonials={ [ testimonials[ 0 ] ] } /> );
|
|
99
|
+
|
|
100
|
+
expect( screen.queryByTestId( 'left-arrow' ) ).not.toBeInTheDocument();
|
|
101
|
+
expect( screen.queryByTestId( 'right-arrow' ) ).not.toBeInTheDocument();
|
|
102
|
+
} );
|
|
103
|
+
} );
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import type { TestimonialType } from './types';
|
|
3
|
+
|
|
4
|
+
import './style.scss';
|
|
5
|
+
|
|
6
|
+
const Testimonial: TestimonialType = ( { quote, author, profession, img, hidden } ) => {
|
|
7
|
+
return (
|
|
8
|
+
<div className={ classNames( 'testimonial', hidden ? 'hidden' : 'show' ) } key={ author }>
|
|
9
|
+
<img className="testimonial__author-img" src={ img } alt={ author } />
|
|
10
|
+
|
|
11
|
+
<div className="testimonial__content">
|
|
12
|
+
<svg
|
|
13
|
+
width="32"
|
|
14
|
+
height="32"
|
|
15
|
+
viewBox="0 0 32 32"
|
|
16
|
+
fill="none"
|
|
17
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
18
|
+
className="testimonial__quote-icon"
|
|
19
|
+
>
|
|
20
|
+
<path d="M14.6667 24L6.66675 24L6.66675 16L14.6667 16L14.6667 24Z" fill="#1E1E1E" />
|
|
21
|
+
<path d="M26.6667 24L18.6667 24L18.6667 16L26.6667 16L26.6667 24Z" fill="#1E1E1E" />
|
|
22
|
+
<path
|
|
23
|
+
d="M6.66675 24.0001C6.66675 19.5573 6.66675 14.9337 6.66675 10.6686C6.66675 6.40254 10.0001 6.67016 12.0001 6.66987"
|
|
24
|
+
stroke="#1E1E1E"
|
|
25
|
+
strokeWidth="1.5"
|
|
26
|
+
/>
|
|
27
|
+
<path
|
|
28
|
+
d="M18.6668 24.0001C18.6667 19.5573 18.6667 14.9337 18.6667 10.6686C18.6667 6.40254 22.0001 6.67016 24.0001 6.66987"
|
|
29
|
+
stroke="#1E1E1E"
|
|
30
|
+
strokeWidth="1.5"
|
|
31
|
+
/>
|
|
32
|
+
</svg>
|
|
33
|
+
|
|
34
|
+
<div className="testimonial__text-block">
|
|
35
|
+
<blockquote className="testimonial__quote">“{ quote }”</blockquote>
|
|
36
|
+
|
|
37
|
+
<figcaption>
|
|
38
|
+
<span className="testimonial__author">{ author }</span>
|
|
39
|
+
<span className="testimonial__profession">{ profession }</span>
|
|
40
|
+
</figcaption>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export { Testimonial };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FC, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface TestimonialProps {
|
|
4
|
+
img: string;
|
|
5
|
+
quote: string;
|
|
6
|
+
author: string;
|
|
7
|
+
profession: string | ReactNode;
|
|
8
|
+
hidden?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type TestimonialType = FC< TestimonialProps >;
|
|
12
|
+
|
|
13
|
+
export interface TestimonialsProps {
|
|
14
|
+
testimonials: TestimonialProps[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type TestimonialsType = FC< TestimonialsProps >;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const VARIANTS_MAPPING = {
|
|
2
|
+
'headline-medium': 'h1',
|
|
3
|
+
'headline-small': 'h2',
|
|
4
|
+
'headline-small-regular': 'h2',
|
|
5
|
+
'title-medium': 'h3',
|
|
6
|
+
'title-medium-semi-bold': 'h3',
|
|
7
|
+
'title-small': 'h4',
|
|
8
|
+
body: 'p',
|
|
9
|
+
'body-small': 'p',
|
|
10
|
+
'body-extra-small': 'p',
|
|
11
|
+
'body-extra-small-bold': 'p',
|
|
12
|
+
label: 'p',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export const BOX_MODEL_VALUES = [
|
|
16
|
+
'mt',
|
|
17
|
+
'mr',
|
|
18
|
+
'mb',
|
|
19
|
+
'ml',
|
|
20
|
+
'mx',
|
|
21
|
+
'my',
|
|
22
|
+
'm',
|
|
23
|
+
'pt',
|
|
24
|
+
'pr',
|
|
25
|
+
'pb',
|
|
26
|
+
'pl',
|
|
27
|
+
'px',
|
|
28
|
+
'py',
|
|
29
|
+
'p',
|
|
30
|
+
] as const;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import React, { useMemo, forwardRef } from 'react';
|
|
3
|
+
import { BOX_MODEL_VALUES, VARIANTS_MAPPING } from './constants';
|
|
4
|
+
import styles from './style.module.scss';
|
|
5
|
+
import type { H3Props, TextProps, TitleProps } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Text component.
|
|
9
|
+
*
|
|
10
|
+
* @param {TextProps} props - Component props.
|
|
11
|
+
* @returns {React.ReactElement} - JSX.Element
|
|
12
|
+
*/
|
|
13
|
+
const Text = forwardRef< HTMLElement, TextProps >(
|
|
14
|
+
( { variant = 'body', children, component, className, ...componentProps }, ref ) => {
|
|
15
|
+
const Component = component || VARIANTS_MAPPING[ variant ] || 'span';
|
|
16
|
+
|
|
17
|
+
// Build Styles module CSS classnames.
|
|
18
|
+
const boxModelClasses = useMemo( () => {
|
|
19
|
+
return BOX_MODEL_VALUES.reduce( ( acc, value ) => {
|
|
20
|
+
if ( typeof componentProps[ value ] !== 'undefined' ) {
|
|
21
|
+
acc += styles[ `${ value }-${ componentProps[ value ] }` ] + ' ';
|
|
22
|
+
// pick spacing prop. Do not pass down to Component.
|
|
23
|
+
delete componentProps[ value ];
|
|
24
|
+
}
|
|
25
|
+
return acc;
|
|
26
|
+
}, '' );
|
|
27
|
+
}, [ componentProps ] );
|
|
28
|
+
|
|
29
|
+
componentProps.ref = ref;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Component
|
|
33
|
+
className={ classNames( styles.reset, styles[ variant ], className, boxModelClasses ) }
|
|
34
|
+
{ ...componentProps }
|
|
35
|
+
>
|
|
36
|
+
{ children }
|
|
37
|
+
</Component>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
Text.displayName = 'Text';
|
|
42
|
+
|
|
43
|
+
export default Text;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Heading component - Medium size.
|
|
47
|
+
*
|
|
48
|
+
* @param {TextProps} props - Component props.
|
|
49
|
+
* @returns {React.ReactElement} - JSX.Element
|
|
50
|
+
*/
|
|
51
|
+
export const H2: React.FC< TextProps > = ( { children, ...componentProps } ) => (
|
|
52
|
+
<Text variant="headline-medium" mb={ 3 } { ...componentProps }>
|
|
53
|
+
{ children }
|
|
54
|
+
</Text>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Heading component - Small size,
|
|
59
|
+
*
|
|
60
|
+
* @param {H3Props} props - Component props.
|
|
61
|
+
* @returns {React.ReactElement} - JSX.Element
|
|
62
|
+
*/
|
|
63
|
+
export const H3: React.FC< H3Props > = ( { children, weight = 'bold', ...componentProps } ) => {
|
|
64
|
+
const variant = `headline-small${
|
|
65
|
+
weight === 'bold' ? '' : `-${ weight }`
|
|
66
|
+
}` as TextProps[ 'variant' ];
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Text variant={ variant } mb={ 3 } { ...componentProps }>
|
|
70
|
+
{ children }
|
|
71
|
+
</Text>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Title component, based on Text component.
|
|
77
|
+
*
|
|
78
|
+
* @param {TitleProps} props - Component props.
|
|
79
|
+
* @returns {React.ReactElement} - JSX.Element
|
|
80
|
+
*/
|
|
81
|
+
export const Title: React.FC< TitleProps > = ( {
|
|
82
|
+
children,
|
|
83
|
+
size = 'medium',
|
|
84
|
+
...componentProps
|
|
85
|
+
} ) => (
|
|
86
|
+
<Text variant={ `title-${ size }` } mb={ 1 } { ...componentProps }>
|
|
87
|
+
{ children }
|
|
88
|
+
</Text>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
export * from './constants';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { H2, H3 } from '../index';
|
|
3
|
+
import styles from './style.module.scss';
|
|
4
|
+
import type { H3Props } from '../types';
|
|
5
|
+
|
|
6
|
+
const TIPOGRAPHY_WEIGHTS: Array< H3Props[ 'weight' ] > = [ 'regular', 'bold' ];
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'JS Packages/Components/Text/Heading',
|
|
10
|
+
component: H2,
|
|
11
|
+
argTypes: {
|
|
12
|
+
children: {
|
|
13
|
+
control: { type: 'text' },
|
|
14
|
+
},
|
|
15
|
+
weight: {
|
|
16
|
+
control: { type: 'select' },
|
|
17
|
+
options: TIPOGRAPHY_WEIGHTS,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Helper component to create a the story.
|
|
24
|
+
*
|
|
25
|
+
* @param {object} props - Component props.
|
|
26
|
+
* @param {React.Component} props.children - Icon component children.
|
|
27
|
+
* @returns {React.Component} Text component instance.
|
|
28
|
+
*/
|
|
29
|
+
function Instance( { children } ) {
|
|
30
|
+
return (
|
|
31
|
+
<div className={ styles.instance }>
|
|
32
|
+
<span>Text above to the the component...</span>
|
|
33
|
+
{ children }
|
|
34
|
+
<span>Text below to the the component...</span>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const TemplateH2 = args => (
|
|
40
|
+
<Instance>
|
|
41
|
+
<H2 { ...args }>
|
|
42
|
+
{ args?.children ||
|
|
43
|
+
'Headline Medium - Manage your Jetpack plan and products all in one place' }
|
|
44
|
+
</H2>
|
|
45
|
+
</Instance>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const TemplateH3 = args => (
|
|
49
|
+
<Instance>
|
|
50
|
+
<H3 { ...args }>
|
|
51
|
+
{ args?.children ||
|
|
52
|
+
'Headline Small - Manage your Jetpack plan and products all in one place' }
|
|
53
|
+
</H3>
|
|
54
|
+
</Instance>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const Template = args => {
|
|
58
|
+
return (
|
|
59
|
+
<>
|
|
60
|
+
<TemplateH2 { ...args } />
|
|
61
|
+
<TemplateH3 { ...args } />
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const DefaultArgs = {};
|
|
67
|
+
export const Default = Template.bind( {} );
|
|
68
|
+
Default.args = DefaultArgs;
|
|
69
|
+
|
|
70
|
+
export const HeadlineH2 = TemplateH2.bind( {} );
|
|
71
|
+
HeadlineH2.storyName = 'H2';
|
|
72
|
+
HeadlineH2.args = {
|
|
73
|
+
weight: 'bold',
|
|
74
|
+
} as H3Props;
|
|
75
|
+
|
|
76
|
+
export const HeadlineH3 = TemplateH3.bind( {} );
|
|
77
|
+
HeadlineH3.storyName = 'H3';
|
|
78
|
+
HeadlineH3.args = {
|
|
79
|
+
weight: 'bold',
|
|
80
|
+
} as H3Props;
|