@transferwise/components 0.0.0-experimental-0400704 → 0.0.0-experimental-c09cb11

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 (121) hide show
  1. package/build/dateLookup/dateTrigger/DateTrigger.js +8 -4
  2. package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
  3. package/build/dateLookup/dateTrigger/DateTrigger.mjs +8 -4
  4. package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
  5. package/build/field/Field.js +34 -7
  6. package/build/field/Field.js.map +1 -1
  7. package/build/field/Field.mjs +34 -7
  8. package/build/field/Field.mjs.map +1 -1
  9. package/build/i18n/en.json +1 -0
  10. package/build/i18n/en.json.js +1 -0
  11. package/build/i18n/en.json.js.map +1 -1
  12. package/build/i18n/en.json.mjs +1 -0
  13. package/build/i18n/en.json.mjs.map +1 -1
  14. package/build/index.js +2 -2
  15. package/build/index.mjs +1 -1
  16. package/build/inlineAlert/InlineAlert.js +13 -6
  17. package/build/inlineAlert/InlineAlert.js.map +1 -1
  18. package/build/inlineAlert/InlineAlert.mjs +13 -6
  19. package/build/inlineAlert/InlineAlert.mjs.map +1 -1
  20. package/build/label/Label.js +49 -5
  21. package/build/label/Label.js.map +1 -1
  22. package/build/label/Label.messages.js +12 -0
  23. package/build/label/Label.messages.js.map +1 -0
  24. package/build/label/Label.messages.mjs +10 -0
  25. package/build/label/Label.messages.mjs.map +1 -0
  26. package/build/label/Label.mjs +50 -6
  27. package/build/label/Label.mjs.map +1 -1
  28. package/build/main.css +4 -8
  29. package/build/styles/dateLookup/dateTrigger/DateTrigger.css +0 -8
  30. package/build/styles/field/Field.css +4 -0
  31. package/build/styles/main.css +4 -8
  32. package/build/tabs/Tab.js +38 -13
  33. package/build/tabs/Tab.js.map +1 -1
  34. package/build/tabs/Tab.mjs +34 -13
  35. package/build/tabs/Tab.mjs.map +1 -1
  36. package/build/tabs/TabList.js +11 -3
  37. package/build/tabs/TabList.js.map +1 -1
  38. package/build/tabs/TabList.mjs +7 -3
  39. package/build/tabs/TabList.mjs.map +1 -1
  40. package/build/tabs/TabPanel.js +16 -3
  41. package/build/tabs/TabPanel.js.map +1 -1
  42. package/build/tabs/TabPanel.mjs +12 -3
  43. package/build/tabs/TabPanel.mjs.map +1 -1
  44. package/build/tabs/Tabs.js +48 -24
  45. package/build/tabs/Tabs.js.map +1 -1
  46. package/build/tabs/Tabs.mjs +47 -24
  47. package/build/tabs/Tabs.mjs.map +1 -1
  48. package/build/tabs/utils.js +1 -0
  49. package/build/tabs/utils.js.map +1 -1
  50. package/build/tabs/utils.mjs +1 -0
  51. package/build/tabs/utils.mjs.map +1 -1
  52. package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
  53. package/build/types/field/Field.d.ts +4 -2
  54. package/build/types/field/Field.d.ts.map +1 -1
  55. package/build/types/index.d.ts +1 -2
  56. package/build/types/index.d.ts.map +1 -1
  57. package/build/types/inlineAlert/InlineAlert.d.ts +9 -0
  58. package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
  59. package/build/types/label/Label.d.ts +39 -3
  60. package/build/types/label/Label.d.ts.map +1 -1
  61. package/build/types/label/Label.messages.d.ts +8 -0
  62. package/build/types/label/Label.messages.d.ts.map +1 -0
  63. package/build/types/label/index.d.ts +3 -0
  64. package/build/types/label/index.d.ts.map +1 -0
  65. package/build/types/tabs/Tab.d.ts +1 -12
  66. package/build/types/tabs/Tab.d.ts.map +1 -1
  67. package/build/types/tabs/TabList.d.ts +8 -3
  68. package/build/types/tabs/TabList.d.ts.map +1 -1
  69. package/build/types/tabs/TabPanel.d.ts +14 -6
  70. package/build/types/tabs/TabPanel.d.ts.map +1 -1
  71. package/build/types/tabs/Tabs.d.ts +30 -83
  72. package/build/types/tabs/Tabs.d.ts.map +1 -1
  73. package/build/types/tabs/index.d.ts +1 -2
  74. package/build/types/tabs/index.d.ts.map +1 -1
  75. package/build/types/tabs/utils.d.ts +7 -12
  76. package/build/types/tabs/utils.d.ts.map +1 -1
  77. package/package.json +4 -4
  78. package/src/dateInput/DateInput.tests.story.tsx +6 -41
  79. package/src/dateLookup/DateLookup.rtl.spec.tsx +1 -1
  80. package/src/dateLookup/dateTrigger/DateTrigger.css +0 -8
  81. package/src/dateLookup/dateTrigger/DateTrigger.less +0 -8
  82. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -1
  83. package/src/dateLookup/dateTrigger/DateTrigger.tsx +9 -4
  84. package/src/field/Field.css +4 -0
  85. package/src/field/Field.less +5 -0
  86. package/src/field/Field.spec.tsx +41 -5
  87. package/src/field/Field.story.tsx +105 -7
  88. package/src/field/Field.tsx +33 -9
  89. package/src/i18n/en.json +1 -0
  90. package/src/index.ts +1 -2
  91. package/src/inlineAlert/InlineAlert.story.tsx +7 -72
  92. package/src/inlineAlert/InlineAlert.tsx +14 -3
  93. package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.js +5 -10
  94. package/src/inputs/InputGroup.spec.tsx +1 -1
  95. package/src/inputs/SearchInput.spec.tsx +1 -1
  96. package/src/inputs/SelectInput.spec.tsx +1 -1
  97. package/src/label/Label.messages.tsx +8 -0
  98. package/src/label/Label.spec.tsx +42 -7
  99. package/src/label/Label.story.tsx +30 -25
  100. package/src/label/Label.tsx +66 -8
  101. package/src/label/index.ts +2 -0
  102. package/src/main.css +4 -8
  103. package/src/main.less +1 -0
  104. package/src/moneyInput/MoneyInput.story.tsx +11 -11
  105. package/src/radioGroup/RadioGroup.rtl.spec.tsx +1 -1
  106. package/src/select/Select.rtl.spec.tsx +1 -1
  107. package/src/switch/Switch.spec.tsx +1 -1
  108. package/src/switch/Switch.story.tsx +19 -21
  109. package/src/tabs/Tab.js +71 -0
  110. package/src/tabs/TabList.js +15 -0
  111. package/src/tabs/TabPanel.js +20 -0
  112. package/src/tabs/{Tabs.tsx → Tabs.js} +74 -111
  113. package/src/tabs/{Tabs.story.tsx → Tabs.story.js} +1 -1
  114. package/src/tabs/index.js +1 -0
  115. package/src/tabs/{utils.ts → utils.js} +9 -15
  116. package/src/tabs/{utils.spec.ts → utils.spec.js} +21 -24
  117. package/src/field/Field.tests.story.tsx +0 -33
  118. package/src/tabs/Tab.tsx +0 -72
  119. package/src/tabs/TabList.tsx +0 -11
  120. package/src/tabs/TabPanel.tsx +0 -14
  121. package/src/tabs/index.ts +0 -2
@@ -4,8 +4,7 @@ import { Lock } from '@transferwise/icons';
4
4
  import { useState } from 'react';
5
5
 
6
6
  import MoneyInput, { CurrencyOptionItem } from '.';
7
- import Provider from '../provider/Provider';
8
- import translations from '../i18n';
7
+ import { Field } from '../field/Field';
9
8
 
10
9
  export default {
11
10
  component: MoneyInput,
@@ -17,14 +16,13 @@ export default {
17
16
  const handleOnCurrencyChange = (value: CurrencyOptionItem) => setSelectedCurrency(value);
18
17
 
19
18
  return (
20
- <>
21
- <label htmlFor={args.id}>Editable money input label</label>
19
+ <Field id={args.id} label="Editable money input label" required>
22
20
  <MoneyInput
23
21
  {...args}
24
22
  selectedCurrency={selectedCurrency}
25
23
  onCurrencyChange={handleOnCurrencyChange}
26
24
  />
27
- </>
25
+ </Field>
28
26
  );
29
27
  },
30
28
  args: {
@@ -161,17 +159,19 @@ export const SmallInput: Story = {
161
159
  render: (args) => {
162
160
  return (
163
161
  <>
164
- <label htmlFor={args.id}>Money inputs</label>
165
- <MoneyInput {...args} {...SingleCurrency.args} />
162
+ <Field id={args.id} label="Money inputs" required>
163
+ <MoneyInput {...args} {...SingleCurrency.args} />
164
+ </Field>
166
165
  <br />
167
166
  <MoneyInput {...args} {...MultipleCurrencies.args} />
168
167
  <hr />
169
- <div className="has-error">
170
- <label htmlFor={args.id}>Error states</label>
168
+ <Field id={args.id} label="Error states" sentiment="negative" required>
171
169
  <MoneyInput {...args} {...SingleCurrency.args} />
172
- <br />
170
+ </Field>
171
+ <br />
172
+ <Field sentiment="negative">
173
173
  <MoneyInput {...args} {...MultipleCurrencies.args} />
174
- </div>
174
+ </Field>
175
175
  </>
176
176
  );
177
177
  },
@@ -1,4 +1,4 @@
1
- import { render, screen } from '@testing-library/react';
1
+ import { render, screen } from '../test-utils';
2
2
 
3
3
  import RadioGroup from '.';
4
4
  import { Field } from '../field/Field';
@@ -12,6 +12,6 @@ describe('Select', () => {
12
12
  <Select options={options} selected={options[0]} onChange={() => {}} />
13
13
  </Field>,
14
14
  );
15
- expect(screen.getByLabelText('Currency')).toHaveTextContent('USD');
15
+ expect(screen.getByLabelText(/Currency/)).toHaveTextContent('USD');
16
16
  });
17
17
  });
@@ -89,6 +89,6 @@ describe('Switch', () => {
89
89
  <Switch checked onClick={props.onClick} />
90
90
  </Field>,
91
91
  );
92
- expect(screen.getByLabelText('Dark mode')).toHaveAttribute('role', 'switch');
92
+ expect(screen.getByLabelText(/Dark mode/)).toHaveAttribute('role', 'switch');
93
93
  });
94
94
  });
@@ -1,6 +1,7 @@
1
1
  import { useState } from 'react';
2
2
 
3
3
  import Switch from './Switch';
4
+ import { Field } from '../field/Field';
4
5
 
5
6
  export default {
6
7
  component: Switch,
@@ -13,16 +14,14 @@ export const Basic = () => {
13
14
 
14
15
  return (
15
16
  <div className="d-flex flex-column">
16
- <label id="labelID" htmlFor="switchId">
17
- A switch with a label
18
- </label>
19
- <Switch
20
- checked={checked1}
21
- className="a-class-name"
22
- aria-labelledby="labelID"
23
- id="switchId"
24
- onClick={() => setCheck1(!checked1)}
25
- />
17
+ <Field id="switchId" label="A switch with a label" required>
18
+ <Switch
19
+ checked={checked1}
20
+ className="a-class-name"
21
+ id="switchId"
22
+ onClick={() => setCheck1(!checked1)}
23
+ />
24
+ </Field>
26
25
 
27
26
  <Switch
28
27
  checked={checked2}
@@ -39,17 +38,16 @@ export const Disabled = () => {
39
38
 
40
39
  return (
41
40
  <div className="d-flex flex-column">
42
- <label id="labelID" htmlFor="switchId">
43
- A switch with a label
44
- </label>
45
- <Switch
46
- checked={checked}
47
- disabled
48
- className="a-class-name"
49
- aria-labelledby="labelID"
50
- id="switchId"
51
- onClick={() => setCheck(!checked)}
52
- />
41
+ <Field id="switchId" label="A switch with a label" required>
42
+ <Switch
43
+ checked={checked}
44
+ disabled
45
+ className="a-class-name"
46
+ aria-labelledby="labelID"
47
+ id="switchId"
48
+ onClick={() => setCheck(!checked)}
49
+ />
50
+ </Field>
53
51
 
54
52
  <Switch
55
53
  checked={!checked}
@@ -0,0 +1,71 @@
1
+ import { clsx } from 'clsx';
2
+ import PropTypes from 'prop-types';
3
+ import { forwardRef, useEffect, useRef } from 'react';
4
+
5
+ const Tab = forwardRef(
6
+ (
7
+ { children, id, disabled, panelId, selected, onKeyDown, onClick, style, focusTab },
8
+ reference,
9
+ ) => {
10
+ const firstUpdate = useRef(true);
11
+
12
+ const checkFocus = () => {
13
+ if (firstUpdate.current) {
14
+ firstUpdate.current = false;
15
+ return;
16
+ }
17
+
18
+ if (selected && focusTab) {
19
+ focusTab();
20
+ }
21
+ };
22
+
23
+ useEffect(() => {
24
+ checkFocus();
25
+ }, [selected]);
26
+
27
+ return (
28
+ <li
29
+ ref={reference}
30
+ className={clsx('tabs__tab', selected ? 'np-text-body-large-bold' : 'np-text-body-large', {
31
+ 'tabs__tab--selected': selected,
32
+ clickable: !disabled,
33
+ disabled,
34
+ })}
35
+ role="tab"
36
+ id={id}
37
+ aria-selected={selected ? 'true' : 'false'}
38
+ aria-disabled={disabled ? 'true' : 'false'}
39
+ aria-controls={disabled ? null : panelId}
40
+ tabIndex="0"
41
+ style={style}
42
+ onKeyDown={disabled ? null : onKeyDown}
43
+ onClick={onClick}
44
+ >
45
+ <span className="tabs__tab__content">{children}</span>
46
+ </li>
47
+ );
48
+ },
49
+ );
50
+
51
+ Tab.defaultProps = {
52
+ disabled: false,
53
+ selected: false,
54
+ onClick: null,
55
+ style: null,
56
+ focusTab: null,
57
+ };
58
+
59
+ Tab.propTypes = {
60
+ children: PropTypes.node.isRequired,
61
+ disabled: PropTypes.bool,
62
+ selected: PropTypes.bool,
63
+ id: PropTypes.string.isRequired,
64
+ panelId: PropTypes.string.isRequired,
65
+ style: PropTypes.shape({ width: PropTypes.string }),
66
+ focusTab: PropTypes.func,
67
+ onKeyDown: PropTypes.func.isRequired,
68
+ onClick: PropTypes.func,
69
+ };
70
+
71
+ export default Tab;
@@ -0,0 +1,15 @@
1
+ import PropTypes from 'prop-types';
2
+
3
+ const TabList = ({ children }) => {
4
+ return (
5
+ <ul className="tabs__tab-list" role="tablist">
6
+ {children}
7
+ </ul>
8
+ );
9
+ };
10
+
11
+ TabList.propTypes = {
12
+ children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
13
+ };
14
+
15
+ export default TabList;
@@ -0,0 +1,20 @@
1
+ import PropTypes from 'prop-types';
2
+
3
+ const TabPanel = ({ children, id, tabId, style }) => {
4
+ return (
5
+ <div className="tabs__panel" role="tabpanel" id={id} aria-labelledby={tabId} style={style}>
6
+ {children}
7
+ </div>
8
+ );
9
+ };
10
+
11
+ TabPanel.propTypes = {
12
+ children: PropTypes.node.isRequired,
13
+ id: PropTypes.string.isRequired,
14
+ tabId: PropTypes.string.isRequired,
15
+ style: PropTypes.shape({
16
+ width: PropTypes.string.isRequired,
17
+ }).isRequired,
18
+ };
19
+
20
+ export default TabPanel;
@@ -1,6 +1,8 @@
1
- import { Spring, animated } from '@react-spring/web';
1
+ /* eslint-disable react/forbid-dom-props */
2
+ import { Spring, SpringValue, animated } from '@react-spring/web';
2
3
  import { clsx } from 'clsx';
3
4
  import clamp from 'lodash.clamp';
5
+ import PropTypes from 'prop-types';
4
6
  import { Component, createRef, Fragment } from 'react';
5
7
 
6
8
  import { Size, Width, Direction } from '../common';
@@ -16,69 +18,19 @@ import {
16
18
  swipedRightToLeft,
17
19
  swipeShouldChangeTab,
18
20
  getVelocity,
19
- Swipe,
20
21
  } from './utils';
21
22
 
22
23
  const MIN_INDEX = 0;
23
24
 
24
- export interface TabItem {
25
- title: string;
26
- content: React.ReactNode;
27
- disabled: boolean;
28
- }
29
-
30
- type TabsTransitionSpacing =
31
- | 'default'
32
- | `${Size.EXTRA_SMALL | Size.SMALL | Size.MEDIUM | Size.LARGE}`;
33
-
34
- export interface TabsProps {
35
- tabs: TabItem[];
36
- selected: number;
37
- name: string;
38
- animatePanelsOnClick?: boolean;
39
- changeTabOnSwipe?: boolean;
40
- className?: string;
41
- transitionSpacing?: TabsTransitionSpacing;
42
- headerWidth?: `${Width}`;
43
- id?: string;
44
- onTabSelect: (index: number) => void;
45
- }
46
-
47
- interface TabsState {
48
- start: Swipe | null;
49
- translateX: number;
50
- translateFrom: number;
51
- translateTo: number;
52
- translateLineX: string | null;
53
- isAnimating: boolean;
54
- isSwiping: boolean;
55
- isScrolling: boolean;
56
- lastSwipeVelocity: number;
57
- fullWidthTabs: boolean;
58
- currentSwipe: Swipe[];
59
- selectedTabIndex: number;
60
- }
61
-
62
- const SpacerWidth = {
63
- default: 0,
64
- xs: 8,
65
- sm: 16,
66
- md: 24,
67
- lg: 32,
68
- } satisfies Record<TabsTransitionSpacing, number>;
25
+ const enabledTabsFilter = (tab) => !tab.disabled;
69
26
 
70
- export default class Tabs extends Component<TabsProps, TabsState> {
71
- declare props: TabsProps & Required<Pick<TabsProps, keyof typeof Tabs.defaultProps>>;
27
+ const SpacerWidth = { default: 0, xs: 8, sm: 16, md: 24, lg: 32 };
72
28
 
73
- static defaultProps = {
74
- changeTabOnSwipe: true,
75
- transitionSpacing: 'default',
76
- headerWidth: Width.BLOCK,
77
- } satisfies Partial<TabsProps>;
29
+ class Tabs extends Component {
30
+ /** @type {React.RefObject<HTMLDivElement | null>} */
31
+ containerReference = createRef();
78
32
 
79
- containerReference = createRef<HTMLDivElement>();
80
-
81
- constructor(props: Tabs['props']) {
33
+ constructor(props) {
82
34
  super(props);
83
35
  this.state = {
84
36
  start: null,
@@ -91,19 +43,17 @@ export default class Tabs extends Component<TabsProps, TabsState> {
91
43
  isScrolling: false,
92
44
  lastSwipeVelocity: 0,
93
45
  fullWidthTabs: props.headerWidth === Width.BLOCK,
94
- currentSwipe: [],
95
- selectedTabIndex: props.selected,
96
46
  };
97
47
  }
98
48
 
99
- container: HTMLDivElement | null = null;
49
+ container = null;
100
50
 
101
51
  containerWidth = 0;
102
52
 
103
- tabRefs: (HTMLLIElement | null)[] = [];
53
+ tabRefs = [];
104
54
 
105
55
  get filteredTabsLength() {
106
- return this.props.tabs.filter((tab) => !tab.disabled).length;
56
+ return this.props.tabs.filter(enabledTabsFilter).length;
107
57
  }
108
58
 
109
59
  get MAX_INDEX() {
@@ -121,17 +71,17 @@ export default class Tabs extends Component<TabsProps, TabsState> {
121
71
  window.addEventListener('resize', this.handleResize);
122
72
  }
123
73
 
124
- componentDidUpdate(previousProps: TabsProps, previousState: TabsState) {
74
+ componentDidUpdate(previousProps, previousState) {
125
75
  const currentSelected = this.props.selected;
126
76
  const previousSelected = previousProps.selected;
127
77
  const currentSelectedTab = this.props.tabs[currentSelected];
128
- const currentSelectedTabIsDisabled = currentSelectedTab?.disabled;
78
+ const currentSelectedTabIsDisabled = currentSelectedTab && currentSelectedTab.disabled;
129
79
  const previousSelectedTab = previousProps.tabs[previousSelected];
130
- const previousSelectedTabIsDisabled = previousSelectedTab?.disabled;
80
+ const previousSelectedTabIsDisabled = previousSelectedTab && previousSelectedTab.disabled;
131
81
  const currentTabsLength = this.props.tabs.length;
132
82
  const previousTabsLength = previousProps.tabs.length;
133
- const currentDisabledTabsLength = this.props.tabs.filter((tab) => !tab.disabled).length;
134
- const previousDisabledTabsLength = previousProps.tabs.filter((tab) => !tab.disabled).length;
83
+ const currentDisabledTabsLength = this.props.tabs.filter(enabledTabsFilter).length;
84
+ const previousDisabledTabsLength = previousProps.tabs.filter(enabledTabsFilter).length;
135
85
  const currentHeaderWidth = this.props.headerWidth;
136
86
  const previousFullHeaderWidth = previousProps.headerWidth;
137
87
  const { animatePanelsOnClick } = this.props;
@@ -170,12 +120,12 @@ export default class Tabs extends Component<TabsProps, TabsState> {
170
120
  }));
171
121
  };
172
122
 
173
- setContainerRefAndWidth = (node: HTMLDivElement | null) => {
123
+ setContainerRefAndWidth = (node) => {
174
124
  this.container = node;
175
125
  this.setContainerWidth(node);
176
126
  };
177
127
 
178
- setContainerWidth = (node: HTMLDivElement | null) => {
128
+ setContainerWidth = (node) => {
179
129
  if (!node) {
180
130
  return;
181
131
  }
@@ -185,10 +135,10 @@ export default class Tabs extends Component<TabsProps, TabsState> {
185
135
  this.containerWidth = width;
186
136
  };
187
137
 
188
- isTabDisabled = (index: number) => {
138
+ isTabDisabled = (index) => {
189
139
  const { tabs } = this.props;
190
140
 
191
- return tabs[index]?.disabled ?? false;
141
+ return tabs[index] && tabs[index].disabled;
192
142
  };
193
143
 
194
144
  getAllTabsWidth = () => {
@@ -199,7 +149,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
199
149
  .reduce((a, b) => a + b, 0);
200
150
  };
201
151
 
202
- getDistanceToSelectedTab = (selectedTabIndex: number) => {
152
+ getDistanceToSelectedTab = (selectedTabIndex) => {
203
153
  return this.tabRefs
204
154
  .filter((_, idx) => idx < selectedTabIndex)
205
155
  .map((reference) => (reference ? reference.getBoundingClientRect().width : 0))
@@ -240,7 +190,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
240
190
  * Gets the next tab that should be selected based on the swipe direction
241
191
  * and the current selected tab (is called recursively to account for disabled tabs).
242
192
  */
243
- getTabToSelect = (selected: number, start: Swipe, end: Swipe): number => {
193
+ getTabToSelect = (selected, start, end) => {
244
194
  let nextSelected = selected;
245
195
 
246
196
  if (swipedLeftToRight(start, end)) {
@@ -270,19 +220,9 @@ export default class Tabs extends Component<TabsProps, TabsState> {
270
220
  return nextSelected;
271
221
  };
272
222
 
273
- swipedOverHalfOfContainer = (difference: number) => difference / this.containerWidth >= 0.5;
274
-
275
- calculateApplicableDragDifference = ({
276
- currentSelected,
277
- nextSelected,
278
- start,
279
- end,
280
- }: {
281
- currentSelected: number;
282
- nextSelected: number;
283
- start: Swipe;
284
- end: Swipe;
285
- }) => {
223
+ swipedOverHalfOfContainer = (difference) => difference / this.containerWidth >= 0.5;
224
+
225
+ calculateApplicableDragDifference = ({ currentSelected, nextSelected, start, end }) => {
286
226
  const difference = getSwipeDifference(start, end);
287
227
  const elasticDrag = getElasticDragDifference(difference);
288
228
 
@@ -302,22 +242,22 @@ export default class Tabs extends Component<TabsProps, TabsState> {
302
242
  return false;
303
243
  };
304
244
 
305
- switchTab = (index: number) => {
245
+ switchTab = (index) => {
306
246
  const { onTabSelect } = this.props;
307
247
  onTabSelect(index);
308
248
  };
309
249
 
310
- getTabIndexWithoutDisabledTabs(index: number) {
311
- return index - this.props.tabs.slice(0, index).filter((tab) => tab.disabled).length;
250
+ getTabIndexWithoutDisabledTabs(index) {
251
+ return index - this.props.tabs.slice(0, index).filter((tab) => !enabledTabsFilter(tab)).length;
312
252
  }
313
253
 
314
- animateToTab = (index: number, instant?: boolean) => {
254
+ animateToTab = (index, instant) => {
315
255
  this.animateLine(index);
316
256
 
317
257
  this.animatePanel(this.getTabIndexWithoutDisabledTabs(index), instant);
318
258
  };
319
259
 
320
- animateLine = (index: number) => {
260
+ animateLine = (index) => {
321
261
  this.setState((previousState) => ({
322
262
  translateLineX: previousState.fullWidthTabs
323
263
  ? `${index * 100}%`
@@ -326,7 +266,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
326
266
  };
327
267
 
328
268
  // Pass `instant` to set the `translateX` to the new panel with no transition
329
- animatePanel = (index: number, instant = false) => {
269
+ animatePanel = (index, instant = false) => {
330
270
  const { translateTo: currentTranslateTo } = this.state;
331
271
 
332
272
  const translateFrom = currentTranslateTo;
@@ -340,7 +280,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
340
280
  });
341
281
  };
342
282
 
343
- disableScroll = (event: Event) => {
283
+ disableScroll = (event) => {
344
284
  const { isSwiping } = this.state;
345
285
 
346
286
  if (isSwiping) {
@@ -348,17 +288,17 @@ export default class Tabs extends Component<TabsProps, TabsState> {
348
288
  }
349
289
  };
350
290
 
351
- handleTabClick = (index: number) => () => {
291
+ handleTabClick = (index) => () => {
352
292
  this.switchTab(index);
353
293
  };
354
294
 
355
- onKeyDown = (index: number) => (event: React.KeyboardEvent<HTMLLIElement>) => {
295
+ onKeyDown = (index) => (event) => {
356
296
  if (event && event.key === 'Enter') {
357
297
  this.switchTab(index);
358
298
  }
359
299
  };
360
300
 
361
- handleTouchStart: React.TouchEventHandler<HTMLDivElement> = (event) => {
301
+ handleTouchStart = (event) => {
362
302
  const start = {
363
303
  x: event.nativeEvent.touches[0].clientX,
364
304
  y: event.nativeEvent.touches[0].clientY,
@@ -372,15 +312,11 @@ export default class Tabs extends Component<TabsProps, TabsState> {
372
312
  event.persist();
373
313
  };
374
314
 
375
- handleTouchMove: React.TouchEventHandler<HTMLDivElement> = (event) => {
315
+ handleTouchMove = (event) => {
376
316
  const { start } = this.state;
377
- if (start == null) {
378
- return;
379
- }
380
-
381
317
  const { selected: currentSelectedFromProps } = this.props;
382
318
  const selected = this.getTabIndexWithoutDisabledTabs(currentSelectedFromProps);
383
- const end: Swipe = {
319
+ const end = {
384
320
  x: event.nativeEvent.changedTouches[0].clientX,
385
321
  y: event.nativeEvent.changedTouches[0].clientY,
386
322
  time: Date.now(),
@@ -427,14 +363,10 @@ export default class Tabs extends Component<TabsProps, TabsState> {
427
363
  }
428
364
  };
429
365
 
430
- handleTouchEnd: React.TouchEventHandler<HTMLDivElement> = (event) => {
366
+ handleTouchEnd = (event) => {
431
367
  const { start, isSwiping } = this.state;
432
- if (start == null) {
433
- return;
434
- }
435
-
436
368
  const { selected } = this.props;
437
- const end: Swipe = {
369
+ const end = {
438
370
  x: event.nativeEvent.changedTouches[0].clientX,
439
371
  y: event.nativeEvent.changedTouches[0].clientY,
440
372
  time: Date.now(),
@@ -501,7 +433,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
501
433
  const sliderWidth = tabsLength * this.containerWidth + spacer * 2;
502
434
 
503
435
  // Uses `props.panelTransitionSpacing` to add a spacer in-between the `TabPanel` you're transitioning to/from
504
- const Spacer = ({ id }: { id: string }) =>
436
+ const Spacer = ({ id }) =>
505
437
  spacer > 0 && (
506
438
  <div
507
439
  key={id}
@@ -540,10 +472,10 @@ export default class Tabs extends Component<TabsProps, TabsState> {
540
472
  disabled={disabled}
541
473
  focusTab={() => {
542
474
  if (this.containerReference.current?.contains(document.activeElement)) {
543
- this.tabRefs[index]?.focus();
475
+ this.tabRefs[index].focus();
544
476
  }
545
477
  }}
546
- onClick={disabled ? undefined : this.handleTabClick(index)}
478
+ onClick={disabled ? null : this.handleTabClick(index)}
547
479
  onKeyDown={this.onKeyDown(index)}
548
480
  {...(fullWidthTabs
549
481
  ? { style: { width: `${(1 / tabs.length) * 100}%` } }
@@ -632,3 +564,34 @@ export default class Tabs extends Component<TabsProps, TabsState> {
632
564
  );
633
565
  }
634
566
  }
567
+
568
+ const SpacerSizes = { ...Size, NONE: 'default' };
569
+
570
+ Tabs.propTypes = {
571
+ tabs: PropTypes.arrayOf(
572
+ PropTypes.shape({
573
+ title: PropTypes.string.isRequired,
574
+ content: PropTypes.node.isRequired,
575
+ disabled: PropTypes.bool.isRequired,
576
+ }),
577
+ ).isRequired,
578
+ selected: PropTypes.number.isRequired,
579
+ name: PropTypes.string.isRequired,
580
+ animatePanelsOnClick: PropTypes.bool,
581
+ changeTabOnSwipe: PropTypes.bool,
582
+ className: PropTypes.string,
583
+ transitionSpacing: PropTypes.oneOf(['default', 'xs', 'sm', 'md', 'lg']),
584
+ headerWidth: PropTypes.oneOf(['auto', 'block']),
585
+ id: PropTypes.string,
586
+ onTabSelect: PropTypes.func.isRequired,
587
+ };
588
+
589
+ Tabs.defaultProps = {
590
+ animatePanelsOnClick: false,
591
+ changeTabOnSwipe: true,
592
+ className: '',
593
+ transitionSpacing: SpacerSizes.NONE,
594
+ headerWidth: Width.BLOCK,
595
+ };
596
+
597
+ export default Tabs;
@@ -97,7 +97,7 @@ export const NoPanelAnimation = () => {
97
97
  const [selected, setSelected] = useState(0);
98
98
  return (
99
99
  <Tabs
100
- animatePanelsOnClick={false}
100
+ animatePanels={false}
101
101
  className="tabs-custom-class"
102
102
  name="tabs-docs"
103
103
  transitionSpacing={Size.MEDIUM}
@@ -0,0 +1 @@
1
+ export { default } from './Tabs';
@@ -1,42 +1,36 @@
1
1
  const MIN_SWIPE_DISTANCE = 50;
2
2
  const MIN_SWIPE_VELOCITY = 0.1;
3
3
 
4
- const swipedSignificantDistance = (difference: number) => Math.abs(difference) > MIN_SWIPE_DISTANCE;
4
+ const swipedSignificantDistance = (difference) => Math.abs(difference) > MIN_SWIPE_DISTANCE;
5
5
 
6
- const swipedWithSignificantVelocity = (velocity: number) => velocity > MIN_SWIPE_VELOCITY;
6
+ const swipedWithSignificantVelocity = (velocity) => velocity > MIN_SWIPE_VELOCITY;
7
7
 
8
- export interface Swipe {
9
- x: number;
10
- y: number;
11
- time: number;
12
- }
13
-
14
- export const getSwipeVelocity = (start: Swipe, end: Swipe) => {
8
+ export const getSwipeVelocity = (start, end) => {
15
9
  const timePassed = end.time - start.time;
16
10
 
17
11
  return getSwipeDifference(start, end) / timePassed;
18
12
  };
19
13
 
20
- export const getSwipeDifference = (start: Swipe, end: Swipe, axis: 'x' | 'y' = 'x') => {
14
+ export const getSwipeDifference = (start, end, axis = 'x') => {
21
15
  return Math.abs(start[axis] - end[axis]);
22
16
  };
23
17
 
24
- export const swipedLeftToRight = (start: Swipe, end: Swipe) => {
18
+ export const swipedLeftToRight = (start, end) => {
25
19
  return end.x > start.x;
26
20
  };
27
21
 
28
- export const swipedRightToLeft = (start: Swipe, end: Swipe) => {
22
+ export const swipedRightToLeft = (start, end) => {
29
23
  return start.x > end.x;
30
24
  };
31
25
 
32
- export const swipeShouldChangeTab = (start: Swipe, end: Swipe) => {
26
+ export const swipeShouldChangeTab = (start, end) => {
33
27
  const difference = getSwipeDifference(start, end);
34
28
  const velocity = getSwipeVelocity(start, end);
35
29
 
36
30
  return swipedSignificantDistance(difference) && swipedWithSignificantVelocity(velocity);
37
31
  };
38
32
 
39
- export function getVelocity(coords: Swipe[]) {
33
+ export function getVelocity(coords) {
40
34
  try {
41
35
  const relevant = coords.slice(Math.max(coords.length - 5, 1));
42
36
  const first = relevant[0];
@@ -53,5 +47,5 @@ export function getVelocity(coords: Swipe[]) {
53
47
  `1 - Math.E ** (-0.005 * difference)` provides a % value of how far we want to translate (0.005 being the rate)
54
48
  `Math.min(150, window.innerWidth / 3)` provides the maximum translate value
55
49
  */
56
- export const getElasticDragDifference = (difference: number) =>
50
+ export const getElasticDragDifference = (difference) =>
57
51
  Math.min(150, window.innerWidth / 3) * (1 - Math.E ** (-0.005 * difference));