@atlaskit/button 16.1.2

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 (113) hide show
  1. package/CHANGELOG.md +1485 -0
  2. package/LICENSE +13 -0
  3. package/README.md +13 -0
  4. package/__perf__/button.tsx +19 -0
  5. package/__perf__/custom.tsx +19 -0
  6. package/__perf__/customised.tsx +11 -0
  7. package/__perf__/default.tsx +5 -0
  8. package/__perf__/loading.tsx +5 -0
  9. package/__perf__/utils/example-runner.tsx +48 -0
  10. package/__perf__/utils/interaction-tasks.tsx +98 -0
  11. package/button-group/package.json +7 -0
  12. package/codemods/15.0.0-lite-mode.ts +49 -0
  13. package/codemods/15.1.1-data-testid.ts +173 -0
  14. package/codemods/__tests__/15.0.0-lite-mode/optimistic.ts +646 -0
  15. package/codemods/__tests__/15.0.0-lite-mode/safe.ts +223 -0
  16. package/codemods/__tests__/15.0.0-lite-mode/shared.ts +257 -0
  17. package/codemods/__tests__/15.1.1-data-testid/rename-data-testid.ts +186 -0
  18. package/codemods/__tests__/_framework.ts +47 -0
  19. package/codemods/helpers/15.0.0-runner.ts +169 -0
  20. package/codemods/helpers/helpers-generic.ts +662 -0
  21. package/codemods/optimistic-15.0.0-lite-mode.ts +279 -0
  22. package/codemods/readme.md +1 -0
  23. package/custom-theme-button/package.json +7 -0
  24. package/dist/cjs/button-group.js +50 -0
  25. package/dist/cjs/button.js +104 -0
  26. package/dist/cjs/custom-theme-button/custom-theme-button-types.js +5 -0
  27. package/dist/cjs/custom-theme-button/custom-theme-button.js +229 -0
  28. package/dist/cjs/custom-theme-button/index.js +23 -0
  29. package/dist/cjs/custom-theme-button/theme.js +108 -0
  30. package/dist/cjs/entry-points/button-group.js +15 -0
  31. package/dist/cjs/entry-points/custom-theme-button.js +25 -0
  32. package/dist/cjs/entry-points/loading-button.js +15 -0
  33. package/dist/cjs/entry-points/standard-button.js +15 -0
  34. package/dist/cjs/entry-points/types.js +5 -0
  35. package/dist/cjs/index.js +51 -0
  36. package/dist/cjs/loading-button.js +34 -0
  37. package/dist/cjs/shared/block-events.js +44 -0
  38. package/dist/cjs/shared/button-base.js +158 -0
  39. package/dist/cjs/shared/colors.js +409 -0
  40. package/dist/cjs/shared/css.js +265 -0
  41. package/dist/cjs/shared/get-is-only-single-icon.js +26 -0
  42. package/dist/cjs/shared/loading-spinner.js +45 -0
  43. package/dist/cjs/types.js +5 -0
  44. package/dist/cjs/version.json +5 -0
  45. package/dist/es2019/button-group.js +36 -0
  46. package/dist/es2019/button.js +69 -0
  47. package/dist/es2019/custom-theme-button/custom-theme-button-types.js +1 -0
  48. package/dist/es2019/custom-theme-button/custom-theme-button.js +164 -0
  49. package/dist/es2019/custom-theme-button/index.js +2 -0
  50. package/dist/es2019/custom-theme-button/theme.js +81 -0
  51. package/dist/es2019/entry-points/button-group.js +1 -0
  52. package/dist/es2019/entry-points/custom-theme-button.js +1 -0
  53. package/dist/es2019/entry-points/loading-button.js +1 -0
  54. package/dist/es2019/entry-points/standard-button.js +1 -0
  55. package/dist/es2019/entry-points/types.js +1 -0
  56. package/dist/es2019/index.js +6 -0
  57. package/dist/es2019/loading-button.js +17 -0
  58. package/dist/es2019/shared/block-events.js +37 -0
  59. package/dist/es2019/shared/button-base.js +127 -0
  60. package/dist/es2019/shared/colors.js +393 -0
  61. package/dist/es2019/shared/css.js +249 -0
  62. package/dist/es2019/shared/get-is-only-single-icon.js +19 -0
  63. package/dist/es2019/shared/loading-spinner.js +33 -0
  64. package/dist/es2019/types.js +1 -0
  65. package/dist/es2019/version.json +5 -0
  66. package/dist/esm/button-group.js +35 -0
  67. package/dist/esm/button.js +79 -0
  68. package/dist/esm/custom-theme-button/custom-theme-button-types.js +1 -0
  69. package/dist/esm/custom-theme-button/custom-theme-button.js +203 -0
  70. package/dist/esm/custom-theme-button/index.js +2 -0
  71. package/dist/esm/custom-theme-button/theme.js +90 -0
  72. package/dist/esm/entry-points/button-group.js +1 -0
  73. package/dist/esm/entry-points/custom-theme-button.js +1 -0
  74. package/dist/esm/entry-points/loading-button.js +1 -0
  75. package/dist/esm/entry-points/standard-button.js +1 -0
  76. package/dist/esm/entry-points/types.js +1 -0
  77. package/dist/esm/index.js +6 -0
  78. package/dist/esm/loading-button.js +19 -0
  79. package/dist/esm/shared/block-events.js +36 -0
  80. package/dist/esm/shared/button-base.js +135 -0
  81. package/dist/esm/shared/colors.js +393 -0
  82. package/dist/esm/shared/css.js +245 -0
  83. package/dist/esm/shared/get-is-only-single-icon.js +19 -0
  84. package/dist/esm/shared/loading-spinner.js +35 -0
  85. package/dist/esm/types.js +1 -0
  86. package/dist/esm/version.json +5 -0
  87. package/dist/types/button-group.d.ts +18 -0
  88. package/dist/types/button.d.ts +8 -0
  89. package/dist/types/custom-theme-button/custom-theme-button-types.d.ts +21 -0
  90. package/dist/types/custom-theme-button/custom-theme-button.d.ts +6 -0
  91. package/dist/types/custom-theme-button/index.d.ts +2 -0
  92. package/dist/types/custom-theme-button/theme.d.ts +21 -0
  93. package/dist/types/entry-points/button-group.d.ts +1 -0
  94. package/dist/types/entry-points/custom-theme-button.d.ts +2 -0
  95. package/dist/types/entry-points/loading-button.d.ts +2 -0
  96. package/dist/types/entry-points/standard-button.d.ts +2 -0
  97. package/dist/types/entry-points/types.d.ts +4 -0
  98. package/dist/types/index.d.ts +8 -0
  99. package/dist/types/loading-button.d.ts +11 -0
  100. package/dist/types/shared/block-events.d.ts +3 -0
  101. package/dist/types/shared/button-base.d.ts +10 -0
  102. package/dist/types/shared/colors.d.ts +31 -0
  103. package/dist/types/shared/css.d.ts +22 -0
  104. package/dist/types/shared/get-is-only-single-icon.d.ts +2 -0
  105. package/dist/types/shared/loading-spinner.d.ts +4 -0
  106. package/dist/types/types.d.ts +51 -0
  107. package/extract-react-types/custom-theme-button-props.tsx +7 -0
  108. package/extract-react-types/loading-button-props.tsx +5 -0
  109. package/extract-react-types/shared-props.tsx +5 -0
  110. package/loading-button/package.json +7 -0
  111. package/package.json +83 -0
  112. package/standard-button/package.json +7 -0
  113. package/types/package.json +7 -0
@@ -0,0 +1,164 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React, { useCallback, useState } from 'react';
3
+ import GlobalTheme from '@atlaskit/theme/components';
4
+ import ButtonBase from '../shared/button-base';
5
+ import getIsOnlySingleIcon from '../shared/get-is-only-single-icon';
6
+ import LoadingSpinner from '../shared/loading-spinner';
7
+ import Theme, { defaultThemeFn, getSpecifiers } from './theme';
8
+
9
+ function getInteractionState({
10
+ isDisabled = false,
11
+ isActive = false,
12
+ isFocus = false,
13
+ isHover = false,
14
+ isSelected = false,
15
+ isLoading = false
16
+ }) {
17
+ if (isDisabled) {
18
+ return 'disabled';
19
+ }
20
+
21
+ if (isSelected && isFocus) {
22
+ return 'focusSelected';
23
+ }
24
+
25
+ if (isSelected) {
26
+ return 'selected';
27
+ } // not allowing active or focus style changes while loading
28
+
29
+
30
+ if (!isLoading && isActive) {
31
+ return 'active';
32
+ }
33
+
34
+ if (!isLoading && isHover) {
35
+ return 'hover';
36
+ }
37
+
38
+ if (isFocus) {
39
+ return 'focus';
40
+ }
41
+
42
+ return 'default';
43
+ }
44
+
45
+ const initial = {
46
+ isHover: false,
47
+ isActive: false,
48
+ isFocus: false
49
+ };
50
+ const CustomThemeButton = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef(function CustomThemeButton({
51
+ // Calculate default props for use in custom themes
52
+ appearance = 'default',
53
+ autoFocus = false,
54
+ isDisabled = false,
55
+ isSelected = false,
56
+ shouldFitContainer = false,
57
+ spacing = 'default',
58
+ isLoading = false,
59
+ onMouseEnter: providedOnMouseEnter,
60
+ onMouseLeave: providedOnMouseLeave,
61
+ onMouseDown: providedOnMouseDown,
62
+ onMouseUp: providedOnMouseUp,
63
+ onFocus: providedOnFocus,
64
+ onBlur: providedOnBlur,
65
+ theme = defaultThemeFn,
66
+ ...rest
67
+ }, ref) {
68
+ // TODO is there a nicer way to do this?
69
+ // Add default props back into object for spreading
70
+ const restProps = {
71
+ appearance,
72
+ autoFocus,
73
+ isDisabled,
74
+ isSelected,
75
+ shouldFitContainer,
76
+ spacing,
77
+ ...rest
78
+ };
79
+ const [state, setState] = useState(initial);
80
+ const onMouseEnter = useCallback(event => {
81
+ setState(current => ({ ...current,
82
+ isHover: true
83
+ }));
84
+
85
+ if (providedOnMouseEnter) {
86
+ providedOnMouseEnter(event);
87
+ }
88
+ }, [providedOnMouseEnter]);
89
+ const onMouseLeave = useCallback(event => {
90
+ setState(current => ({ ...current,
91
+ isHover: false,
92
+ isActive: false
93
+ }));
94
+
95
+ if (providedOnMouseLeave) {
96
+ providedOnMouseLeave(event);
97
+ }
98
+ }, [providedOnMouseLeave]);
99
+ const onMouseDown = useCallback(event => {
100
+ setState(current => ({ ...current,
101
+ isActive: true
102
+ }));
103
+
104
+ if (providedOnMouseDown) {
105
+ providedOnMouseDown(event);
106
+ }
107
+ }, [providedOnMouseDown]);
108
+ const onMouseUp = useCallback(event => {
109
+ setState(current => ({ ...current,
110
+ isActive: false
111
+ }));
112
+
113
+ if (providedOnMouseUp) {
114
+ providedOnMouseUp(event);
115
+ }
116
+ }, [providedOnMouseUp]);
117
+ const onFocus = useCallback(event => {
118
+ setState(current => ({ ...current,
119
+ isFocus: true
120
+ }));
121
+
122
+ if (providedOnFocus) {
123
+ providedOnFocus(event);
124
+ }
125
+ }, [providedOnFocus]);
126
+ const onBlur = useCallback(event => {
127
+ setState(current => ({ ...current,
128
+ isFocus: false
129
+ }));
130
+
131
+ if (providedOnBlur) {
132
+ providedOnBlur(event);
133
+ }
134
+ }, [providedOnBlur]);
135
+ return /*#__PURE__*/React.createElement(Theme.Provider, {
136
+ value: theme
137
+ }, /*#__PURE__*/React.createElement(GlobalTheme.Consumer, null, ({
138
+ mode
139
+ }) => /*#__PURE__*/React.createElement(Theme.Consumer, _extends({
140
+ mode: mode,
141
+ state: getInteractionState({ ...state,
142
+ isLoading,
143
+ isSelected: restProps.isSelected,
144
+ isDisabled: restProps.isDisabled
145
+ }),
146
+ iconIsOnlyChild: getIsOnlySingleIcon(restProps),
147
+ isLoading: isLoading
148
+ }, restProps), ({
149
+ buttonStyles
150
+ }) => /*#__PURE__*/React.createElement(ButtonBase, _extends({}, restProps, {
151
+ ref: ref,
152
+ overlay: isLoading ? /*#__PURE__*/React.createElement(LoadingSpinner, restProps) : null,
153
+ onMouseEnter: onMouseEnter,
154
+ onMouseLeave: onMouseLeave,
155
+ onMouseDown: onMouseDown,
156
+ onMouseUp: onMouseUp,
157
+ onFocus: onFocus,
158
+ onBlur: onBlur,
159
+ buttonCss: getSpecifiers(buttonStyles)
160
+ })))));
161
+ })); // Tools including enzyme rely on components having a display name
162
+
163
+ CustomThemeButton.displayName = 'CustomThemeButton';
164
+ export default CustomThemeButton;
@@ -0,0 +1,2 @@
1
+ export { default } from './custom-theme-button';
2
+ export { default as Theme } from './theme';
@@ -0,0 +1,81 @@
1
+ import { createTheme } from '@atlaskit/theme/components';
2
+ import { getCss } from '../shared/css';
3
+ const stateToSelectorMap = {
4
+ focus: '&:focus',
5
+ focusSelected: '&:focus',
6
+ hover: '&:hover',
7
+ active: '&:active',
8
+ disabled: '&[disabled]'
9
+ }; // Mapping the new clean css back to the legacy theme format.
10
+ // The legacy theme format has all styles at the top level (no nested selectors)
11
+ // and uses `getSpecifiers()` to apply the style to all pseudo states
12
+
13
+ export function getCustomCss({
14
+ appearance = 'default',
15
+ spacing = 'default',
16
+ mode = 'light',
17
+ isSelected = false,
18
+ shouldFitContainer = false,
19
+ iconIsOnlyChild = false,
20
+ isLoading = false,
21
+ state
22
+ }) {
23
+ let result = getCss({
24
+ appearance,
25
+ spacing,
26
+ mode,
27
+ isSelected,
28
+ shouldFitContainer,
29
+ isOnlySingleIcon: iconIsOnlyChild
30
+ }); // we need to disable the default browser focus styles always
31
+ // this is because we are not expressing that we can have two pseudo states at a time
32
+
33
+ result.outline = 'none'; // Pulling relevant styles up to the top level
34
+
35
+ const selector = stateToSelectorMap[state];
36
+
37
+ if (selector) {
38
+ result = { ...result,
39
+ ...result[selector]
40
+ };
41
+ }
42
+
43
+ if (isLoading) {
44
+ result = { ...result,
45
+ // Pull overlay styles up to the top
46
+ ...result['&[data-has-overlay="true"]']
47
+ };
48
+ } // Delete all selectors and just keep root styles
49
+
50
+
51
+ Object.keys(result).forEach(key => {
52
+ // want to keep this one
53
+ if (key === '&::-moz-focus-inner') {
54
+ return;
55
+ } // Not using .startsWith for ie11
56
+
57
+
58
+ if (key.indexOf('&') === 0) {
59
+ delete result[key];
60
+ }
61
+ });
62
+ return result;
63
+ } // This styling approach works by generating a 'style' and applying with maximum specificity
64
+ // To do this we are overwriting all pseudo selectors
65
+
66
+ export function getSpecifiers(styles) {
67
+ return {
68
+ '&, &:hover, &:active, &:focus, &:visited, &:disabled, &[disabled]': styles
69
+ };
70
+ }
71
+ export function defaultThemeFn(current, values) {
72
+ return current(values);
73
+ }
74
+ const Theme = createTheme(themeProps => ({
75
+ buttonStyles: getCustomCss(themeProps),
76
+ // No styles being applied directly to spinner by default
77
+ // Keeping this for legacy compat. We could remove it, but given
78
+ // that we are changing theme soon there is no point
79
+ spinnerStyles: {}
80
+ }));
81
+ export default Theme;
@@ -0,0 +1 @@
1
+ export { default } from '../button-group';
@@ -0,0 +1 @@
1
+ export { default, Theme } from '../custom-theme-button';
@@ -0,0 +1 @@
1
+ export { default } from '../loading-button';
@@ -0,0 +1 @@
1
+ export { default } from '../button';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ // Ideally this file is not used directly. But rather, you go through the entry points
2
+ export { // default export is Button
3
+ default } from './entry-points/standard-button';
4
+ export { default as LoadingButton } from './entry-points/loading-button';
5
+ export { default as CustomThemeButton, Theme } from './entry-points/custom-theme-button';
6
+ export { default as ButtonGroup } from './entry-points/button-group';
@@ -0,0 +1,17 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import React from 'react';
3
+ import Button from './button';
4
+ import LoadingSpinner from './shared/loading-spinner';
5
+ const LoadingButton = /*#__PURE__*/React.forwardRef(function LoadingButton({
6
+ isLoading = false,
7
+ ...rest
8
+ }, ref) {
9
+ // Button already has React.memo, so just leaning on that
10
+ return /*#__PURE__*/React.createElement(Button, _extends({}, rest, {
11
+ ref: ref,
12
+ overlay: isLoading ? /*#__PURE__*/React.createElement(LoadingSpinner, rest) : null
13
+ }));
14
+ }); // Tools including enzyme rely on components having a display name
15
+
16
+ LoadingButton.displayName = 'LoadingButton';
17
+ export default LoadingButton;
@@ -0,0 +1,37 @@
1
+ function abort(event) {
2
+ event.preventDefault();
3
+ event.stopPropagation();
4
+ }
5
+
6
+ const tabKeyCode = 9;
7
+
8
+ function onKey(event) {
9
+ // Allowing tab so that a user can move focus away
10
+ if (event.keyCode === tabKeyCode) {
11
+ return;
12
+ }
13
+
14
+ abort(event);
15
+ }
16
+
17
+ const block = {
18
+ onMouseDownCapture: abort,
19
+ onMouseUpCapture: abort,
20
+ // because we have tabIndex = -1 when disabled,
21
+ // keyboard events can only occur when there is an overlay
22
+ onKeyDownCapture: onKey,
23
+ onKeyUpCapture: onKey,
24
+ onTouchStartCapture: abort,
25
+ onTouchEndCapture: abort,
26
+ onPointerDownCapture: abort,
27
+ onPointerUpCapture: abort,
28
+ onClickCapture: abort,
29
+ // Just smashing the existing onClick for good measure
30
+ onClick: abort
31
+ };
32
+ const doNotBlock = {};
33
+ export default function blockEvents({
34
+ isInteractive
35
+ }) {
36
+ return isInteractive ? doNotBlock : block;
37
+ }
@@ -0,0 +1,127 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+
3
+ /** @jsx jsx */
4
+ import React, { useCallback, useEffect, useRef } from 'react';
5
+ import { jsx } from '@emotion/core';
6
+ import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next';
7
+ import useAutoFocus from '@atlaskit/ds-lib/use-auto-focus';
8
+ import blockEvents from './block-events';
9
+ import { getContentStyle, getFadingCss, getIconStyle, overlayCss } from './css';
10
+
11
+ function noop() {} // Disabled buttons will still publish events for nested elements in webkit.
12
+ // We are disabling pointer events on child elements so that
13
+ // the button will always be the target of events
14
+ // Note: firefox does not have this behaviour for child elements
15
+
16
+
17
+ const noPointerEventsOnChildrenCss = {
18
+ '> *': {
19
+ pointerEvents: 'none'
20
+ }
21
+ };
22
+ export default /*#__PURE__*/React.forwardRef(function ButtonBase(props, ref) {
23
+ const {
24
+ appearance = 'default',
25
+ buttonCss,
26
+ spacing = 'default',
27
+ autoFocus = false,
28
+ isDisabled = false,
29
+ shouldFitContainer = false,
30
+ isSelected = false,
31
+ iconBefore,
32
+ iconAfter,
33
+ children,
34
+ className,
35
+ href,
36
+ overlay,
37
+ tabIndex = 0,
38
+ type = !href ? 'button' : undefined,
39
+ onMouseDown: providedOnMouseDown = noop,
40
+ onClick: providedOnClick = noop,
41
+ // use the provided component prop,
42
+ // else default to anchor if there is a href, and button if there is no href
43
+ component: Component = href ? 'a' : 'button',
44
+ testId,
45
+ // I don't think this should be in button, but for now it is
46
+ analyticsContext,
47
+ ...rest
48
+ } = props;
49
+ const ourRef = useRef();
50
+ const setRef = useCallback(node => {
51
+ ourRef.current = node;
52
+
53
+ if (ref == null) {
54
+ return;
55
+ }
56
+
57
+ if (typeof ref === 'function') {
58
+ ref(node);
59
+ return;
60
+ } // @ts-ignore
61
+
62
+
63
+ ref.current = node;
64
+ }, [ourRef, ref]); // Cross browser auto focusing is pretty broken, so we are doing it ourselves
65
+
66
+ useAutoFocus(ourRef, autoFocus);
67
+ const onClick = usePlatformLeafEventHandler({
68
+ fn: providedOnClick,
69
+ action: 'clicked',
70
+ componentName: 'button',
71
+ packageName: "@atlaskit/button",
72
+ packageVersion: "16.1.2",
73
+ analyticsData: analyticsContext
74
+ }); // Button currently calls preventDefault, which is not standard button behaviour
75
+
76
+ const onMouseDown = useCallback(event => {
77
+ event.preventDefault();
78
+ providedOnMouseDown(event);
79
+ }, [providedOnMouseDown]); // Lose focus when becoming disabled (standard button behaviour)
80
+
81
+ useEffect(() => {
82
+ const el = ourRef.current;
83
+
84
+ if (isDisabled && el && el === document.activeElement) {
85
+ el.blur();
86
+ }
87
+ }, [isDisabled]); // we are 'disabling' input with a button when there is an overlay
88
+
89
+ const hasOverlay = Boolean(overlay);
90
+ const fadeCss = getFadingCss({
91
+ hasOverlay
92
+ });
93
+ const isInteractive = !isDisabled && !hasOverlay;
94
+ return jsx(Component, _extends({}, rest, {
95
+ css: [buttonCss, isInteractive ? null : noPointerEventsOnChildrenCss],
96
+ className: className,
97
+ ref: setRef,
98
+ onClick: onClick,
99
+ onMouseDown: onMouseDown,
100
+ disabled: isDisabled,
101
+ href: isInteractive ? href : undefined // using undefined so that the property doesn't exist when false
102
+ ,
103
+ "data-has-overlay": hasOverlay ? true : undefined,
104
+ "data-testid": testId,
105
+ type: type // Adding a tab index so element is always focusable, even when not a <button> or <a>
106
+ // Disabling focus via keyboard navigation when disabled
107
+ // as this is standard button behaviour
108
+ ,
109
+ tabIndex: isDisabled ? -1 : tabIndex
110
+ }, blockEvents({
111
+ isInteractive
112
+ })), iconBefore ? jsx("span", {
113
+ css: [fadeCss, getIconStyle({
114
+ spacing
115
+ })]
116
+ }, iconBefore) : null, children ? jsx("span", {
117
+ css: [fadeCss, getContentStyle({
118
+ spacing
119
+ })]
120
+ }, children) : null, iconAfter ? jsx("span", {
121
+ css: [fadeCss, getIconStyle({
122
+ spacing
123
+ })]
124
+ }, iconAfter) : null, overlay ? jsx("span", {
125
+ css: overlayCss
126
+ }, overlay) : null);
127
+ });