@skyscanner/backpack-web 22.1.0 → 22.3.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/bpk-component-link/README.md +1 -0
- package/bpk-component-link/src/BpkLink.js +4 -3
- package/bpk-component-navigation-bar/src/BpkNavigationBar.module.scss +4 -3
- package/bpk-component-navigation-bar/src/BpkNavigationBarButtonLink.module.scss +4 -4
- package/bpk-component-navigation-bar/src/BpkNavigationBarIconButton.module.scss +3 -3
- package/bpk-component-rating/README.md +8 -1
- package/bpk-component-rating/src/BpkRating.js +15 -11
- package/bpk-component-rating/src/BpkRating.module.scss +10 -13
- package/bpk-react-utils/README.md +33 -30
- package/bpk-react-utils/src/Portal.js +16 -1
- package/package.json +1 -1
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
/* @flow strict */
|
|
20
|
-
|
|
21
20
|
import PropTypes from 'prop-types';
|
|
22
21
|
import type { Node } from 'react';
|
|
22
|
+
import { forwardRef } from 'react';
|
|
23
23
|
|
|
24
24
|
import { cssModules } from '../../bpk-react-utils';
|
|
25
25
|
|
|
@@ -40,7 +40,7 @@ type Props = {
|
|
|
40
40
|
alternate: boolean,
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
const BpkLink = (props: Props) => {
|
|
43
|
+
const BpkLink = forwardRef((props: Props, ref) => {
|
|
44
44
|
const {
|
|
45
45
|
alternate,
|
|
46
46
|
blank,
|
|
@@ -72,12 +72,13 @@ const BpkLink = (props: Props) => {
|
|
|
72
72
|
onClick={onClick}
|
|
73
73
|
target={target}
|
|
74
74
|
rel={rel}
|
|
75
|
+
ref={ref}
|
|
75
76
|
{...rest}
|
|
76
77
|
>
|
|
77
78
|
{children}
|
|
78
79
|
</a>
|
|
79
80
|
);
|
|
80
|
-
};
|
|
81
|
+
});
|
|
81
82
|
|
|
82
83
|
BpkLink.propTypes = {
|
|
83
84
|
children: PropTypes.oneOfType([
|
|
@@ -32,15 +32,14 @@ $bpk-spacing-v2: true;
|
|
|
32
32
|
@include bpk-themeable-property(
|
|
33
33
|
background-color,
|
|
34
34
|
--bpk-navigation-bar-background-color,
|
|
35
|
-
$bpk-
|
|
35
|
+
$bpk-surface-default-day
|
|
36
36
|
);
|
|
37
|
-
@include bpk-border-bottom-sm($bpk-line-day);
|
|
38
37
|
|
|
39
38
|
&__title {
|
|
40
39
|
@include bpk-themeable-property(
|
|
41
40
|
color,
|
|
42
41
|
--bpk-navigation-bar-title-color,
|
|
43
|
-
$bpk-text-primary-
|
|
42
|
+
$bpk-text-primary-day
|
|
44
43
|
);
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -73,5 +72,7 @@ $bpk-spacing-v2: true;
|
|
|
73
72
|
position: sticky;
|
|
74
73
|
top: 0;
|
|
75
74
|
z-index: $bpk-zindex-tooltip - 1; // Allow tooltips/modals/... to be displayed above the navigation bar
|
|
75
|
+
|
|
76
|
+
@include bpk-box-shadow-sm;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
@include bpk-themeable-property(
|
|
23
23
|
color,
|
|
24
24
|
--bpk-navigation-bar-button-link-color,
|
|
25
|
-
$bpk-text-primary-
|
|
25
|
+
$bpk-text-primary-day
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
@include bpk-hover {
|
|
29
29
|
@include bpk-themeable-property(
|
|
30
30
|
color,
|
|
31
31
|
--bpk-navigation-bar-button-link-hover-color,
|
|
32
|
-
$bpk-text-primary-
|
|
32
|
+
$bpk-text-primary-day
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
@include bpk-themeable-property(
|
|
38
38
|
color,
|
|
39
39
|
--bpk-navigation-bar-button-link-active-color,
|
|
40
|
-
$bpk-text-primary-
|
|
40
|
+
$bpk-text-primary-day
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
@include bpk-themeable-property(
|
|
46
46
|
color,
|
|
47
47
|
--bpk-navigation-bar-button-link-visited-color,
|
|
48
|
-
$bpk-text-primary-
|
|
48
|
+
$bpk-text-primary-day
|
|
49
49
|
);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
@include bpk-themeable-property(
|
|
23
23
|
color,
|
|
24
24
|
--bpk-navigation-bar-icon-button-color,
|
|
25
|
-
$bpk-text-primary-
|
|
25
|
+
$bpk-text-primary-day
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
@include bpk-hover {
|
|
29
29
|
@include bpk-themeable-property(
|
|
30
30
|
color,
|
|
31
31
|
--bpk-navigation-bar-icon-button-hover-color,
|
|
32
|
-
$bpk-text-primary-
|
|
32
|
+
$bpk-text-primary-day
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
@include bpk-themeable-property(
|
|
38
38
|
color,
|
|
39
39
|
--bpk-navigation-bar-icon-button-active-color,
|
|
40
|
-
$bpk-text-primary-
|
|
40
|
+
$bpk-text-primary-day
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -35,6 +35,13 @@ export default () => (
|
|
|
35
35
|
value={2.3}
|
|
36
36
|
/>
|
|
37
37
|
|
|
38
|
+
// Subtitle only rating
|
|
39
|
+
<BpkRating
|
|
40
|
+
ariaLabel="4.8 2,420 reviews"
|
|
41
|
+
value={4.8}
|
|
42
|
+
subtitle="2,420 reviews"
|
|
43
|
+
/>
|
|
44
|
+
|
|
38
45
|
// Show scale rating
|
|
39
46
|
<BpkRating
|
|
40
47
|
ariaLabel="3.8 Good 530 reviews"
|
|
@@ -68,8 +75,8 @@ export default () => (
|
|
|
68
75
|
| Property | PropType | Required | Default Value |
|
|
69
76
|
| --------- | --------------------- | -------- | ----------------- |
|
|
70
77
|
| ariaLabel | string | true | - |
|
|
71
|
-
| title | oneOfType(string, node) | true | - |
|
|
72
78
|
| value | oneOfType(string, number) | true | - |
|
|
79
|
+
| title | oneOfType(string, node) | false | - |
|
|
73
80
|
| className | string | false | null |
|
|
74
81
|
| ratingScale | oneOf(RATING_SCALES) | false | RATING_SCALES.zeroToFive |
|
|
75
82
|
| size | oneOf(RATING_SIZES) | false | RATING_SIZES.base |
|
|
@@ -51,7 +51,7 @@ type Props = {
|
|
|
51
51
|
showScale: ?boolean,
|
|
52
52
|
size: $Values<typeof RATING_SIZES>,
|
|
53
53
|
subtitle: ?string,
|
|
54
|
-
title: string | Node,
|
|
54
|
+
title: ?string | Node,
|
|
55
55
|
value: string | number,
|
|
56
56
|
};
|
|
57
57
|
|
|
@@ -71,7 +71,7 @@ const BpkRating = (props: Props) => {
|
|
|
71
71
|
const classNames = getClassName(
|
|
72
72
|
'bpk-rating',
|
|
73
73
|
className,
|
|
74
|
-
size === RATING_SIZES.large && subtitle && 'bpk-rating--large',
|
|
74
|
+
size === RATING_SIZES.large && title && subtitle && 'bpk-rating--large',
|
|
75
75
|
);
|
|
76
76
|
const valueStyles = getClassName('bpk-rating__value');
|
|
77
77
|
const scaleStyles = getClassName('bpk-rating__scale');
|
|
@@ -80,6 +80,7 @@ const BpkRating = (props: Props) => {
|
|
|
80
80
|
size === RATING_SIZES.large && 'bpk-rating__text-wrapper--large',
|
|
81
81
|
);
|
|
82
82
|
const titleStyles = getClassName(
|
|
83
|
+
subtitle && 'bpk-rating__title--with-subtitle',
|
|
83
84
|
size === RATING_SIZES.large && 'bpk-rating__title--large',
|
|
84
85
|
);
|
|
85
86
|
const subtitleStyles = getClassName(
|
|
@@ -132,14 +133,16 @@ const BpkRating = (props: Props) => {
|
|
|
132
133
|
</BpkText>
|
|
133
134
|
|
|
134
135
|
<div className={textWrapperStyles}>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
{title && (
|
|
137
|
+
<BpkText
|
|
138
|
+
textStyle={titleTextSize}
|
|
139
|
+
className={titleStyles}
|
|
140
|
+
tagName="span"
|
|
141
|
+
aria-hidden="true"
|
|
142
|
+
>
|
|
143
|
+
{title}
|
|
144
|
+
</BpkText>
|
|
145
|
+
)}
|
|
143
146
|
|
|
144
147
|
{subtitle && (
|
|
145
148
|
<BpkText
|
|
@@ -158,13 +161,13 @@ const BpkRating = (props: Props) => {
|
|
|
158
161
|
|
|
159
162
|
BpkRating.propTypes = {
|
|
160
163
|
ariaLabel: PropTypes.string.isRequired,
|
|
161
|
-
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
|
|
162
164
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
163
165
|
className: PropTypes.string,
|
|
164
166
|
ratingScale: PropTypes.oneOf(Object.keys(RATING_SCALES)),
|
|
165
167
|
size: PropTypes.oneOf(Object.keys(RATING_SIZES)),
|
|
166
168
|
subtitle: PropTypes.string,
|
|
167
169
|
showScale: PropTypes.bool,
|
|
170
|
+
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
|
168
171
|
};
|
|
169
172
|
|
|
170
173
|
BpkRating.defaultProps = {
|
|
@@ -173,6 +176,7 @@ BpkRating.defaultProps = {
|
|
|
173
176
|
size: RATING_SIZES.base,
|
|
174
177
|
subtitle: null,
|
|
175
178
|
showScale: true,
|
|
179
|
+
title: null,
|
|
176
180
|
};
|
|
177
181
|
|
|
178
182
|
export default BpkRating;
|
|
@@ -59,28 +59,25 @@ $bpk-spacing-v2: true;
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
&__title {
|
|
62
|
+
&--with-subtitle {
|
|
63
|
+
padding-right: bpk-spacing-md();
|
|
64
|
+
|
|
65
|
+
@include bpk-rtl {
|
|
66
|
+
padding-right: 0;
|
|
67
|
+
padding-left: bpk-spacing-md();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
62
71
|
&--large {
|
|
63
72
|
padding-top: $bpk-one-pixel-rem;
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
&__subtitle {
|
|
68
|
-
padding-left: bpk-spacing-md();
|
|
69
77
|
color: $bpk-text-secondary-day;
|
|
70
78
|
|
|
71
|
-
@include bpk-rtl {
|
|
72
|
-
padding-right: bpk-spacing-md();
|
|
73
|
-
padding-left: 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
79
|
&--large {
|
|
77
|
-
padding-top: $bpk-one-pixel-rem;
|
|
78
|
-
padding-left: 0;
|
|
79
|
-
|
|
80
|
-
@include bpk-rtl {
|
|
81
|
-
padding-right: 0;
|
|
82
|
-
padding-left: 0;
|
|
83
|
-
}
|
|
80
|
+
// padding-top: $bpk-one-pixel-rem;
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
}
|
|
@@ -26,30 +26,28 @@ class MyComponent extends Component {
|
|
|
26
26
|
this.state = {
|
|
27
27
|
isOpen: false,
|
|
28
28
|
};
|
|
29
|
-
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
onOpen = () => {
|
|
33
32
|
this.setState({
|
|
34
33
|
isOpen: true,
|
|
35
34
|
});
|
|
36
|
-
}
|
|
35
|
+
};
|
|
37
36
|
|
|
38
37
|
onClose = () => {
|
|
39
38
|
this.setState({
|
|
40
39
|
isOpen: false,
|
|
41
40
|
});
|
|
42
|
-
}
|
|
41
|
+
};
|
|
43
42
|
|
|
44
43
|
render() {
|
|
45
44
|
return (
|
|
46
45
|
<div>
|
|
47
46
|
<BpkButton onClick={this.onOpen}>Open portal</BpkButton>
|
|
48
|
-
<Portal
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<div>I'm now appended to <BpkCode>document.body</BpkCode></div>
|
|
47
|
+
<Portal isOpen={this.state.isOpen} onClose={this.onClose}>
|
|
48
|
+
<div>
|
|
49
|
+
I'm now appended to <BpkCode>document.body</BpkCode>
|
|
50
|
+
</div>
|
|
53
51
|
</Portal>
|
|
54
52
|
</div>
|
|
55
53
|
);
|
|
@@ -57,19 +55,25 @@ class MyComponent extends Component {
|
|
|
57
55
|
}
|
|
58
56
|
```
|
|
59
57
|
|
|
58
|
+
**NOTE:** Events used to bubble up from the portal into the parent component. Due to the way React works, events do not
|
|
59
|
+
bubble up into the component in the DOM where the portal is rendered (i.e. render target), but instead they bubble up
|
|
60
|
+
into the parent component in the shadow DOM, that is the component where you added the `Portal` in your React code. To
|
|
61
|
+
avoid confusion and bugs caused by this behaviour, we have prevented mouse and keyboard events from bubbling up outside
|
|
62
|
+
the portal.
|
|
63
|
+
|
|
60
64
|
### Props
|
|
61
65
|
|
|
62
|
-
| Property
|
|
63
|
-
|
|
|
64
|
-
| children
|
|
65
|
-
| isOpen
|
|
66
|
-
| beforeClose
|
|
67
|
-
| onClose
|
|
68
|
-
| onOpen
|
|
69
|
-
| onRender
|
|
70
|
-
| renderTarget
|
|
71
|
-
| target
|
|
72
|
-
| closeOnEscPressed
|
|
66
|
+
| Property | PropType | Required | Default Value |
|
|
67
|
+
| ----------------- | ----------------------- | -------- | ------------- |
|
|
68
|
+
| children | node | true | - |
|
|
69
|
+
| isOpen | bool | true | - |
|
|
70
|
+
| beforeClose | func | false | null |
|
|
71
|
+
| onClose | func | false | noop |
|
|
72
|
+
| onOpen | func | false | noop |
|
|
73
|
+
| onRender | func | false | noop |
|
|
74
|
+
| renderTarget | func | false | null |
|
|
75
|
+
| target | oneOf([function, node]) | false | null |
|
|
76
|
+
| closeOnEscPressed | bool | false | true |
|
|
73
77
|
|
|
74
78
|
## `cssModules.js`
|
|
75
79
|
|
|
@@ -86,9 +90,7 @@ const getClassName = cssModules(STYLES);
|
|
|
86
90
|
|
|
87
91
|
const MyComponent = (props) => (
|
|
88
92
|
<div className={getClassName('MyComponent')}>
|
|
89
|
-
<div className={getClassName('MyComponent__inner')}>
|
|
90
|
-
{props.children}
|
|
91
|
-
</div>
|
|
93
|
+
<div className={getClassName('MyComponent__inner')}>{props.children}</div>
|
|
92
94
|
</div>
|
|
93
95
|
);
|
|
94
96
|
```
|
|
@@ -97,9 +99,7 @@ With CSS modules:
|
|
|
97
99
|
|
|
98
100
|
```html
|
|
99
101
|
<div class="_35WloynrPDta8fhSfoHEgE">
|
|
100
|
-
<div class="_1ghNYY7jOYzUneVCT4piQ9">
|
|
101
|
-
Some text.
|
|
102
|
-
</div>
|
|
102
|
+
<div class="_1ghNYY7jOYzUneVCT4piQ9">Some text.</div>
|
|
103
103
|
</div>
|
|
104
104
|
```
|
|
105
105
|
|
|
@@ -107,9 +107,7 @@ Without CSS modules:
|
|
|
107
107
|
|
|
108
108
|
```html
|
|
109
109
|
<div class="MyComponent">
|
|
110
|
-
<div class="MyComponent__inner">
|
|
111
|
-
Some text.
|
|
112
|
-
</div>
|
|
110
|
+
<div class="MyComponent__inner">Some text.</div>
|
|
113
111
|
</div>
|
|
114
112
|
```
|
|
115
113
|
|
|
@@ -123,8 +121,13 @@ import STYLES from './MyComponent.scss';
|
|
|
123
121
|
const getClassNames = cssModules(STYLES);
|
|
124
122
|
|
|
125
123
|
const MyComponent = (props) => (
|
|
126
|
-
<div
|
|
127
|
-
|
|
124
|
+
<div
|
|
125
|
+
className={getClassName(
|
|
126
|
+
'MyComponent',
|
|
127
|
+
props.disabled && 'MyComponent--disabled',
|
|
128
|
+
)}
|
|
129
|
+
>
|
|
130
|
+
{props.children}
|
|
128
131
|
</div>
|
|
129
132
|
);
|
|
130
133
|
```
|
|
@@ -263,7 +263,22 @@ class Portal extends Component {
|
|
|
263
263
|
return null;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
const stopPropagationHandler = (e) => e.stopPropagation();
|
|
267
|
+
|
|
268
|
+
return createPortal(
|
|
269
|
+
<div
|
|
270
|
+
onClick={stopPropagationHandler}
|
|
271
|
+
onMouseDown={stopPropagationHandler}
|
|
272
|
+
onMouseUp={stopPropagationHandler}
|
|
273
|
+
onKeyDown={stopPropagationHandler}
|
|
274
|
+
onKeyUp={stopPropagationHandler}
|
|
275
|
+
tabIndex={-1}
|
|
276
|
+
role="none"
|
|
277
|
+
>
|
|
278
|
+
{children}
|
|
279
|
+
</div>,
|
|
280
|
+
portalElement,
|
|
281
|
+
);
|
|
267
282
|
}
|
|
268
283
|
}
|
|
269
284
|
|