@transferwise/components 0.0.0-experimental-3868cf1 → 0.0.0-experimental-69a95e1

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 (174) hide show
  1. package/build/avatarLayout/AvatarLayout.js +10 -4
  2. package/build/avatarLayout/AvatarLayout.js.map +1 -1
  3. package/build/avatarLayout/AvatarLayout.mjs +10 -4
  4. package/build/avatarLayout/AvatarLayout.mjs.map +1 -1
  5. package/build/button/Button.js +79 -79
  6. package/build/button/Button.js.map +1 -1
  7. package/build/button/Button.mjs +80 -80
  8. package/build/button/Button.mjs.map +1 -1
  9. package/build/button/Button.resolver.js +86 -0
  10. package/build/button/Button.resolver.js.map +1 -0
  11. package/build/button/Button.resolver.mjs +84 -0
  12. package/build/button/Button.resolver.mjs.map +1 -0
  13. package/build/button/LegacyButton.js +114 -0
  14. package/build/button/LegacyButton.js.map +1 -0
  15. package/build/button/LegacyButton.mjs +112 -0
  16. package/build/button/LegacyButton.mjs.map +1 -0
  17. package/build/criticalBanner/CriticalCommsBanner.js +2 -2
  18. package/build/criticalBanner/CriticalCommsBanner.js.map +1 -1
  19. package/build/criticalBanner/CriticalCommsBanner.mjs +1 -1
  20. package/build/header/Header.js +2 -2
  21. package/build/header/Header.js.map +1 -1
  22. package/build/header/Header.mjs +1 -1
  23. package/build/i18n/ja.json +0 -1
  24. package/build/i18n/ja.json.js +0 -1
  25. package/build/i18n/ja.json.js.map +1 -1
  26. package/build/i18n/ja.json.mjs +0 -1
  27. package/build/i18n/ja.json.mjs.map +1 -1
  28. package/build/i18n/pt.json +0 -1
  29. package/build/i18n/pt.json.js +0 -1
  30. package/build/i18n/pt.json.js.map +1 -1
  31. package/build/i18n/pt.json.mjs +0 -1
  32. package/build/i18n/pt.json.mjs.map +1 -1
  33. package/build/i18n/ru.json +0 -1
  34. package/build/i18n/ru.json.js +0 -1
  35. package/build/i18n/ru.json.js.map +1 -1
  36. package/build/i18n/ru.json.mjs +0 -1
  37. package/build/i18n/ru.json.mjs.map +1 -1
  38. package/build/i18n/zh-HK.json +0 -1
  39. package/build/i18n/zh-HK.json.js +0 -1
  40. package/build/i18n/zh-HK.json.js.map +1 -1
  41. package/build/i18n/zh-HK.json.mjs +0 -1
  42. package/build/i18n/zh-HK.json.mjs.map +1 -1
  43. package/build/index.js +2 -4
  44. package/build/index.js.map +1 -1
  45. package/build/index.mjs +1 -2
  46. package/build/index.mjs.map +1 -1
  47. package/build/link/Link.js +8 -3
  48. package/build/link/Link.js.map +1 -1
  49. package/build/link/Link.mjs +8 -3
  50. package/build/link/Link.mjs.map +1 -1
  51. package/build/main.css +227 -2
  52. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.js +2 -4
  53. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.js.map +1 -1
  54. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.mjs +2 -4
  55. package/build/primitives/PrimitiveAnchor/src/PrimitiveAnchor.mjs.map +1 -1
  56. package/build/primitives/PrimitiveButton/src/PrimitiveButton.js +3 -5
  57. package/build/primitives/PrimitiveButton/src/PrimitiveButton.js.map +1 -1
  58. package/build/primitives/PrimitiveButton/src/PrimitiveButton.mjs +3 -5
  59. package/build/primitives/PrimitiveButton/src/PrimitiveButton.mjs.map +1 -1
  60. package/build/select/Select.js +2 -2
  61. package/build/select/Select.js.map +1 -1
  62. package/build/select/Select.mjs +1 -1
  63. package/build/styles/avatarLayout/AvatarLayout.css +12 -2
  64. package/build/styles/button/Button.css +207 -15
  65. package/build/styles/button/Button.vars.css +39 -0
  66. package/build/styles/button/LegacyButton.css +23 -0
  67. package/build/styles/main.css +227 -2
  68. package/build/types/avatarLayout/AvatarLayout.d.ts +1 -2
  69. package/build/types/avatarLayout/AvatarLayout.d.ts.map +1 -1
  70. package/build/types/avatarLayout/index.d.ts +1 -0
  71. package/build/types/avatarLayout/index.d.ts.map +1 -1
  72. package/build/types/button/Button.d.ts +1 -23
  73. package/build/types/button/Button.d.ts.map +1 -1
  74. package/build/types/button/Button.resolver.d.ts +31 -0
  75. package/build/types/button/Button.resolver.d.ts.map +1 -0
  76. package/build/types/button/Button.types.d.ts +65 -0
  77. package/build/types/button/Button.types.d.ts.map +1 -0
  78. package/build/types/button/LegacyButton.d.ts +30 -0
  79. package/build/types/button/LegacyButton.d.ts.map +1 -0
  80. package/build/types/button/index.d.ts +2 -2
  81. package/build/types/button/index.d.ts.map +1 -1
  82. package/build/types/index.d.ts +0 -2
  83. package/build/types/index.d.ts.map +1 -1
  84. package/build/types/link/Link.d.ts +2 -2
  85. package/build/types/link/Link.d.ts.map +1 -1
  86. package/build/types/primitives/PrimitiveAnchor/src/PrimitiveAnchor.d.ts.map +1 -1
  87. package/build/types/primitives/PrimitiveAnchor/src/PrimitiveAnchor.types.d.ts +5 -1
  88. package/build/types/primitives/PrimitiveAnchor/src/PrimitiveAnchor.types.d.ts.map +1 -1
  89. package/build/types/primitives/PrimitiveButton/src/PrimitiveButton.d.ts.map +1 -1
  90. package/build/types/test-utils/story-config.d.ts +1 -1
  91. package/build/types/test-utils/story-config.d.ts.map +1 -1
  92. package/build/types/uploadInput/uploadButton/getAllowedFileTypes.d.ts.map +1 -1
  93. package/build/upload/steps/completeStep/completeStep.js +2 -2
  94. package/build/upload/steps/completeStep/completeStep.js.map +1 -1
  95. package/build/upload/steps/completeStep/completeStep.mjs +1 -1
  96. package/build/upload/steps/processingStep/processingStep.js +2 -2
  97. package/build/upload/steps/processingStep/processingStep.js.map +1 -1
  98. package/build/upload/steps/processingStep/processingStep.mjs +1 -1
  99. package/build/uploadInput/UploadInput.js +3 -3
  100. package/build/uploadInput/UploadInput.js.map +1 -1
  101. package/build/uploadInput/UploadInput.mjs +1 -1
  102. package/build/uploadInput/uploadButton/getAllowedFileTypes.js +3 -23
  103. package/build/uploadInput/uploadButton/getAllowedFileTypes.js.map +1 -1
  104. package/build/uploadInput/uploadButton/getAllowedFileTypes.mjs +3 -23
  105. package/build/uploadInput/uploadButton/getAllowedFileTypes.mjs.map +1 -1
  106. package/package.json +5 -5
  107. package/src/alert/Alert.tests.story.tsx +1 -1
  108. package/src/avatarLayout/AvatarLayout.css +12 -2
  109. package/src/avatarLayout/AvatarLayout.less +19 -2
  110. package/src/avatarLayout/AvatarLayout.tsx +11 -4
  111. package/src/avatarLayout/index.ts +1 -0
  112. package/src/button/Button.css +207 -15
  113. package/src/button/Button.less +217 -14
  114. package/src/button/Button.resolver.tsx +134 -0
  115. package/src/button/Button.spec.tsx +176 -225
  116. package/src/button/Button.story.tsx +729 -135
  117. package/src/button/Button.tests.story.tsx +27 -0
  118. package/src/button/Button.tsx +89 -132
  119. package/src/button/Button.types.ts +86 -0
  120. package/src/button/Button.vars.css +39 -0
  121. package/src/button/Button.vars.less +49 -0
  122. package/src/button/LegacyButton.css +23 -0
  123. package/src/button/LegacyButton.less +24 -0
  124. package/src/button/LegacyButton.spec.tsx +146 -0
  125. package/src/button/LegacyButton.story.tsx +225 -0
  126. package/src/button/LegacyButton.tsx +161 -0
  127. package/src/button/index.ts +2 -3
  128. package/src/field/Field.story.tsx +1 -1
  129. package/src/flowNavigation/__snapshots__/FlowNavigation.spec.js.snap +1 -2
  130. package/src/i18n/ja.json +0 -1
  131. package/src/i18n/pt.json +0 -1
  132. package/src/i18n/ru.json +0 -1
  133. package/src/i18n/zh-HK.json +0 -1
  134. package/src/index.ts +0 -12
  135. package/src/inputs/SelectInput.story.tsx +1 -1
  136. package/src/label/Label.story.tsx +1 -1
  137. package/src/link/Link.tsx +15 -6
  138. package/src/main.css +227 -2
  139. package/src/main.less +1 -0
  140. package/src/primitives/PrimitiveAnchor/src/PrimitiveAnchor.tsx +2 -8
  141. package/src/primitives/PrimitiveAnchor/src/PrimitiveAnchor.types.ts +6 -1
  142. package/src/primitives/PrimitiveAnchor/test/PrimitiveAnchor.spec.tsx +1 -3
  143. package/src/primitives/PrimitiveButton/src/PrimitiveButton.tsx +4 -12
  144. package/src/primitives/PrimitiveButton/test/PrimitiveButton.spec.tsx +16 -13
  145. package/src/test-utils/Parameters.d.ts +9 -1
  146. package/src/test-utils/story-config.ts +10 -1
  147. package/src/uploadInput/UploadInput.tests.story.tsx +5 -5
  148. package/src/uploadInput/uploadButton/getAllowedFileTypes.spec.ts +0 -12
  149. package/src/uploadInput/uploadButton/getAllowedFileTypes.ts +7 -33
  150. package/build/table/Table.js +0 -166
  151. package/build/table/Table.js.map +0 -1
  152. package/build/table/Table.messages.js +0 -24
  153. package/build/table/Table.messages.js.map +0 -1
  154. package/build/table/Table.messages.mjs +0 -22
  155. package/build/table/Table.messages.mjs.map +0 -1
  156. package/build/table/Table.mjs +0 -164
  157. package/build/table/Table.mjs.map +0 -1
  158. package/build/table/TableCell.js +0 -86
  159. package/build/table/TableCell.js.map +0 -1
  160. package/build/table/TableCell.mjs +0 -84
  161. package/build/table/TableCell.mjs.map +0 -1
  162. package/build/table/TableHeader.js +0 -57
  163. package/build/table/TableHeader.js.map +0 -1
  164. package/build/table/TableHeader.mjs +0 -55
  165. package/build/table/TableHeader.mjs.map +0 -1
  166. package/build/table/TableRow.js +0 -85
  167. package/build/table/TableRow.js.map +0 -1
  168. package/build/table/TableRow.mjs +0 -83
  169. package/build/table/TableRow.mjs.map +0 -1
  170. package/build/table/TableStatusText.js +0 -54
  171. package/build/table/TableStatusText.js.map +0 -1
  172. package/build/table/TableStatusText.mjs +0 -52
  173. package/build/table/TableStatusText.mjs.map +0 -1
  174. package/src/button/__snapshots__/Button.spec.tsx.snap +0 -309
@@ -0,0 +1,27 @@
1
+ import { Meta } from '@storybook/react';
2
+ import Button from './Button.resolver';
3
+ import { expect, userEvent, within } from '@storybook/test';
4
+ import { storyConfig } from '../test-utils';
5
+
6
+ const meta: Meta<typeof Button> = {
7
+ component: Button,
8
+ title: 'Actions/Button/Tests',
9
+ };
10
+ export default meta;
11
+
12
+ export const FocusMinimal = storyConfig(
13
+ {
14
+ render: () => (
15
+ <Button v2 priority="minimal">
16
+ Focus has underline
17
+ </Button>
18
+ ),
19
+ },
20
+ {},
21
+ );
22
+ FocusMinimal.play = async ({ canvasElement }) => {
23
+ await userEvent.tab();
24
+ const canvas = within(canvasElement);
25
+ const buttonElement: HTMLButtonElement = canvas.getByRole('button');
26
+ await expect(buttonElement).toHaveFocus();
27
+ };
@@ -1,154 +1,111 @@
1
- import { clsx } from 'clsx';
2
- import { ElementType, forwardRef, MouseEvent } from 'react';
3
- import { useIntl } from 'react-intl';
4
-
5
- import {
6
- Size,
7
- ControlType,
8
- Priority,
9
- ControlTypeAccent,
10
- ControlTypeNegative,
11
- ControlTypePositive,
12
- PriorityPrimary,
13
- PrioritySecondary,
14
- PriorityTertiary,
15
- SizeExtraSmall,
16
- SizeSmall,
17
- SizeMedium,
18
- SizeLarge,
19
- } from '../common';
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ import { forwardRef } from 'react';
3
+ import { ButtonProps as NewButtonProps } from './Button.types';
4
+ import { PrimitiveAnchor, PrimitiveButton } from '../primitives';
5
+ import AvatarLayout from '../avatarLayout';
20
6
  import ProcessIndicator from '../processIndicator';
7
+ import { clsx } from 'clsx';
8
+ import { Typography } from '../common';
9
+ import Body from '../body';
21
10
 
22
- import messages from '../i18n/commonMessages/Button.messages';
23
- import { typeClassMap, priorityClassMap } from './classMap';
24
- import { establishNewPriority, establishNewType, logDeprecationNotices } from './legacyUtils';
25
-
26
- /** @deprecated */
27
- type DeprecatedTypes = 'primary' | 'pay' | 'secondary' | 'danger' | 'link';
28
-
29
- /** @deprecated */
30
- type DeprecatedSizes = SizeExtraSmall;
31
-
32
- type CommonProps = {
33
- block?: boolean;
34
- disabled?: boolean;
35
- loading?: boolean;
36
- type?: ControlTypeAccent | ControlTypeNegative | ControlTypePositive | DeprecatedTypes | null;
37
- priority?: PriorityPrimary | PrioritySecondary | PriorityTertiary | null;
38
- size?: SizeSmall | SizeMedium | SizeLarge | DeprecatedSizes;
39
- };
40
-
41
- type ButtonProps = CommonProps &
42
- Omit<React.ComponentPropsWithRef<'button'>, 'type'> & {
43
- as?: 'button';
44
- htmlType?: 'submit' | 'reset' | 'button';
45
- };
46
-
47
- type AnchorProps = CommonProps &
48
- Omit<React.ComponentPropsWithRef<'a'>, 'type'> & {
49
- as?: 'a';
50
- };
51
-
52
- export type Props = ButtonProps | AnchorProps;
53
-
54
- export type ButtonReferenceType = HTMLButtonElement | HTMLAnchorElement;
55
-
56
- const Button = forwardRef<ButtonReferenceType, Props>(
11
+ const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, NewButtonProps>(
57
12
  (
58
13
  {
59
- as: component,
60
- block = false,
14
+ as = 'button',
61
15
  children,
62
16
  className,
63
- disabled,
17
+ size = 'lg',
18
+ href,
19
+ disabled = false,
20
+ priority = 'primary',
21
+ sentiment = 'default',
22
+ iconStart: IconStart,
23
+ iconEnd: IconEnd,
24
+ avatars,
25
+ type = 'button',
64
26
  loading = false,
65
- priority = Priority.PRIMARY,
66
- size = Size.MEDIUM,
67
- type = ControlType.ACCENT,
68
- onClick,
69
- ...rest
70
- }: Props,
71
- reference,
27
+ block = false,
28
+ ...props
29
+ },
30
+ ref,
72
31
  ) => {
73
- const intl = useIntl();
74
-
75
- logDeprecationNotices({ size, type });
76
-
77
- const newType = establishNewType(type);
78
- const newPriority = establishNewPriority(priority, type);
79
-
80
- const classes = clsx(
81
- `btn btn-${size}`,
82
- `np-btn np-btn-${size}`,
32
+ const classNames = clsx(
33
+ 'wds-Button',
83
34
  {
84
- 'btn-loading': loading,
85
- 'btn-block np-btn-block': block,
86
- disabled,
35
+ [`wds-Button--block`]: block,
36
+ [`wds-Button--disabled`]: disabled,
37
+ [`wds-Button--loading`]: loading,
87
38
  },
88
- // @ts-expect-error fix when refactor `typeClassMap` to TypeScript
89
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
90
- typeClassMap[newType],
91
- // @ts-expect-error fix when refactor `typeClassMap` to TypeScript
92
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
93
- priorityClassMap[newPriority],
39
+ `wds-Button--${{ sm: 'small', md: 'medium', lg: 'large' }[size]}`,
40
+ `wds-Button--${priority}`,
41
+ `wds-Button--${sentiment}`,
94
42
  className,
95
43
  );
96
44
 
97
- function processIndicatorSize() {
98
- return ['sm', 'xs'].includes(size) ? 'xxs' : 'xs';
99
- }
100
-
101
- const Element = (component as ElementType) ?? 'button';
102
- let props;
103
-
104
- if (Element === 'button') {
105
- const { htmlType = 'button', ...restProps } = rest as ButtonProps;
106
- props = {
107
- ...restProps,
108
- disabled,
109
- 'aria-disabled': loading,
110
- type: htmlType,
111
- };
112
- } else {
113
- props = {
114
- ...rest,
115
- 'aria-disabled': loading,
116
- } as AnchorProps;
117
- }
118
-
119
- /**
120
- * Ensures that the button cannot be activated in loading or disabled mode,
121
- * when `aria-disabled` might be used over the `disabled` HTML attribute
122
- */
123
- const handleClick =
124
- (handler: Props['onClick']) =>
125
- (event: MouseEvent<HTMLButtonElement> & MouseEvent<HTMLAnchorElement>) => {
126
- if (disabled || loading) {
127
- event.preventDefault();
128
- } else if (typeof handler === 'function') {
129
- handler(event);
130
- }
131
- };
45
+ const contentClassNames = clsx('wds-Button-content', {
46
+ [`wds-Button-content--loading`]: loading,
47
+ });
132
48
 
133
- return (
134
- <Element
135
- ref={reference}
136
- className={classes}
137
- onClick={handleClick(onClick)}
138
- {...props}
139
- aria-live={loading ? 'polite' : 'off'}
140
- aria-busy={loading}
141
- aria-label={loading ? intl.formatMessage(messages.loadingAriaLabel) : rest['aria-label']}
49
+ const content = (
50
+ <Body
51
+ as="span"
52
+ type={size === 'sm' ? Typography.BODY_DEFAULT_BOLD : Typography.BODY_LARGE_BOLD}
53
+ className={contentClassNames}
142
54
  >
143
- {children}
144
55
  {loading && (
145
56
  <ProcessIndicator
146
- size={processIndicatorSize()}
147
- className="btn-loader"
148
- data-testid="ButtonProgressIndicator"
57
+ size={size === 'sm' ? 'xxs' : 'xs'}
58
+ className="wds-Button-loader"
59
+ data-testid="button-loader-indicator"
149
60
  />
150
61
  )}
151
- </Element>
62
+ <span className="wds-Button-label" aria-hidden={loading}>
63
+ {size === 'lg' ? (
64
+ children
65
+ ) : (
66
+ <>
67
+ {size === 'md' && avatars && (
68
+ <span className="wds-Button-avatars">
69
+ <AvatarLayout orientation="horizontal" avatars={avatars} size={24} />
70
+ </span>
71
+ )}
72
+ {!avatars && IconStart && (
73
+ <IconStart className="wds-Button-icon wds-Button-icon--start" />
74
+ )}
75
+ {children}
76
+ {IconEnd && <IconEnd className="wds-Button-icon wds-Button-icon--end" />}
77
+ </>
78
+ )}
79
+ </span>
80
+ </Body>
81
+ );
82
+
83
+ if (href || as === 'a') {
84
+ return (
85
+ <PrimitiveAnchor
86
+ ref={ref as React.Ref<HTMLAnchorElement>}
87
+ {...(props as any)}
88
+ href={href}
89
+ className={classNames}
90
+ disabled={disabled || loading}
91
+ aria-busy={loading || undefined}
92
+ >
93
+ {content}
94
+ </PrimitiveAnchor>
95
+ );
96
+ }
97
+
98
+ return (
99
+ <PrimitiveButton
100
+ ref={ref as React.Ref<HTMLButtonElement>}
101
+ {...(props as any)}
102
+ className={classNames}
103
+ disabled={disabled}
104
+ loading={loading}
105
+ type={type}
106
+ >
107
+ {content}
108
+ </PrimitiveButton>
152
109
  );
153
110
  },
154
111
  );
@@ -0,0 +1,86 @@
1
+ import { ReactNode } from 'react';
2
+ import type {
3
+ PrimitiveButtonProps,
4
+ PrimitiveAnchorProps,
5
+ PrimitiveButtonElementRef,
6
+ PrimitiveAnchorElementRef,
7
+ } from '../primitives';
8
+ import type { SingleAvatarType } from '../avatarLayout';
9
+
10
+ export type ButtonSentiment = 'default' | 'negative';
11
+ export type ButtonPriority = 'primary' | 'secondary' | 'tertiary' | 'minimal';
12
+ export type ButtonType = 'button' | 'submit' | 'reset';
13
+ export type ButtonSize = 'sm' | 'md' | 'lg';
14
+ export type ButtonRefType = PrimitiveButtonElementRef | PrimitiveAnchorElementRef;
15
+
16
+ /**
17
+ * Common properties for the Button component.
18
+ */
19
+ export interface CommonButtonProps {
20
+ /**
21
+ * If set, toggles the new Button API
22
+ * @default false
23
+ * */
24
+ v2?: boolean;
25
+
26
+ /**
27
+ * The HTML element to render
28
+ * @default 'button'
29
+ **/
30
+ as?: 'button' | 'a';
31
+
32
+ /** Additional class name(s) to apply to the button */
33
+ className?: string;
34
+
35
+ /**
36
+ * @default false
37
+ **/
38
+ disabled?: boolean;
39
+
40
+ href?: string;
41
+
42
+ /**
43
+ * @default false
44
+ * */
45
+ loading?: boolean;
46
+
47
+ /** Whether the button should take up the full width of its container */
48
+ block?: boolean;
49
+
50
+ /**
51
+ * Size of the button
52
+ * @default lg
53
+ * */
54
+ size?: ButtonSize;
55
+
56
+ /**
57
+ * Priority of the button
58
+ * @default "primary"
59
+ */
60
+ priority?: ButtonPriority;
61
+
62
+ /**
63
+ * Sentiment of the button
64
+ * @default default
65
+ */
66
+ sentiment?: ButtonSentiment;
67
+
68
+ /** Icon to be displayed on the left side of the button */
69
+ iconStart?: React.ElementType;
70
+
71
+ /** Icon to be displayed on the right side of the button */
72
+ iconEnd?: React.ElementType;
73
+
74
+ /** Media to be displayed on the left side of the button */
75
+ avatars?: SingleAvatarType[];
76
+
77
+ /** Optional property to provide component Ref */
78
+ ref?: ButtonRefType;
79
+
80
+ /** Content to be displayed inside the button */
81
+ children?: ReactNode;
82
+ }
83
+
84
+ export type ButtonElementProps = PrimitiveButtonProps & CommonButtonProps;
85
+ export type AnchorElementProps = PrimitiveAnchorProps & CommonButtonProps;
86
+ export type ButtonProps = ButtonElementProps | AnchorElementProps;
@@ -0,0 +1,39 @@
1
+ .wds-Button {
2
+ --Button-background: var(--color-interactive-accent);
3
+ --Button-background-hover: var(--color-interactive-accent-hover);
4
+ --Button-background-active: var(--color-interactive-accent-active);
5
+ --Button-color: var(--color-interactive-control);
6
+ --Button-border-radius: var(--radius-full);
7
+ --Button-label-gap: var(--size-4);
8
+ --Button-large-padding: var(--size-12) var(--size-16);
9
+ --Button-medium-padding: var(--size-8) var(--size-12);
10
+ --Button-small-padding: calc(var(--size-10) * 0.5) var(--size-12);
11
+ --Button-avatar-border-color: var(--color-border-neutral);
12
+ --Button-transition-duration: 150ms;
13
+ --Button-transition-easing: ease-in-out;
14
+ --Button-secondary-background: var(--color-interactive-neutral);
15
+ --Button-secondary-background-hover: var(--color-interactive-neutral-hover);
16
+ --Button-secondary-background-active: var(--color-interactive-neutral-active);
17
+ --Button-secondary-color: var(--color-interactive-primary);
18
+ --Button-tertiary-background: var(--color-background-neutral);
19
+ --Button-tertiary-background-hover: var(--color-background-neutral-hover);
20
+ --Button-tertiary-background-active: var(--color-background-neutral-active);
21
+ --Button-tertiary-color: var(--color-content-primary);
22
+ --Button-minimal-background: transparent;
23
+ --Button-minimal-background-hover: var(--color-background-screen-hover);
24
+ --Button-minimal-background-active: var(--color-background-screen-active);
25
+ --Button-minimal-color: var(--color-interactive-primary);
26
+ --Button-primary-negative-background: var(--color-sentiment-negative-primary);
27
+ --Button-primary-negative-background-hover: var(--color-sentiment-negative-primary-hover);
28
+ --Button-primary-negative-background-active: var(--color-sentiment-negative-primary-active);
29
+ --Button-primary-negative-color: var(--color-contrast);
30
+ --Button-secondary-negative-background: var(--color-sentiment-negative-secondary);
31
+ --Button-secondary-negative-background-hover: var(--color-sentiment-negative-secondary-hover);
32
+ --Button-secondary-negative-background-active: var(--color-sentiment-negative-secondary-active);
33
+ --Button-secondary-negative-color: var(--color-sentiment-negative-primary);
34
+ }
35
+ .np-theme-personal--bright-green .wds-Button {
36
+ --color-contrast: #FFFFFF;
37
+ --Button-secondary-color: var(--color-interactive-control);
38
+ --Button-secondary-negative-color: var(--color-contrast);
39
+ }
@@ -0,0 +1,49 @@
1
+ .wds-Button {
2
+ --Button-background: var(--color-interactive-accent);
3
+ --Button-background-hover: var(--color-interactive-accent-hover);
4
+ --Button-background-active: var(--color-interactive-accent-active);
5
+ --Button-color: var(--color-interactive-control);
6
+ --Button-border-radius: var(--radius-full);
7
+ --Button-label-gap: var(--size-4);
8
+
9
+ --Button-large-padding: var(--size-12) var(--size-16);
10
+ --Button-medium-padding: var(--size-8) var(--size-12);
11
+ // 5px is required by design
12
+ --Button-small-padding: calc(var(--size-10) * .5) var(--size-12);
13
+
14
+ --Button-avatar-border-color: var(--color-border-neutral);
15
+ --Button-transition-duration: 150ms;
16
+ --Button-transition-easing: ease-in-out;
17
+
18
+ --Button-secondary-background: var(--color-interactive-neutral);
19
+ --Button-secondary-background-hover: var(--color-interactive-neutral-hover);
20
+ --Button-secondary-background-active: var(--color-interactive-neutral-active);
21
+ --Button-secondary-color: var(--color-interactive-primary);
22
+
23
+ --Button-tertiary-background: var(--color-background-neutral);
24
+ --Button-tertiary-background-hover: var(--color-background-neutral-hover);
25
+ --Button-tertiary-background-active: var(--color-background-neutral-active);
26
+ --Button-tertiary-color: var(--color-content-primary);
27
+
28
+ --Button-minimal-background: transparent;
29
+ --Button-minimal-background-hover: var(--color-background-screen-hover);
30
+ --Button-minimal-background-active: var(--color-background-screen-active);
31
+ --Button-minimal-color: var(--color-interactive-primary);
32
+
33
+ --Button-primary-negative-background: var(--color-sentiment-negative-primary);
34
+ --Button-primary-negative-background-hover: var(--color-sentiment-negative-primary-hover);
35
+ --Button-primary-negative-background-active: var(--color-sentiment-negative-primary-active);
36
+ --Button-primary-negative-color: var(--color-contrast);
37
+
38
+ --Button-secondary-negative-background: var(--color-sentiment-negative-secondary);
39
+ --Button-secondary-negative-background-hover: var(--color-sentiment-negative-secondary-hover);
40
+ --Button-secondary-negative-background-active: var(--color-sentiment-negative-secondary-active);
41
+ --Button-secondary-negative-color: var(--color-sentiment-negative-primary);
42
+
43
+
44
+ .np-theme-personal--bright-green & {
45
+ --color-contrast: #FFFFFF;
46
+ --Button-secondary-color: var(--color-interactive-control);
47
+ --Button-secondary-negative-color: var(--color-contrast);
48
+ }
49
+ }
@@ -0,0 +1,23 @@
1
+ .np-btn {
2
+ position: relative;
3
+ }
4
+ .np-btn.np-btn-block > span.btn-loader {
5
+ position: absolute;
6
+ left: 24px;
7
+ left: var(--size-24);
8
+ }
9
+ [dir="rtl"] .np-btn.np-btn-block > span.btn-loader {
10
+ right: 24px;
11
+ right: var(--size-24);
12
+ left: auto;
13
+ left: initial;
14
+ }
15
+ .np-btn.np-btn-xs > span.btn-loader {
16
+ background-size: 16px 16px;
17
+ }
18
+ .np-btn.np-btn-xs.btn-block > span.btn-loader {
19
+ top: 0;
20
+ }
21
+ .np-btn.disabled[class] {
22
+ pointer-events: auto;
23
+ }
@@ -0,0 +1,24 @@
1
+ @import (reference) "../../node_modules/@transferwise/neptune-css/src/less/mixins/_logical-properties.less";
2
+
3
+ .np-btn {
4
+ position: relative;
5
+
6
+ &.np-btn-block > span.btn-loader {
7
+ position: absolute;
8
+ .left(var(--size-24));
9
+ }
10
+
11
+ &.np-btn-xs > span.btn-loader {
12
+ background-size: 16px 16px;
13
+ }
14
+
15
+ &.np-btn-xs.btn-block > span.btn-loader {
16
+ top: 0;
17
+ }
18
+
19
+ // we do not want pointer events disabled as that
20
+ // prevents the custom cursor to be shown
21
+ &.disabled[class] {
22
+ pointer-events: auto;
23
+ }
24
+ }
@@ -0,0 +1,146 @@
1
+ import { createRef } from 'react';
2
+
3
+ import { render, screen, userEvent } from '../test-utils';
4
+
5
+ import LegacyButton, { ButtonReferenceType } from './LegacyButton';
6
+ import messages from '../i18n/commonMessages/Button.messages';
7
+
8
+ describe('LegacyButton', () => {
9
+ // eslint-disable-next-line no-console
10
+ const originalWarn = console.warn;
11
+ let mockedWarn: typeof originalWarn;
12
+
13
+ const props = {
14
+ onClick: jest.fn(),
15
+ onFocus: jest.fn(),
16
+ onBlur: jest.fn(),
17
+ children: 'Send money',
18
+ };
19
+
20
+ beforeAll(() => {
21
+ mockedWarn = jest.fn().mockImplementation((args) => {
22
+ if (typeof args !== 'string' || !args.startsWith('Button has deprecated the')) {
23
+ originalWarn(args);
24
+ }
25
+ });
26
+ // eslint-disable-next-line no-console
27
+ console.warn = mockedWarn;
28
+ });
29
+
30
+ beforeEach(jest.clearAllMocks);
31
+
32
+ afterAll(() => {
33
+ // eslint-disable-next-line no-console
34
+ console.warn = originalWarn;
35
+ });
36
+
37
+ describe('by default', () => {
38
+ it('renders the text', () => {
39
+ render(<LegacyButton {...props} />);
40
+ expect(screen.getByText(props.children)).toBeInTheDocument();
41
+ });
42
+
43
+ it('set `ref` to be true on Button', () => {
44
+ const reference = createRef<ButtonReferenceType>();
45
+
46
+ expect(reference.current).toBeFalsy();
47
+ render(<LegacyButton ref={reference}>Click me!</LegacyButton>);
48
+ expect(reference.current).toBeTruthy();
49
+ });
50
+
51
+ it('is not disabled', () => {
52
+ render(<LegacyButton {...props} />);
53
+ expect(screen.getByRole('button')).toBeEnabled();
54
+ });
55
+ });
56
+
57
+ describe('button attributes', () => {
58
+ it('sets the htmlType if set', () => {
59
+ render(<LegacyButton {...props} htmlType="submit" />);
60
+ expect(screen.getByRole('button')).toHaveAttribute('type', 'submit');
61
+ });
62
+
63
+ it('passes through custom classes if set', () => {
64
+ render(<LegacyButton {...props} className="donkeysarethebest" />);
65
+ expect(screen.getByRole('button')).toHaveClass('donkeysarethebest');
66
+ });
67
+
68
+ it('passes through aria-label if set', () => {
69
+ render(<LegacyButton {...props} aria-label="unique label" />);
70
+ const loadingButton = screen.getByLabelText('unique label');
71
+ expect(loadingButton).toBeInTheDocument();
72
+ });
73
+ });
74
+
75
+ describe('onClick', () => {
76
+ it('calls onClick when clicked', async () => {
77
+ render(<LegacyButton {...props} />);
78
+ await userEvent.click(screen.getByRole('button'));
79
+ expect(props.onClick).toHaveBeenCalledTimes(1);
80
+ });
81
+
82
+ it('does not call onClick when clicked if disabled', async () => {
83
+ render(<LegacyButton {...props} disabled />);
84
+ await userEvent.click(screen.getByRole('button'));
85
+ expect(props.onClick).toHaveBeenCalledTimes(0);
86
+ });
87
+
88
+ it('does not call onClick when clicked if loading', async () => {
89
+ render(<LegacyButton {...props} loading />);
90
+ await userEvent.click(screen.getByRole('button'));
91
+ expect(props.onClick).toHaveBeenCalledTimes(0);
92
+ });
93
+ });
94
+
95
+ describe('onFocus and onBlur', () => {
96
+ it('calls both handlers by default', async () => {
97
+ render(<LegacyButton {...props} />);
98
+ await userEvent.tab();
99
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
100
+ await userEvent.tab();
101
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
102
+ });
103
+
104
+ it('does not call either handler if disabled', async () => {
105
+ render(<LegacyButton {...props} disabled />);
106
+ await userEvent.tab();
107
+ expect(props.onFocus).not.toHaveBeenCalled();
108
+ await userEvent.tab();
109
+ expect(props.onFocus).not.toHaveBeenCalled();
110
+ });
111
+
112
+ it('calls both handlers if loading', async () => {
113
+ render(<LegacyButton {...props} loading />);
114
+ await userEvent.tab();
115
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
116
+ await userEvent.tab();
117
+ expect(props.onFocus).toHaveBeenCalledTimes(1);
118
+ });
119
+ });
120
+
121
+ describe('other states', () => {
122
+ it('renders as loading if `loading` is true', () => {
123
+ render(<LegacyButton {...props} loading />);
124
+ const button = screen.queryByRole('button', {
125
+ name: messages.loadingAriaLabel.defaultMessage,
126
+ });
127
+ expect(button).toBeInTheDocument();
128
+ expect(button).toBeEnabled();
129
+ expect(button).toHaveClass('btn-loading');
130
+ expect(button).toHaveAttribute('aria-disabled', 'true');
131
+ expect(button).toHaveAttribute('aria-busy', 'true');
132
+ expect(button).toHaveAttribute('aria-live', 'polite');
133
+ expect(screen.getByTestId('ButtonProgressIndicator')).toBeInTheDocument();
134
+ });
135
+
136
+ it('disables the button', () => {
137
+ render(<LegacyButton {...props} disabled />);
138
+ const button = screen.queryByRole('button');
139
+ expect(button).toBeDisabled();
140
+ expect(button).toHaveClass('disabled');
141
+ expect(button).toHaveAttribute('aria-disabled', 'false');
142
+ expect(button).toHaveAttribute('aria-busy', 'false');
143
+ expect(button).toHaveAttribute('aria-live', 'off');
144
+ });
145
+ });
146
+ });