@skyscanner/backpack-web 41.10.0-beta-v1 → 41.11.1-beta.1

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.
Files changed (79) hide show
  1. package/bpk-component-accordion/src/BpkAccordion.js +2 -1
  2. package/bpk-component-accordion/src/BpkAccordionItem.js +2 -1
  3. package/bpk-component-aria-live/src/BpkAriaLive.js +2 -1
  4. package/bpk-component-autosuggest/src/BpkAutosuggestV2/BpkAutosuggest.js +2 -1
  5. package/bpk-component-bottom-sheet/src/BpkBottomSheet.js +14 -5
  6. package/bpk-component-breakpoint/src/BpkBreakpoint.js +2 -2
  7. package/bpk-component-breakpoint/src/useMediaQuery.js +27 -14
  8. package/bpk-component-bubble/src/BpkBubble.js +2 -1
  9. package/bpk-component-calendar/src/BpkCalendarDate.js +2 -1
  10. package/bpk-component-calendar/src/BpkCalendarGrid.js +2 -1
  11. package/bpk-component-calendar/src/BpkCalendarGridHeader.js +2 -1
  12. package/bpk-component-calendar/src/BpkCalendarGridTransition.js +2 -1
  13. package/bpk-component-calendar/src/BpkCalendarNav.js +2 -1
  14. package/bpk-component-calendar/src/BpkCalendarWeek.js +2 -1
  15. package/bpk-component-card-list/src/BpkCardList.js +3 -0
  16. package/bpk-component-card-list/src/BpkCardListRowRail/BpkCardListCarousel.js +15 -30
  17. package/bpk-component-card-list/src/BpkCardListRowRail/BpkCardListCarousel.module.css +1 -1
  18. package/bpk-component-card-list/src/BpkCardListRowRail/BpkCardListRowRailContainer.js +14 -1
  19. package/bpk-component-card-list/src/BpkCardListRowRail/constants.d.ts +1 -1
  20. package/bpk-component-card-list/src/BpkCardListRowRail/constants.js +1 -1
  21. package/bpk-component-card-list/src/BpkCardListRowRail/utils.d.ts +25 -8
  22. package/bpk-component-card-list/src/BpkCardListRowRail/utils.js +118 -35
  23. package/bpk-component-card-list/src/common-types.d.ts +3 -0
  24. package/bpk-component-chip/src/BpkDismissibleChip.js +3 -2
  25. package/bpk-component-chip/src/BpkDropdownChip.js +2 -0
  26. package/bpk-component-chip/src/BpkIconChip.js +2 -0
  27. package/bpk-component-chip/src/BpkSelectableChip.js +2 -1
  28. package/bpk-component-chip-group/src/BpkMultiSelectChipGroup.js +2 -1
  29. package/bpk-component-chip-group/src/BpkStickyChip.js +2 -1
  30. package/bpk-component-content-cards/src/BpkContentCard.module.css +1 -1
  31. package/bpk-component-input/src/withOpenEvents.d.ts +11 -12
  32. package/bpk-component-input/src/withOpenEvents.js +3 -3
  33. package/bpk-component-map/src/BpkBasicMapMarker.js +3 -0
  34. package/bpk-component-map/src/BpkPriceMarker.js +2 -0
  35. package/bpk-component-nudger/src/BpkNudger.js +2 -2
  36. package/bpk-component-panel/index.d.ts +2 -0
  37. package/bpk-component-panel/index.js +1 -0
  38. package/bpk-component-panel/src/BpkPanel.d.ts +14 -1
  39. package/bpk-component-panel/src/BpkPanel.js +4 -1
  40. package/bpk-component-panel/src/BpkPanel.module.css +1 -1
  41. package/bpk-component-price-range/src/BpkPriceMarker.js +2 -1
  42. package/bpk-component-price-range/src/BpkPriceRange.js +2 -1
  43. package/bpk-component-scrollable-calendar/src/BpkScrollableCalendarGrid.js +2 -1
  44. package/bpk-component-scrollable-calendar/src/BpkScrollableCalendarGridList.js +2 -1
  45. package/bpk-component-tooltip/src/BpkTooltip.js +2 -1
  46. package/bpk-mixins/_index.scss +1 -0
  47. package/bpk-mixins/_surfaces.scss +52 -0
  48. package/bpk-react-utils/index.d.ts +13 -1
  49. package/bpk-react-utils/index.js +4 -2
  50. package/bpk-react-utils/src/surfaceColors.d.ts +11 -0
  51. package/{bpk-component-layout/src/BpkStack.constant.js → bpk-react-utils/src/surfaceColors.js} +11 -4
  52. package/package.json +1 -2
  53. package/bpk-component-layout/index.d.ts +0 -18
  54. package/bpk-component-layout/index.js +0 -29
  55. package/bpk-component-layout/src/BpkBox.d.ts +0 -3
  56. package/bpk-component-layout/src/BpkBox.js +0 -33
  57. package/bpk-component-layout/src/BpkFlex.d.ts +0 -3
  58. package/bpk-component-layout/src/BpkFlex.js +0 -51
  59. package/bpk-component-layout/src/BpkGrid.d.ts +0 -3
  60. package/bpk-component-layout/src/BpkGrid.js +0 -57
  61. package/bpk-component-layout/src/BpkGridItem.d.ts +0 -3
  62. package/bpk-component-layout/src/BpkGridItem.js +0 -45
  63. package/bpk-component-layout/src/BpkProvider.d.ts +0 -14
  64. package/bpk-component-layout/src/BpkProvider.js +0 -42
  65. package/bpk-component-layout/src/BpkStack.constant.d.ts +0 -2
  66. package/bpk-component-layout/src/BpkStack.d.ts +0 -5
  67. package/bpk-component-layout/src/BpkStack.js +0 -57
  68. package/bpk-component-layout/src/BpkVessel.d.ts +0 -46
  69. package/bpk-component-layout/src/BpkVessel.js +0 -70
  70. package/bpk-component-layout/src/commonProps.d.ts +0 -86
  71. package/bpk-component-layout/src/commonProps.js +0 -1
  72. package/bpk-component-layout/src/theme.d.ts +0 -36
  73. package/bpk-component-layout/src/theme.js +0 -229
  74. package/bpk-component-layout/src/tokenUtils.d.ts +0 -108
  75. package/bpk-component-layout/src/tokenUtils.js +0 -323
  76. package/bpk-component-layout/src/tokens.d.ts +0 -96
  77. package/bpk-component-layout/src/tokens.js +0 -138
  78. package/bpk-component-layout/src/types.d.ts +0 -236
  79. package/bpk-component-layout/src/types.js +0 -1
@@ -19,25 +19,6 @@
19
19
  import { useEffect, useRef } from 'react';
20
20
  import { RELEASE_LOCK_DELAY } from "./constants";
21
21
 
22
- /**
23
- * Typically used to prevent useScrollToCard() from being called, to prevent scrollings caused by state changes, so as to avoid cyclic dependencies.
24
- * @param stateScrollingLockRef - Ref to the state that indicates if scrollIntoView should be locked
25
- * @param openSetStateLockTimeoutRef - Ref to the timeout that releases the lock after a delay. Should be renewed every time another scroll action is triggered, with a new lock is added.
26
- */
27
-
28
- export const lockScroll = (stateScrollingLockRef, openSetStateLockTimeoutRef) => {
29
- // eslint-disable-next-line no-param-reassign
30
- stateScrollingLockRef.current = true;
31
- if (openSetStateLockTimeoutRef.current) {
32
- clearTimeout(openSetStateLockTimeoutRef.current);
33
- }
34
- // eslint-disable-next-line no-param-reassign
35
- openSetStateLockTimeoutRef.current = setTimeout(() => {
36
- // eslint-disable-next-line no-param-reassign
37
- stateScrollingLockRef.current = false;
38
- }, RELEASE_LOCK_DELAY);
39
- };
40
-
41
22
  /**
42
23
  * Only sets the tabIndex of focusable elements as 0 if the card is visible, otherwise sets it to -1, including all its children.
43
24
  * For example, if there is a button inside a card which is not shown, it cannot be focused as well.
@@ -53,22 +34,6 @@ export const setA11yTabIndex = (el, index, visibilityList) => {
53
34
  targetElement.tabIndex = visibilityList[index] === 1 ? 0 : -1;
54
35
  });
55
36
  };
56
- export const useScrollToCard = (currentIndex, container, cardRefs, stateScrollingLockRef) => {
57
- useEffect(() => {
58
- const isVisible = container && container.getBoundingClientRect().bottom > 0 && container.getBoundingClientRect().bottom <= window.innerHeight;
59
- if (!isVisible) return; // Escape from scrollIntoView if the container is not visible, otherwise the webpage will scroll down to the last rendered & non-visible container
60
-
61
- if (stateScrollingLockRef.current && stateScrollingLockRef.current === true) return;
62
- const targetCard = cardRefs.current[currentIndex];
63
- if (targetCard) {
64
- targetCard.scrollIntoView({
65
- behavior: 'smooth',
66
- block: 'nearest',
67
- inline: 'start'
68
- });
69
- }
70
- }, [currentIndex]);
71
- };
72
37
  export const useIntersectionObserver = ({
73
38
  root,
74
39
  threshold
@@ -115,4 +80,122 @@ export const useIntersectionObserver = ({
115
80
  }
116
81
  };
117
82
  return observeElement;
83
+ };
84
+ /**
85
+ * Provides bidirectional synchronisation between page index state and scroll position.
86
+ *
87
+ * - **State → Scroll**: When `currentIndex` changes (e.g. via pagination buttons),
88
+ * the container scrolls to bring the corresponding page into view.
89
+ * - **Scroll → State**: When the user scrolls (via wheel or touch), `currentIndex`
90
+ * is updated to reflect the first visible page.
91
+ *
92
+ * The hook uses a lock mechanism to prevent conflicts between programmatic and
93
+ * user-initiated scrolling.
94
+ */
95
+
96
+ export const usePageScrollSync = ({
97
+ cardRefs,
98
+ container,
99
+ currentIndex,
100
+ enabled,
101
+ initialPageIndex,
102
+ initiallyShownCards,
103
+ setCurrentIndex,
104
+ visibilityList
105
+ }) => {
106
+ const isUserScrollingRef = useRef(false);
107
+ const scrollEndTimeoutRef = useRef(null);
108
+ const lastCurrentIndexRef = useRef(currentIndex);
109
+ const hasInitialScrolled = useRef(false);
110
+
111
+ // Effect 0: Initial scroll (instant, runs once on mount)
112
+ useEffect(() => {
113
+ if (hasInitialScrolled.current) {
114
+ return;
115
+ }
116
+ hasInitialScrolled.current = true;
117
+ if (!enabled || initialPageIndex === 0) {
118
+ return;
119
+ }
120
+ const targetCard = cardRefs.current?.[initialPageIndex * initiallyShownCards];
121
+ if (targetCard) {
122
+ targetCard.scrollIntoView({
123
+ behavior: 'instant',
124
+ block: 'nearest',
125
+ inline: 'start'
126
+ });
127
+ }
128
+ }, [cardRefs, enabled, initialPageIndex, initiallyShownCards]); // Run once on mount only
129
+
130
+ // Effect 1: Programmatic scroll when currentIndex changes
131
+ useEffect(() => {
132
+ if (!enabled ||
133
+ // Avoid triggering programmatic scroll when currentIndex changes due to user scroll
134
+ isUserScrollingRef.current || lastCurrentIndexRef.current === currentIndex) {
135
+ return;
136
+ }
137
+ lastCurrentIndexRef.current = currentIndex;
138
+ const isVisible = container && container.getBoundingClientRect().bottom > 0 && container.getBoundingClientRect().bottom <= window.innerHeight;
139
+ if (!isVisible) {
140
+ return;
141
+ }
142
+ const targetCard = cardRefs.current?.[currentIndex * initiallyShownCards];
143
+ if (targetCard) {
144
+ targetCard.scrollIntoView({
145
+ behavior: 'smooth',
146
+ block: 'nearest',
147
+ inline: 'start'
148
+ });
149
+ }
150
+ }, [currentIndex, enabled, container, initiallyShownCards, cardRefs]);
151
+
152
+ // Effect 2: Scroll detection and silence-based lock release
153
+ useEffect(() => {
154
+ if (!enabled || !container) return undefined;
155
+ const handleScrollActivity = e => {
156
+ // Set flag only for user input events, not scroll events
157
+ if (e.type === 'wheel' || e.type === 'touchstart') {
158
+ isUserScrollingRef.current = true;
159
+ }
160
+
161
+ // Reset silence timer on every scroll-related event
162
+ if (scrollEndTimeoutRef.current) {
163
+ clearTimeout(scrollEndTimeoutRef.current);
164
+ }
165
+
166
+ // Release lock after scroll silence
167
+ scrollEndTimeoutRef.current = setTimeout(() => {
168
+ isUserScrollingRef.current = false;
169
+ }, RELEASE_LOCK_DELAY);
170
+ };
171
+ container.addEventListener('scroll', handleScrollActivity, {
172
+ passive: true
173
+ });
174
+ container.addEventListener('wheel', handleScrollActivity, {
175
+ passive: true
176
+ });
177
+ container.addEventListener('touchstart', handleScrollActivity, {
178
+ passive: true
179
+ });
180
+ return () => {
181
+ container.removeEventListener('scroll', handleScrollActivity);
182
+ container.removeEventListener('wheel', handleScrollActivity);
183
+ container.removeEventListener('touchstart', handleScrollActivity);
184
+ if (scrollEndTimeoutRef.current) {
185
+ clearTimeout(scrollEndTimeoutRef.current);
186
+ }
187
+ };
188
+ }, [enabled, container]);
189
+
190
+ // Effect 3: Update currentIndex from visibility during user scroll
191
+ useEffect(() => {
192
+ if (!enabled || !isUserScrollingRef.current) return;
193
+ const firstVisibleIndex = visibilityList.indexOf(1);
194
+ if (firstVisibleIndex === -1) return;
195
+ const newPageIndex = Math.floor(firstVisibleIndex / initiallyShownCards);
196
+ if (newPageIndex !== currentIndex) {
197
+ setCurrentIndex(newPageIndex);
198
+ lastCurrentIndexRef.current = newPageIndex;
199
+ }
200
+ }, [visibilityList, enabled, initiallyShownCards, currentIndex, setCurrentIndex]);
118
201
  };
@@ -36,6 +36,7 @@ type CardListBaseProps = {
36
36
  accessoryMobile?: (typeof ACCESSORY_MOBILE_TYPES)[keyof typeof ACCESSORY_MOBILE_TYPES];
37
37
  initiallyShownCardsDesktop?: number;
38
38
  initiallyShownCardsMobile?: number;
39
+ initiallyInViewCardIndex?: number;
39
40
  chipGroup?: ReactElement;
40
41
  buttonContent?: React.ReactNode;
41
42
  onButtonClick?: () => void;
@@ -69,6 +70,7 @@ type CardListRowRailProps = {
69
70
  accessory?: typeof ACCESSORY_DESKTOP_TYPES.pagination;
70
71
  isMobile?: boolean;
71
72
  accessibilityLabels?: AccessibilityLabels;
73
+ initiallyInViewCardIndex: number;
72
74
  };
73
75
  type CardListCarouselProps = {
74
76
  children: Array<ReactElement<HTMLDivElement | HTMLAnchorElement>>;
@@ -79,6 +81,7 @@ type CardListCarouselProps = {
79
81
  isMobile?: boolean;
80
82
  carouselLabel?: (initiallyShownCards: number, childrenLength: number) => string;
81
83
  slideLabel?: (index: number, childrenLength: number) => string;
84
+ initialPageIndex: number;
82
85
  };
83
86
  type CardListProps = CardListBaseProps;
84
87
  export default CardListProps;
@@ -23,7 +23,7 @@ accessory view of a close icon.
23
23
  */
24
24
  import { chipColors } from '@skyscanner/bpk-foundations-web/tokens/base.es6';
25
25
  import CloseCircleIconSm from "../../bpk-component-icon/sm/close-circle";
26
- import { cssModules } from "../../bpk-react-utils";
26
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
27
27
  import BpkSelectableChip from "./BpkSelectableChip";
28
28
  import { CHIP_TYPES } from "./commonTypes";
29
29
  import STYLES from "./BpkSelectableChip.module.css";
@@ -48,7 +48,8 @@ const BpkDismissibleChip = ({
48
48
  type: type,
49
49
  role: "button" // Override role="checkbox" because this chip is not selectable.
50
50
  ,
51
- className: classNames
51
+ className: classNames,
52
+ ...getDataComponentAttribute('DismissibleChip')
52
53
  });
53
54
  };
54
55
  export default BpkDismissibleChip;
@@ -22,6 +22,7 @@ with a trailing accessory view of a chevron down icon.
22
22
  */
23
23
 
24
24
  import ChevronDownIconSm from "../../bpk-component-icon/sm/chevron-down";
25
+ import { getDataComponentAttribute } from "../../bpk-react-utils";
25
26
  import BpkSelectableChip from "./BpkSelectableChip";
26
27
  import { CHIP_TYPES } from "./commonTypes";
27
28
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -36,6 +37,7 @@ const BpkDropdownChip = ({
36
37
  leadingAccessoryView: leadingAccessoryView,
37
38
  selected: selected,
38
39
  type: type,
40
+ ...getDataComponentAttribute('DropdownChip'),
39
41
  ...rest,
40
42
  trailingAccessoryView: /*#__PURE__*/_jsx(ChevronDownIconSm, {})
41
43
  });
@@ -22,9 +22,11 @@ been hard coded to have no text or trailing icon and padding/margin
22
22
  to match.
23
23
  */
24
24
 
25
+ import { getDataComponentAttribute } from "../../bpk-react-utils";
25
26
  import BpkSelectableChip from "./BpkSelectableChip";
26
27
  import { jsx as _jsx } from "react/jsx-runtime";
27
28
  const BpkIconChip = props => /*#__PURE__*/_jsx(BpkSelectableChip, {
29
+ ...getDataComponentAttribute('IconChip'),
28
30
  ...props,
29
31
  children: null
30
32
  });
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import BpkText, { TEXT_STYLES } from "../../bpk-component-text";
20
- import { cssModules } from "../../bpk-react-utils";
20
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
21
21
  import { CHIP_TYPES } from "./commonTypes";
22
22
  import STYLES from "./BpkSelectableChip.module.css";
23
23
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -39,6 +39,7 @@ const BpkSelectableChip = ({
39
39
  return /*#__PURE__*/_jsxs("button", {
40
40
  "aria-checked": role === 'button' || role === 'tab' ? undefined : selected,
41
41
  className: classNames,
42
+ ...getDataComponentAttribute('SelectableChip'),
42
43
  disabled: disabled,
43
44
  role: role,
44
45
  title: accessibilityLabel,
@@ -22,7 +22,7 @@ import BpkSelectableChip, { BpkDismissibleChip, BpkIconChip, BpkDropdownChip, CH
22
22
  import BpkMobileScrollContainer from "../../bpk-component-mobile-scroll-container";
23
23
  import BpkText, { TEXT_STYLES } from "../../bpk-component-text/src/BpkText";
24
24
  import BpkVisuallyHidden from "../../bpk-component-visually-hidden";
25
- import { cssModules } from "../../bpk-react-utils";
25
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
26
26
  import BpkStickyChip from "./BpkStickyChip";
27
27
  import Nudger, { POSITION } from "./Nudger";
28
28
  import STYLES from "./BpkChipGroup.module.css";
@@ -164,6 +164,7 @@ const WrapChipGroup = ({
164
164
  });
165
165
  const BpkMultiSelectChipGroup = props => /*#__PURE__*/_jsx("div", {
166
166
  className: getClassName('bpk-chip-group-container'),
167
+ ...getDataComponentAttribute('MultiSelectChipGroup'),
167
168
  children: props.type === CHIP_GROUP_TYPES.rail ? /*#__PURE__*/_jsx(RailChipGroup, {
168
169
  ...props
169
170
  }) : /*#__PURE__*/_jsx(WrapChipGroup, {
@@ -25,7 +25,7 @@ import { useEffect, useState } from 'react';
25
25
  import BpkBreakpoint, { BREAKPOINTS } from "../../bpk-component-breakpoint";
26
26
  import BpkSelectableChip from "../../bpk-component-chip";
27
27
  import FilterIconSm from "../../bpk-component-icon/sm/filter";
28
- import { cssModules } from "../../bpk-react-utils";
28
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
29
29
  import STYLES from "./BpkStickyChip.module.css";
30
30
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
31
31
  const getClassName = cssModules(STYLES);
@@ -52,6 +52,7 @@ const BpkStickyChip = ({
52
52
  const containerClassNames = getClassName('bpk-sticky-chip-container', `bpk-sticky-chip-container--${chipStyle}`);
53
53
  return /*#__PURE__*/_jsx("div", {
54
54
  className: containerClassNames,
55
+ ...getDataComponentAttribute('StickyChip'),
55
56
  children: /*#__PURE__*/_jsx(BpkBreakpoint, {
56
57
  query: BREAKPOINTS.ABOVE_TABLET,
57
58
  children: isDesktop => {
@@ -15,4 +15,4 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- .bpk-content-card--link{color:inherit;font:inherit;text-decoration:inherit}.bpk-content-card--horizontal{display:grid;column-gap:2.5rem;grid-template-columns:minmax(10%, 38.75rem) 40%;align-items:center}@media(max-width: 32rem){.bpk-content-card--horizontal{grid-template-columns:1fr}}.bpk-content-card--horizontal .bpk-content-card--image-container{width:100%;height:26.25rem}@media(max-width: 32rem){.bpk-content-card--horizontal .bpk-content-card--image-container{max-height:11.25rem;margin-bottom:1rem}}@media(max-width: 64rem){.bpk-content-card--horizontal .bpk-content-card--image-container{height:100%}}.bpk-content-card--vertical .bpk-content-card--image{height:28.75rem;margin-bottom:1rem}@media(max-width: 48rem){.bpk-content-card--vertical .bpk-content-card--image{width:100%;height:auto;max-height:16.25rem}}@media(max-width: 32rem){.bpk-content-card--vertical .bpk-content-card--image{max-height:11.25rem}}.bpk-content-card--headline{margin-bottom:.25rem;font-size:1.25rem;line-height:1.5rem;font-weight:700}.bpk-content-card--description{color:#626971;font-size:1rem;line-height:1.5rem;font-weight:400}.bpk-content-card--image{width:100%;height:100%;border-radius:.75rem;object-fit:cover}
18
+ .bpk-content-card--link{position:relative;display:block;border-radius:.75rem;color:inherit;font:inherit;text-decoration:inherit}.bpk-content-card--link:focus{outline:.125rem solid #0062e3;outline-offset:.125rem}.bpk-content-card--horizontal{display:grid;column-gap:2.5rem;grid-template-columns:minmax(10%, 38.75rem) 40%;align-items:center}@media(max-width: 32rem){.bpk-content-card--horizontal{grid-template-columns:1fr}}.bpk-content-card--horizontal .bpk-content-card--image-container{width:100%;height:26.25rem}@media(max-width: 32rem){.bpk-content-card--horizontal .bpk-content-card--image-container{max-height:11.25rem;margin-bottom:1rem}}@media(max-width: 64rem){.bpk-content-card--horizontal .bpk-content-card--image-container{height:100%}}.bpk-content-card--vertical .bpk-content-card--image{height:28.75rem;margin-bottom:1rem}@media(max-width: 48rem){.bpk-content-card--vertical .bpk-content-card--image{width:100%;height:auto;max-height:16.25rem}}@media(max-width: 32rem){.bpk-content-card--vertical .bpk-content-card--image{max-height:11.25rem}}.bpk-content-card--headline{margin-bottom:.25rem;font-size:1.25rem;line-height:1.5rem;font-weight:700}.bpk-content-card--description{color:#626971;font-size:1rem;line-height:1.5rem;font-weight:400}.bpk-content-card--image{width:100%;height:100%;border-radius:.75rem;object-fit:cover}
@@ -23,10 +23,9 @@ type EventHandlers = {
23
23
  onTouchEnd?: (event: UIEvent) => void;
24
24
  onKeyDown?: (event: UIEvent) => void;
25
25
  onKeyUp?: (event: UIEvent) => void;
26
- readOnly?: string;
27
- 'aria-readonly'?: boolean;
26
+ inputMode?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
28
27
  };
29
- type InputProps = ComponentProps<'input'> & Omit<EventHandlers, 'readOnly' | 'aria-readonly'>;
28
+ type InputProps = ComponentProps<'input'> & Omit<EventHandlers, 'inputMode'>;
30
29
  declare const withOpenEvents: <P extends object>(WithOpenEventsInputComponent: ComponentType<P>) => {
31
30
  new (props: P & WithOpenEventsProps & InputProps): {
32
31
  focusCanOpen: boolean;
@@ -35,25 +34,25 @@ declare const withOpenEvents: <P extends object>(WithOpenEventsInputComponent: C
35
34
  handleBlur: () => void;
36
35
  render(): ReactElement;
37
36
  context: unknown;
38
- setState<K extends never>(state: {} | ((prevState: Readonly<{}>, props: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>) => {} | Pick<{}, K> | null) | Pick<{}, K> | null, callback?: (() => void) | undefined): void;
37
+ setState<K extends never>(state: {} | ((prevState: Readonly<{}>, props: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>) => {} | Pick<{}, K> | null) | Pick<{}, K> | null, callback?: (() => void) | undefined): void;
39
38
  forceUpdate(callback?: (() => void) | undefined): void;
40
- readonly props: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>;
39
+ readonly props: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>;
41
40
  state: Readonly<{}>;
42
41
  refs: {
43
42
  [key: string]: import("react").ReactInstance;
44
43
  };
45
44
  componentDidMount?(): void;
46
- shouldComponentUpdate?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>, nextState: Readonly<{}>, nextContext: any): boolean;
45
+ shouldComponentUpdate?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>, nextState: Readonly<{}>, nextContext: any): boolean;
47
46
  componentWillUnmount?(): void;
48
47
  componentDidCatch?(error: Error, errorInfo: import("react").ErrorInfo): void;
49
- getSnapshotBeforeUpdate?(prevProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>, prevState: Readonly<{}>): any;
50
- componentDidUpdate?(prevProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>, prevState: Readonly<{}>, snapshot?: any): void;
48
+ getSnapshotBeforeUpdate?(prevProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>, prevState: Readonly<{}>): any;
49
+ componentDidUpdate?(prevProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>, prevState: Readonly<{}>, snapshot?: any): void;
51
50
  componentWillMount?(): void;
52
51
  UNSAFE_componentWillMount?(): void;
53
- componentWillReceiveProps?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>, nextContext: any): void;
54
- UNSAFE_componentWillReceiveProps?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>, nextContext: any): void;
55
- componentWillUpdate?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>, nextState: Readonly<{}>, nextContext: any): void;
56
- UNSAFE_componentWillUpdate?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "aria-readonly" | "readOnly"> & WithOpenEventsProps>, nextState: Readonly<{}>, nextContext: any): void;
52
+ componentWillReceiveProps?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>, nextContext: any): void;
53
+ UNSAFE_componentWillReceiveProps?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>, nextContext: any): void;
54
+ componentWillUpdate?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>, nextState: Readonly<{}>, nextContext: any): void;
55
+ UNSAFE_componentWillUpdate?(nextProps: Readonly<P & import("react").ClassAttributes<HTMLInputElement> & import("react").InputHTMLAttributes<HTMLInputElement> & Omit<EventHandlers, "inputMode"> & WithOpenEventsProps>, nextState: Readonly<{}>, nextContext: any): void;
57
56
  };
58
57
  displayName: string;
59
58
  defaultProps: {
@@ -105,9 +105,9 @@ const withOpenEvents = WithOpenEventsInputComponent => {
105
105
  onKeyDown: withEventHandler(handleKeyEvent(onOpen), onKeyDown)
106
106
  };
107
107
  if (hasTouchSupport) {
108
- // Prevents the mobile keyboard from opening (iOS / Android), while not announcing it as 'read only' to a screen reader
109
- eventHandlers.readOnly = 'readOnly';
110
- eventHandlers['aria-readonly'] = false;
108
+ // Prevents the mobile keyboard from opening (iOS / Android) using inputMode="none"
109
+ // This avoids WCAG 1.3.1 violation from conflicting readonly + aria-readonly attributes
110
+ eventHandlers.inputMode = 'none';
111
111
  eventHandlers.onTouchEnd = withEventHandler(this.handleTouchEnd, onTouchEnd);
112
112
  }
113
113
 
@@ -17,6 +17,8 @@
17
17
  */
18
18
 
19
19
  import PropTypes from 'prop-types';
20
+ import { getDataComponentAttribute } from "../../bpk-react-utils";
21
+
20
22
  // @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`.
21
23
  import BpkOverlayView from "./BpkOverlayView";
22
24
  // @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`.
@@ -35,6 +37,7 @@ const BpkBasicMapMarker = props => {
35
37
  return /*#__PURE__*/_jsx(BpkOverlayView, {
36
38
  position: position,
37
39
  getPixelPositionOffset: getPixelPositionOffset,
40
+ ...getDataComponentAttribute('BasicMapMarker'),
38
41
  ...rest,
39
42
  children: children
40
43
  });
@@ -16,6 +16,7 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
+ import { getDataComponentAttribute } from "../../bpk-react-utils";
19
20
  import BpkBasicMapMarker from "./BpkBasicMapMarker";
20
21
  import BpkPriceMarkerButton, { MARKER_STATUSES } from "./BpkPriceMarkerButton";
21
22
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -42,6 +43,7 @@ const BpkPriceMarker = props => {
42
43
  return /*#__PURE__*/_jsx(BpkBasicMapMarker, {
43
44
  position: position,
44
45
  "aria-label": accessibilityLabel,
46
+ ...getDataComponentAttribute('PriceMarker'),
45
47
  ...rest,
46
48
  children: /*#__PURE__*/_jsx(BpkPriceMarkerButton, {
47
49
  ...allButtonProps
@@ -110,7 +110,7 @@ const BpkNudger = ({
110
110
  inputRef.current && setNativeValue(inputRef.current, newValue);
111
111
  },
112
112
  disabled: minButtonDisabled,
113
- title: decreaseButtonLabel,
113
+ "aria-label": decreaseButtonLabel,
114
114
  "aria-controls": id,
115
115
  children: /*#__PURE__*/_jsx(AlignedMinusIcon, {})
116
116
  }), /*#__PURE__*/_jsx("input", {
@@ -151,7 +151,7 @@ const BpkNudger = ({
151
151
  inputRef.current && setNativeValue(inputRef.current, newValue);
152
152
  },
153
153
  disabled: maxButtonDisabled,
154
- title: increaseButtonLabel,
154
+ "aria-label": increaseButtonLabel,
155
155
  "aria-controls": id,
156
156
  children: /*#__PURE__*/_jsx(AlignedPlusIcon, {})
157
157
  })]
@@ -1,2 +1,4 @@
1
1
  import BpkPanel from './src/BpkPanel';
2
+ export type { PanelBgColor } from './src/BpkPanel';
3
+ export { PANEL_BG_COLORS } from './src/BpkPanel';
2
4
  export default BpkPanel;
@@ -17,4 +17,5 @@
17
17
  */
18
18
 
19
19
  import BpkPanel from "./src/BpkPanel";
20
+ export { PANEL_BG_COLORS } from "./src/BpkPanel";
20
21
  export default BpkPanel;
@@ -1,11 +1,24 @@
1
1
  import type { ReactNode } from 'react';
2
+ import type { SurfaceBgColor } from '../../bpk-react-utils';
3
+ export declare const PANEL_BG_COLORS: {
4
+ readonly surfaceDefault: "surface-default";
5
+ readonly surfaceElevated: "surface-elevated";
6
+ readonly surfaceHero: "surface-hero";
7
+ readonly surfaceContrast: "surface-contrast";
8
+ readonly surfaceHighlight: "surface-highlight";
9
+ readonly surfaceSubtle: "surface-subtle";
10
+ readonly surfaceLowContrast: "surface-low-contrast";
11
+ readonly surfaceTint: "surface-tint";
12
+ };
13
+ export type PanelBgColor = SurfaceBgColor;
2
14
  export type Props = {
3
15
  children: ReactNode;
16
+ bgColor?: PanelBgColor;
4
17
  padded?: boolean;
5
18
  fullWidth?: boolean;
6
19
  className?: string | null;
7
20
  keyline?: boolean;
8
21
  [rest: string]: any;
9
22
  };
10
- declare const BpkPanel: ({ children, className, fullWidth, keyline, padded, ...rest }: Props) => import("react/jsx-runtime").JSX.Element;
23
+ declare const BpkPanel: ({ bgColor, children, className, fullWidth, keyline, padded, ...rest }: Props) => import("react/jsx-runtime").JSX.Element;
11
24
  export default BpkPanel;
@@ -16,11 +16,13 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
19
+ import { cssModules, getDataComponentAttribute, SURFACE_COLORS } from "../../bpk-react-utils";
20
20
  import STYLES from "./BpkPanel.module.css";
21
21
  import { jsx as _jsx } from "react/jsx-runtime";
22
22
  const getClassName = cssModules(STYLES);
23
+ export const PANEL_BG_COLORS = SURFACE_COLORS;
23
24
  const BpkPanel = ({
25
+ bgColor = PANEL_BG_COLORS.surfaceDefault,
24
26
  children,
25
27
  className = null,
26
28
  fullWidth = false,
@@ -42,6 +44,7 @@ const BpkPanel = ({
42
44
  classNames.push(getClassName('bpk-panel--keyline'));
43
45
  }
44
46
  }
47
+ classNames.push(getClassName(`bpk-panel--${bgColor}`));
45
48
  if (className) {
46
49
  classNames.push(className);
47
50
  }
@@ -15,4 +15,4 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- .bpk-panel{display:block;background-color:#fff;border-radius:.75rem}.bpk-panel--padded{padding:1rem}.bpk-panel--full-width{border-radius:0}.bpk-panel--keyline{box-shadow:0 0 0 1px #c1c7cf inset}.bpk-panel--full-width-keyline{box-shadow:#c1c7cf 0 -0.0625rem 0 0 inset,#c1c7cf 0 .0625rem 0 0 inset}
18
+ .bpk-panel{display:block;background-color:#fff;border-radius:.75rem}.bpk-panel--surface-default{background-color:#fff}.bpk-panel--surface-elevated{background-color:#fff}.bpk-panel--surface-hero{background-color:#0062e3}.bpk-panel--surface-contrast{background-color:#05203c}.bpk-panel--surface-highlight{background-color:#e0e4e9}.bpk-panel--surface-subtle{background-color:#e3f0ff}.bpk-panel--surface-low-contrast{background-color:#f5f7fa}.bpk-panel--surface-tint{background-color:hsla(0,0%,100%,.1)}.bpk-panel--padded{padding:1rem}.bpk-panel--full-width{border-radius:0}.bpk-panel--keyline{box-shadow:0 0 0 1px #c1c7cf inset}.bpk-panel--full-width-keyline{box-shadow:#c1c7cf 0 -0.0625rem 0 0 inset,#c1c7cf 0 .0625rem 0 0 inset}
@@ -18,7 +18,7 @@
18
18
 
19
19
  import { forwardRef } from 'react';
20
20
  import BpkText, { TEXT_STYLES } from "../../bpk-component-text/src/BpkText";
21
- import { cssModules } from "../../bpk-react-utils";
21
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
22
22
  import STYLES from "./BpkPriceMarker.module.css";
23
23
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
24
24
  const getClassName = cssModules(STYLES);
@@ -28,6 +28,7 @@ const BpkPriceMarker = ({
28
28
  }, ref) => /*#__PURE__*/_jsxs("div", {
29
29
  className: getClassName('bpk-price-marker', `bpk-price-marker--${type}`),
30
30
  ref: ref,
31
+ ...getDataComponentAttribute('PriceMarker'),
31
32
  children: [/*#__PURE__*/_jsx(BpkText, {
32
33
  textStyle: TEXT_STYLES.label2,
33
34
  children: priceLabel
@@ -19,7 +19,7 @@
19
19
  import { useEffect, useRef, useState } from 'react';
20
20
  import clamp from 'lodash/clamp';
21
21
  import BpkText, { TEXT_STYLES } from "../../bpk-component-text/src/BpkText";
22
- import { cssModules } from "../../bpk-react-utils";
22
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
23
23
  import BpkPriceMarker from "./BpkPriceMarker";
24
24
  import { MARKER_DISPLAY_TYPES, MARKER_TYPES } from "./common-types";
25
25
  import STYLES from "./BpkPriceRange.module.css";
@@ -102,6 +102,7 @@ const BpkPriceRange = ({
102
102
  },
103
103
  className: getClassName('bpk-price-range', shouldShowPriceOnBoundaries && 'bpk-price-range--large'),
104
104
  ref: linesRef,
105
+ ...getDataComponentAttribute('PriceRange'),
105
106
  children: [shouldShowBubble && /*#__PURE__*/_jsx("div", {
106
107
  className: getClassName('bpk-price-range__marker'),
107
108
  children: /*#__PURE__*/_jsx(BpkPriceMarker, {
@@ -18,7 +18,7 @@
18
18
 
19
19
  import { BpkCalendarGrid } from "../../bpk-component-calendar";
20
20
  import BpkText, { TEXT_STYLES } from "../../bpk-component-text";
21
- import { cssModules } from "../../bpk-react-utils";
21
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
22
22
  import STYLES from "./BpkScrollableCalendarGrid.module.css";
23
23
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
24
24
  const getClassName = cssModules(STYLES);
@@ -32,6 +32,7 @@ const BpkScrollableCalendarGrid = ({
32
32
  const classNames = getClassName('bpk-scrollable-calendar-grid', className);
33
33
  return /*#__PURE__*/_jsxs("div", {
34
34
  className: classNames,
35
+ ...getDataComponentAttribute('ScrollableCalendarGrid'),
35
36
  children: [/*#__PURE__*/_jsx("span", {
36
37
  className: getClassName('bpk-scrollable-calendar-grid__title'),
37
38
  children: /*#__PURE__*/_jsx(BpkText, {
@@ -21,7 +21,7 @@ import { startOfDay, startOfMonth } from 'date-fns';
21
21
  import AutoSizer from 'react-virtualized-auto-sizer';
22
22
  import { VariableSizeList as List } from 'react-window';
23
23
  import { CALENDAR_SELECTION_TYPE, DateUtils } from "../../bpk-component-calendar";
24
- import { cssModules } from "../../bpk-react-utils";
24
+ import { cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
25
25
  import BpkScrollableCalendarGrid from "./BpkScrollableCalendarGrid";
26
26
  import { getMonthsArray, getMonthItemHeights } from "./utils";
27
27
  import STYLES from "./BpkScrollableCalendarGridList.module.css";
@@ -102,6 +102,7 @@ const BpkScrollableCalendarGridList = props => {
102
102
  const selectedDate = focusedDate || date;
103
103
  return /*#__PURE__*/_jsx("div", {
104
104
  className: getClassName('bpk-scrollable-calendar-grid-list', className),
105
+ ...getDataComponentAttribute('ScrollableCalendarGridList'),
105
106
  children: /*#__PURE__*/_jsx(AutoSizer, {
106
107
  onResize: onResize,
107
108
  defaultHeight: estimatedMonthItemHeight,
@@ -25,7 +25,7 @@ The actual component that developers create (i.e. the default export from this p
25
25
  import { cloneElement, useRef, useState, useEffect } from 'react';
26
26
  import { arrow, FloatingArrow, FloatingPortal, offset, safePolygon, shift, useFloating, useFocus, useHover, useInteractions, useRole } from '@floating-ui/react';
27
27
  import { surfaceHighlightDay, onePixelRem } from '@skyscanner/bpk-foundations-web/tokens/base.es6';
28
- import { TransitionInitialMount, cssModules } from "../../bpk-react-utils";
28
+ import { TransitionInitialMount, cssModules, getDataComponentAttribute } from "../../bpk-react-utils";
29
29
  import { ARROW_ID, TOOLTIP_TYPES } from "./constants";
30
30
  import STYLES from "./BpkTooltip.module.css";
31
31
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -118,6 +118,7 @@ const BpkTooltip = ({
118
118
  tabIndex: -1,
119
119
  role: "dialog",
120
120
  className: classNames,
121
+ ...getDataComponentAttribute('Tooltip'),
121
122
  ...rest,
122
123
  children: [/*#__PURE__*/_jsx(FloatingArrow, {
123
124
  ref: arrowRef,
@@ -32,6 +32,7 @@
32
32
  @forward 'scroll-indicators';
33
33
  @forward 'shadows';
34
34
  @forward 'spinners';
35
+ @forward 'surfaces';
35
36
  @forward 'typography';
36
37
  @forward 'utils';
37
38