@skyscanner/backpack-web 9.1.0 → 9.2.0-beta.3

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.
@@ -8,25 +8,25 @@ npm install bpk-component-graphic-promotion --save-dev
8
8
 
9
9
  ## Usage
10
10
 
11
- ```js
11
+ ```tsx
12
12
  import React from 'react';
13
13
  import BpkGraphicPromo from 'bpk-component-graphic-promotion';
14
14
 
15
15
  export default () => (
16
16
  <BpkGraphicPromo
17
+ buttonText="Learn more"
17
18
  contentId="graphic-promo-1"
18
- tagline="Tagline"
19
19
  headline="Ride your wave"
20
- subheading="Portugal and 6 more countries have just been added to the UK travel green list"
20
+ invertVertically
21
+ onClick={() => redirect("https://www.sponsor-name.com/promotion")}
21
22
  sponsor={{
22
23
  label: 'Sponsored',
23
24
  logo: './path/to/sponsor/logo.png',
24
25
  altText: 'Sponsor Name',
25
26
  }}
26
- buttonText="Learn more"
27
- onClick={() => redirect("https://www.sponsor-name.com/promotion")}
27
+ subheading="Portugal and 6 more countries have just been added to the UK travel green list"
28
+ tagline="Tagline"
28
29
  textAlign={TEXT_ALIGN.start}
29
- invertVertically
30
30
  />
31
31
  );
32
32
  ```
@@ -35,17 +35,17 @@ export default () => (
35
35
 
36
36
  | Property | PropType | Required | Default Value |
37
37
  | ---------------- | ----------------- | -------- | ------------- |
38
- | contentId | string | false | null |
39
38
  | buttonText | string | true | - |
40
39
  | headline | string | true | - |
41
- | invertVertically | bool | true | - |
42
40
  | onClick | func | true | - |
43
41
  | textAlign | oneOf(TEXT_ALIGN) | true | - |
44
42
  | className | string | false | null |
45
- | style | object | false | {} |
46
- | tagline | string | false | null |
43
+ | contentId | string | false | null |
44
+ | invertVertically | bool | false | false |
47
45
  | sponsor | object | false | null |
46
+ | style | object | false | {} |
48
47
  | subheading | string | false | null |
48
+ | tagline | string | false | null |
49
49
 
50
50
  ### Prop Details
51
51
 
@@ -61,7 +61,7 @@ The object consists of the following fields:
61
61
 
62
62
  ##### Example
63
63
 
64
- ```typescript
64
+ ```ts
65
65
  const sponsor = {
66
66
  label: 'Sponsored',
67
67
  altText: 'Skyscanner',
@@ -15,7 +15,6 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- /* @flow strict */
19
18
 
20
19
  import BpkGraphicPromo, {
21
20
  type Props as BpkGraphicPromoProps,
@@ -8,7 +8,7 @@
8
8
  "url": "git@github.com:Skyscanner/backpack.git"
9
9
  },
10
10
  "author": "Backpack Design System <backpack@skyscanner.net>",
11
- "main": "index.js",
11
+ "main": "index.ts",
12
12
  "publishConfig": {
13
13
  "registry": "https://registry.npmjs.org/"
14
14
  },
@@ -16,8 +16,7 @@
16
16
  "bpk-component-button": "^7.0.1",
17
17
  "bpk-component-text": "^8.0.1",
18
18
  "bpk-mixins": "^31.4.0",
19
- "bpk-react-utils": "^7.0.0",
20
- "prop-types": "^15.5.8"
19
+ "bpk-react-utils": "^7.0.0"
21
20
  },
22
21
  "peerDependencies": {
23
22
  "react": "^17.0.2"
@@ -0,0 +1,133 @@
1
+ /*
2
+ * Backpack - Skyscanner's Design System
3
+ *
4
+ * Copyright 2022 Skyscanner Ltd
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import React from 'react';
20
+ import { fireEvent, render } from '@testing-library/react';
21
+
22
+ // @ts-expect-error Untyped import. Not added as a stub declaration so consumers still have the suppression in their node_modules when shipped untranspiled.
23
+ import { cssModules } from '../../bpk-react-utils';
24
+
25
+ import BpkGraphicPromo, { TEXT_ALIGN } from './BpkGraphicPromo';
26
+ import type { Props } from './BpkGraphicPromo';
27
+ import STYLES from './BpkGraphicPromo.module.scss';
28
+
29
+ const getClassName = cssModules(STYLES);
30
+
31
+ const props: Props = {
32
+ buttonText: 'Learn more',
33
+ headline: 'Ride your wave',
34
+ invertVertically: false,
35
+ onClick: jest.fn(),
36
+ sponsor: {
37
+ label: 'Sponsored',
38
+ logo: 'path/to/logo.png',
39
+ altText: 'Airline Name',
40
+ },
41
+ subheading:
42
+ 'Portugal and 6 more countries have just been added to the UK travel green list',
43
+ tagline: 'Tagline',
44
+ textAlign: TEXT_ALIGN.start,
45
+ };
46
+
47
+ describe('BpkGraphicPromo', () => {
48
+ beforeEach(() => {
49
+ (props.onClick as jest.MockedFunction<Props['onClick']>).mockReset();
50
+ });
51
+
52
+ it('should render correctly with all properties set', () => {
53
+ const { asFragment } = render(<BpkGraphicPromo {...props} />);
54
+
55
+ expect(asFragment()).toMatchSnapshot();
56
+ });
57
+
58
+ it('should support custom class names', () => {
59
+ const { asFragment } = render(
60
+ <BpkGraphicPromo className="custom-classname" {...props} />,
61
+ );
62
+
63
+ expect(asFragment()).toMatchSnapshot();
64
+ });
65
+
66
+ it('should render correctly when centre aligned', () => {
67
+ const customProps = { ...props, textAlign: TEXT_ALIGN.center };
68
+ const { asFragment } = render(<BpkGraphicPromo {...customProps} />);
69
+
70
+ expect(asFragment()).toMatchSnapshot();
71
+ });
72
+
73
+ it('should render correctly when right aligned', () => {
74
+ const customProps = { ...props, textAlign: TEXT_ALIGN.end };
75
+ const { asFragment } = render(<BpkGraphicPromo {...customProps} />);
76
+
77
+ expect(asFragment()).toMatchSnapshot();
78
+ });
79
+
80
+ it('should render correctly when inverted portrait', () => {
81
+ const customProps = { ...props, invertVertically: true };
82
+ const { asFragment } = render(<BpkGraphicPromo {...customProps} />);
83
+
84
+ expect(asFragment()).toMatchSnapshot();
85
+ });
86
+
87
+ it('should not display tagline or subheading when not provided', () => {
88
+ const customProps = { ...props };
89
+ delete customProps.tagline;
90
+ delete customProps.subheading;
91
+ const { asFragment } = render(<BpkGraphicPromo {...customProps} />);
92
+
93
+ expect(asFragment()).toMatchSnapshot();
94
+ });
95
+
96
+ it('should display the tagline and no sponsor for non-sponsored ad', () => {
97
+ const customProps = { ...props };
98
+ delete customProps.sponsor;
99
+ const { asFragment } = render(<BpkGraphicPromo {...customProps} />);
100
+
101
+ expect(asFragment()).toMatchSnapshot();
102
+ });
103
+
104
+ it('should redirect us to link when card is clicked', () => {
105
+ render(<BpkGraphicPromo {...props} />);
106
+
107
+ const graphicPromo = document.getElementsByClassName(
108
+ getClassName('bpk-graphic-promo'),
109
+ )[0];
110
+ fireEvent.click(graphicPromo);
111
+
112
+ expect(props.onClick).toHaveBeenCalledTimes(1);
113
+ });
114
+
115
+ it('should redirect us to link when button is clicked', () => {
116
+ render(<BpkGraphicPromo {...props} />);
117
+
118
+ const graphicPromoCTA = document.getElementsByClassName(
119
+ getClassName('bpk-graphic-promo__cta'),
120
+ )[0];
121
+ fireEvent.click(graphicPromoCTA);
122
+
123
+ expect(props.onClick).toHaveBeenCalledTimes(1);
124
+ });
125
+
126
+ it('should render correctly with a content ID', () => {
127
+ const { asFragment } = render(
128
+ <BpkGraphicPromo contentId="content-id" {...props} />,
129
+ );
130
+
131
+ expect(asFragment()).toMatchSnapshot();
132
+ });
133
+ });
@@ -15,13 +15,14 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- /* @flow strict */
19
18
 
20
- import PropTypes from 'prop-types';
21
19
  import React from 'react';
22
20
 
21
+ // @ts-expect-error Untyped import. Not added as a stub declaration so consumers still have the suppression in their node_modules when shipped untranspiled.
23
22
  import { cssModules } from '../../bpk-react-utils';
23
+ // @ts-expect-error Untyped import. Not added as a stub declaration so consumers still have the suppression in their node_modules when shipped untranspiled.
24
24
  import BpkText from '../../bpk-component-text';
25
+ // @ts-expect-error Untyped import. Not added as a stub declaration so consumers still have the suppression in their node_modules when shipped untranspiled.
25
26
  import BpkButton from '../../bpk-component-button';
26
27
 
27
28
  import STYLES from './BpkGraphicPromo.module.scss';
@@ -41,64 +42,66 @@ export const TEXT_ALIGN = {
41
42
  start: 'start',
42
43
  center: 'center',
43
44
  end: 'end',
44
- };
45
+ } as const;
45
46
 
46
47
  export type Props = {
47
- className: ?string,
48
- contentId: ?string,
49
- tagline: ?string,
50
- headline: string,
51
- subheading: ?string,
52
- sponsor: ?{
53
- label: string,
54
- logo: string,
55
- altText: string,
56
- },
57
- buttonText: string,
58
- onClick: () => void,
59
- invertVertically: boolean,
60
- textAlign: TEXT_ALIGN,
61
- style: ?{},
48
+ buttonText: string;
49
+ className?: string | null;
50
+ contentId?: string | null;
51
+ headline: string;
52
+ invertVertically?: boolean;
53
+ onClick: () => void;
54
+ sponsor?: {
55
+ label: string;
56
+ logo: string;
57
+ altText: string;
58
+ } | null;
59
+ style?: {};
60
+ subheading?: string | null;
61
+ tagline?: string | null;
62
+ textAlign: typeof TEXT_ALIGN[keyof typeof TEXT_ALIGN];
62
63
  };
63
64
 
64
- const constructAriaLabel = ({
65
- buttonText,
66
- headline,
67
- sponsor,
68
- subheading,
69
- tagline,
70
- }: Props) => {
71
- const text = [];
72
- const addText = (value) => value && text.push(value);
65
+ const constructAriaLabel = (
66
+ buttonText: Props['buttonText'],
67
+ headline: Props['headline'],
68
+ sponsor: Props['sponsor'],
69
+ subheading: Props['subheading'],
70
+ tagline: Props['tagline'],
71
+ ) => {
72
+ const text: string[] = [];
73
+ const addText = (value: string) => value && text.push(value);
73
74
 
74
75
  if (sponsor) {
75
76
  addText(sponsor.label);
76
77
  addText(sponsor.altText);
77
- } else {
78
+ } else if (tagline) {
78
79
  addText(tagline);
79
80
  }
81
+
80
82
  addText(headline);
81
- addText(subheading);
83
+
84
+ if (subheading) {
85
+ addText(subheading);
86
+ }
82
87
  addText(buttonText);
83
88
 
84
89
  return text.join('. ');
85
90
  };
86
91
 
87
- const BpkGraphicPromo = (props: Props) => {
88
- const {
89
- buttonText,
90
- className,
91
- contentId,
92
- headline,
93
- invertVertically,
94
- onClick,
95
- sponsor,
96
- style,
97
- subheading,
98
- tagline,
99
- textAlign,
100
- } = props;
101
-
92
+ const BpkGraphicPromo = ({
93
+ buttonText,
94
+ className = null,
95
+ contentId,
96
+ headline,
97
+ invertVertically = false,
98
+ onClick,
99
+ sponsor = null,
100
+ style = {},
101
+ subheading = null,
102
+ tagline = null,
103
+ textAlign,
104
+ }: Props) => {
102
105
  // FIXME: Use useCallback() here when React is updated.
103
106
  const onClickWrapper = (event: React.MouseEvent<HTMLElement>) => {
104
107
  event.stopPropagation();
@@ -122,17 +125,23 @@ const BpkGraphicPromo = (props: Props) => {
122
125
  // clicks and key presses (Enter/Space) for the whole component, as described here:
123
126
  // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets
124
127
  <div
125
- id={contentId}
128
+ id={contentId || ''}
126
129
  className={cardClasses}
127
130
  style={style}
128
131
  role="link"
129
- aria-label={constructAriaLabel(props)}
132
+ aria-label={constructAriaLabel(
133
+ buttonText,
134
+ headline,
135
+ sponsor,
136
+ subheading,
137
+ tagline,
138
+ )}
130
139
  tabIndex={0}
131
140
  onClick={onClickWrapper}
132
141
  onKeyDown={onKeyWrapper}
133
142
  >
134
143
  <div
135
- id={contentId && `${contentId}__content`}
144
+ id={(contentId && `${contentId}__content`) || ''}
136
145
  className={containerClasses}
137
146
  aria-hidden
138
147
  >
@@ -190,30 +199,4 @@ const BpkGraphicPromo = (props: Props) => {
190
199
  );
191
200
  };
192
201
 
193
- BpkGraphicPromo.propTypes = {
194
- className: PropTypes.string,
195
- tagline: PropTypes.string,
196
- headline: PropTypes.string.isRequired,
197
- subheading: PropTypes.string,
198
- sponsor: PropTypes.shape({
199
- label: PropTypes.string,
200
- logo: PropTypes.string,
201
- altText: PropTypes.string,
202
- }),
203
- buttonText: PropTypes.string.isRequired,
204
- onClick: PropTypes.func.isRequired,
205
- invertVertically: PropTypes.bool,
206
- textAlign: PropTypes.oneOf(Object.values(TEXT_ALIGN)).isRequired,
207
- style: PropTypes.shape({ [PropTypes.string]: PropTypes.string }),
208
- };
209
-
210
- BpkGraphicPromo.defaultProps = {
211
- className: null,
212
- tagline: null,
213
- subheading: null,
214
- sponsor: null,
215
- invertVertically: false,
216
- style: {},
217
- };
218
-
219
202
  export default BpkGraphicPromo;
@@ -0,0 +1,147 @@
1
+ /*
2
+ * Backpack - Skyscanner's Design System
3
+ *
4
+ * Copyright 2022 Skyscanner Ltd
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ import React from 'react';
19
+ import { render } from '@testing-library/react';
20
+ import { axe } from 'jest-axe';
21
+
22
+ import BpkGraphicPromo, { TEXT_ALIGN } from './BpkGraphicPromo';
23
+
24
+ const sponsor = {
25
+ label: 'Sponsored',
26
+ logo: 'path/to/logo.png',
27
+ altText: 'Airline Name',
28
+ };
29
+ const buttonText = 'Learn more';
30
+ const onClick = () => {
31
+ window.location.href = 'https://www.skyscanner.net';
32
+ };
33
+
34
+ const tagline = 'Tagline';
35
+ const headline = 'Ride your wave';
36
+ const subheading =
37
+ 'Portugal and 6 more countries have just been added to the UK travel green list';
38
+
39
+ describe('BpkGraphicPromo accessibility tests', () => {
40
+ // TODO: Temporarily disabling tests due to a newer version of jest-axe which causes tests to fail when using nested interactive elements
41
+
42
+ it('should not have programmatically-detectable accessibility issues', async () => {
43
+ const { container } = render(
44
+ <BpkGraphicPromo
45
+ buttonText={buttonText}
46
+ className="test"
47
+ headline={headline}
48
+ invertVertically={false}
49
+ onClick={onClick}
50
+ sponsor={sponsor}
51
+ subheading={subheading}
52
+ tagline={tagline}
53
+ textAlign={TEXT_ALIGN.start}
54
+ />,
55
+ );
56
+
57
+ const results = await axe(container);
58
+ expect(results).toHaveNoViolations();
59
+ });
60
+
61
+ it('should not have programmatically-detectable accessibility issues in centre aligned', async () => {
62
+ const { container } = render(
63
+ <BpkGraphicPromo
64
+ buttonText={buttonText}
65
+ headline={headline}
66
+ invertVertically={false}
67
+ onClick={onClick}
68
+ sponsor={sponsor}
69
+ subheading={subheading}
70
+ tagline={tagline}
71
+ textAlign={TEXT_ALIGN.center}
72
+ />,
73
+ );
74
+
75
+ const results = await axe(container);
76
+ expect(results).toHaveNoViolations();
77
+ });
78
+
79
+ it('should not have programmatically-detectable accessibility issues in right aligned', async () => {
80
+ const { container } = render(
81
+ <BpkGraphicPromo
82
+ buttonText={buttonText}
83
+ headline={headline}
84
+ invertVertically={false}
85
+ onClick={onClick}
86
+ sponsor={sponsor}
87
+ subheading={subheading}
88
+ tagline={tagline}
89
+ textAlign={TEXT_ALIGN.end}
90
+ />,
91
+ );
92
+
93
+ const results = await axe(container);
94
+ expect(results).toHaveNoViolations();
95
+ });
96
+
97
+ it('should not have programmatically-detectable accessibility issues in inverted portrait', async () => {
98
+ const { container } = render(
99
+ <BpkGraphicPromo
100
+ buttonText={buttonText}
101
+ headline={headline}
102
+ invertVertically
103
+ onClick={onClick}
104
+ sponsor={sponsor}
105
+ subheading={subheading}
106
+ tagline={tagline}
107
+ textAlign={TEXT_ALIGN.start}
108
+ />,
109
+ );
110
+
111
+ const results = await axe(container);
112
+ expect(results).toHaveNoViolations();
113
+ });
114
+
115
+ it('should not have programmatically-detectable accessibility issues when optional text is missing', async () => {
116
+ const { container } = render(
117
+ <BpkGraphicPromo
118
+ buttonText={buttonText}
119
+ headline={headline}
120
+ invertVertically={false}
121
+ onClick={onClick}
122
+ sponsor={sponsor}
123
+ textAlign={TEXT_ALIGN.start}
124
+ />,
125
+ );
126
+
127
+ const results = await axe(container);
128
+ expect(results).toHaveNoViolations();
129
+ });
130
+
131
+ it('should not have programmatically-detectable accessibility issues in non-sponsored', async () => {
132
+ const { container } = render(
133
+ <BpkGraphicPromo
134
+ buttonText={buttonText}
135
+ headline={headline}
136
+ invertVertically={false}
137
+ onClick={onClick}
138
+ subheading={subheading}
139
+ tagline={tagline}
140
+ textAlign={TEXT_ALIGN.start}
141
+ />,
142
+ );
143
+
144
+ const results = await axe(container);
145
+ expect(results).toHaveNoViolations();
146
+ });
147
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyscanner/backpack-web",
3
- "version": "9.1.0",
3
+ "version": "9.2.0-beta.3",
4
4
  "description": "Backpack Design System web library",
5
5
  "repository": {
6
6
  "type": "git",