@khanacademy/wonder-blocks-clickable 3.1.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/components/clickable-behavior.d.ts +8 -3
- package/dist/components/clickable.d.ts +114 -56
- package/package.json +3 -3
- package/src/components/__tests__/clickable-behavior.typestest.tsx +20 -0
- package/src/components/__tests__/clickable.typestest.tsx +45 -0
- package/src/components/clickable-behavior.ts +38 -26
- package/src/components/clickable.tsx +75 -9
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/clickable-behavior.js.flow +0 -286
- package/dist/components/clickable.js.flow +0 -166
- package/dist/index.js.flow +0 -17
- package/dist/util/get-clickable-behavior.js.flow +0 -14
- package/dist/util/is-client-side-url.js.flow +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-clickable
|
|
2
2
|
|
|
3
|
+
## 4.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 674a1e5c: Props are using discriminated union types to prevent invalid combinations of props
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 8c77f29d: Create new Switch component and add 'switch' role to ClickableRole
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 674a1e5c: We're no longer building flow types
|
|
16
|
+
- Updated dependencies [674a1e5c]
|
|
17
|
+
- Updated dependencies [674a1e5c]
|
|
18
|
+
- @khanacademy/wonder-blocks-core@6.0.0
|
|
19
|
+
|
|
3
20
|
## 3.1.3
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
export type ClickableRole = "button" | "link" | "
|
|
3
|
-
type
|
|
2
|
+
export type ClickableRole = "button" | "checkbox" | "link" | "listbox" | "menu" | "menuitem" | "option" | "radio" | "switch" | "tab";
|
|
3
|
+
type CommonProps = Readonly<{
|
|
4
4
|
/**
|
|
5
5
|
* A function that returns the a React `Element`.
|
|
6
6
|
*
|
|
@@ -74,11 +74,15 @@ type Props = Readonly<{
|
|
|
74
74
|
* Respond to raw "keyup" event.
|
|
75
75
|
*/
|
|
76
76
|
onKeyUp?: (e: React.KeyboardEvent) => unknown;
|
|
77
|
+
}>;
|
|
78
|
+
type Props = (CommonProps & Readonly<{
|
|
77
79
|
/**
|
|
78
80
|
* A target destination window for a link to open in. Should only be used
|
|
79
81
|
* when `href` is specified.
|
|
80
82
|
*/
|
|
81
83
|
target?: "_blank";
|
|
84
|
+
beforeNav?: never;
|
|
85
|
+
}>) | (CommonProps & Readonly<{
|
|
82
86
|
/**
|
|
83
87
|
* Run async code before navigating to the URL passed to `href`. If the
|
|
84
88
|
* promise returned rejects then navigation will not occur.
|
|
@@ -93,7 +97,8 @@ type Props = Readonly<{
|
|
|
93
97
|
* navigation.
|
|
94
98
|
*/
|
|
95
99
|
beforeNav?: () => Promise<unknown>;
|
|
96
|
-
|
|
100
|
+
target?: never;
|
|
101
|
+
}>);
|
|
97
102
|
export type ClickableState = Readonly<{
|
|
98
103
|
/**
|
|
99
104
|
* Whether the component is hovered.
|
|
@@ -2,55 +2,28 @@ import * as React from "react";
|
|
|
2
2
|
import { Link } from "react-router-dom";
|
|
3
3
|
import type { AriaProps, StyleType } from "@khanacademy/wonder-blocks-core";
|
|
4
4
|
import type { ClickableRole, ClickableState } from "./clickable-behavior";
|
|
5
|
+
type CommonProps =
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the child appropriately and encapsulates routing logic which can be
|
|
10
|
-
* customized. Expects a function which returns an element as its child.
|
|
11
|
-
*
|
|
12
|
-
* Clickable allows your components to:
|
|
13
|
-
*
|
|
14
|
-
* - Handle mouse / touch / keyboard events
|
|
15
|
-
* - Match the standard behavior of the given role
|
|
16
|
-
* - Apply custom styles based on pressed / focused / hovered state
|
|
17
|
-
* - Perform Client Side Navigation when href is passed and the component is a
|
|
18
|
-
* descendent of a react-router Router.
|
|
19
|
-
*
|
|
20
|
-
* ### Usage
|
|
21
|
-
*
|
|
22
|
-
* ```jsx
|
|
23
|
-
* <Clickable onClick={() => alert("You clicked me!")}>
|
|
24
|
-
* {({hovered, focused, pressed}) =>
|
|
25
|
-
* <div
|
|
26
|
-
* style={[
|
|
27
|
-
* hovered && styles.hovered,
|
|
28
|
-
* focused && styles.focused,
|
|
29
|
-
* pressed && styles.pressed,
|
|
30
|
-
* ]}
|
|
31
|
-
* >
|
|
32
|
-
* Click Me!
|
|
33
|
-
* </div>
|
|
34
|
-
* }
|
|
35
|
-
* </Clickable>
|
|
36
|
-
* ```
|
|
7
|
+
* aria-label should be used when `spinner={true}` to let people using screen
|
|
8
|
+
* readers that the action taken by clicking the button will take some
|
|
9
|
+
* time to complete.
|
|
37
10
|
*/
|
|
38
|
-
|
|
11
|
+
Partial<Omit<AriaProps, "aria-disabled">> & {
|
|
39
12
|
/**
|
|
40
13
|
* The child of Clickable must be a function which returns the component
|
|
41
14
|
* which should be made Clickable. The function is passed an object with
|
|
42
15
|
* three boolean properties: hovered, focused, and pressed.
|
|
43
16
|
*/
|
|
44
|
-
children: (
|
|
17
|
+
children: (clickableState: ClickableState) => React.ReactNode;
|
|
45
18
|
/**
|
|
46
19
|
* An onClick function which Clickable can execute when clicked
|
|
47
20
|
*/
|
|
48
|
-
onClick?: (
|
|
21
|
+
onClick?: (e: React.SyntheticEvent) => unknown;
|
|
49
22
|
/**
|
|
50
23
|
* Optional href which Clickable should direct to, uses client-side routing
|
|
51
24
|
* by default if react-router is present
|
|
52
25
|
*/
|
|
53
|
-
href?: string
|
|
26
|
+
href?: string;
|
|
54
27
|
/**
|
|
55
28
|
* Styles to apply to the Clickable component
|
|
56
29
|
*/
|
|
@@ -58,63 +31,57 @@ declare const Clickable: React.ForwardRefExoticComponent<Partial<Omit<AriaProps,
|
|
|
58
31
|
/**
|
|
59
32
|
* Adds CSS classes to the Clickable.
|
|
60
33
|
*/
|
|
61
|
-
className?: string
|
|
34
|
+
className?: string;
|
|
62
35
|
/**
|
|
63
36
|
* Whether the Clickable is on a dark colored background.
|
|
64
37
|
* Sets the default focus ring color to white, instead of blue.
|
|
65
38
|
* Defaults to false.
|
|
66
39
|
*/
|
|
67
|
-
light?: boolean
|
|
40
|
+
light?: boolean;
|
|
68
41
|
/**
|
|
69
42
|
* Disables or enables the child; defaults to false
|
|
70
43
|
*/
|
|
71
|
-
disabled?: boolean
|
|
44
|
+
disabled?: boolean;
|
|
72
45
|
/**
|
|
73
46
|
* An optional id attribute.
|
|
74
47
|
*/
|
|
75
|
-
id?: string
|
|
48
|
+
id?: string;
|
|
76
49
|
/**
|
|
77
50
|
* Specifies the type of relationship between the current document and the
|
|
78
51
|
* linked document. Should only be used when `href` is specified. This
|
|
79
52
|
* defaults to "noopener noreferrer" when `target="_blank"`, but can be
|
|
80
53
|
* overridden by setting this prop to something else.
|
|
81
54
|
*/
|
|
82
|
-
rel?: string
|
|
55
|
+
rel?: string;
|
|
83
56
|
/**
|
|
84
57
|
* The role of the component, can be a role of type ClickableRole
|
|
85
58
|
*/
|
|
86
|
-
role?: ClickableRole
|
|
59
|
+
role?: ClickableRole;
|
|
87
60
|
/**
|
|
88
61
|
* Avoids client-side routing in the presence of the href prop
|
|
89
62
|
*/
|
|
90
|
-
skipClientNav?: boolean
|
|
63
|
+
skipClientNav?: boolean;
|
|
91
64
|
/**
|
|
92
65
|
* Test ID used for e2e testing.
|
|
93
66
|
*/
|
|
94
|
-
testId?: string
|
|
67
|
+
testId?: string;
|
|
95
68
|
/**
|
|
96
69
|
* Respond to raw "keydown" event.
|
|
97
70
|
*/
|
|
98
|
-
onKeyDown?: (
|
|
71
|
+
onKeyDown?: (e: React.KeyboardEvent) => unknown;
|
|
99
72
|
/**
|
|
100
73
|
* Respond to raw "keyup" event.
|
|
101
74
|
*/
|
|
102
|
-
onKeyUp?: (
|
|
75
|
+
onKeyUp?: (e: React.KeyboardEvent) => unknown;
|
|
103
76
|
/**
|
|
104
77
|
* Don't show the default focus ring. This should be used when implementing
|
|
105
78
|
* a custom focus ring within your own component that uses Clickable.
|
|
106
79
|
*/
|
|
107
|
-
hideDefaultFocusRing?: boolean
|
|
108
|
-
/**
|
|
109
|
-
* A target destination window for a link to open in.
|
|
110
|
-
*
|
|
111
|
-
* TODO(WB-1262): only allow this prop when `href` is also set.t
|
|
112
|
-
*/
|
|
113
|
-
target?: "_blank" | undefined;
|
|
80
|
+
hideDefaultFocusRing?: boolean;
|
|
114
81
|
/**
|
|
115
82
|
* Set the tabindex attribute on the rendered element.
|
|
116
83
|
*/
|
|
117
|
-
tabIndex?: number
|
|
84
|
+
tabIndex?: number;
|
|
118
85
|
/**
|
|
119
86
|
* Run async code before navigating. If the promise returned rejects then
|
|
120
87
|
* navigation will not occur.
|
|
@@ -125,13 +92,104 @@ declare const Clickable: React.ForwardRefExoticComponent<Partial<Omit<AriaProps,
|
|
|
125
92
|
* WARNING: This prop must be used with `href` and should not be used with
|
|
126
93
|
* `target="blank"`.
|
|
127
94
|
*/
|
|
128
|
-
beforeNav?: (
|
|
95
|
+
beforeNav?: () => Promise<unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* Run async code in the background while client-side navigating. If the
|
|
98
|
+
* browser does a full page load navigation, the callback promise must be
|
|
99
|
+
* settled before the navigation will occur. Errors are ignored so that
|
|
100
|
+
* navigation is guaranteed to succeed.
|
|
101
|
+
*/
|
|
102
|
+
safeWithNav?: () => Promise<unknown>;
|
|
103
|
+
};
|
|
104
|
+
type Props = (CommonProps & {
|
|
105
|
+
target?: never;
|
|
106
|
+
beforeNav?: never;
|
|
107
|
+
safeWithNav?: never;
|
|
108
|
+
}) | (CommonProps & {
|
|
109
|
+
href: string;
|
|
110
|
+
/**
|
|
111
|
+
* A target destination window for a link to open in.
|
|
112
|
+
*/
|
|
113
|
+
target?: "_blank";
|
|
114
|
+
beforeNav?: never;
|
|
115
|
+
safeWithNav?: never;
|
|
116
|
+
}) | (CommonProps & {
|
|
117
|
+
href: string;
|
|
118
|
+
/**
|
|
119
|
+
* Run async code before navigating. If the promise returned rejects then
|
|
120
|
+
* navigation will not occur.
|
|
121
|
+
*
|
|
122
|
+
* If both safeWithNav and beforeNav are provided, beforeNav will be run
|
|
123
|
+
* first and safeWithNav will only be run if beforeNav does not reject.
|
|
124
|
+
*/
|
|
125
|
+
beforeNav: () => Promise<unknown>;
|
|
126
|
+
safeWithNav?: never;
|
|
127
|
+
target?: never;
|
|
128
|
+
}) | (CommonProps & {
|
|
129
|
+
href: string;
|
|
129
130
|
/**
|
|
130
131
|
* Run async code in the background while client-side navigating. If the
|
|
131
132
|
* browser does a full page load navigation, the callback promise must be
|
|
132
133
|
* settled before the navigation will occur. Errors are ignored so that
|
|
133
134
|
* navigation is guaranteed to succeed.
|
|
134
135
|
*/
|
|
135
|
-
safeWithNav
|
|
136
|
-
|
|
136
|
+
safeWithNav: () => Promise<unknown>;
|
|
137
|
+
/**
|
|
138
|
+
* A target destination window for a link to open in.
|
|
139
|
+
*/
|
|
140
|
+
target?: "_blank";
|
|
141
|
+
beforeNav?: never;
|
|
142
|
+
}) | (CommonProps & {
|
|
143
|
+
href: string;
|
|
144
|
+
/**
|
|
145
|
+
* Run async code before navigating. If the promise returned rejects then
|
|
146
|
+
* navigation will not occur.
|
|
147
|
+
*
|
|
148
|
+
* If both safeWithNav and beforeNav are provided, beforeNav will be run
|
|
149
|
+
* first and safeWithNav will only be run if beforeNav does not reject.
|
|
150
|
+
*/
|
|
151
|
+
beforeNav: () => Promise<unknown>;
|
|
152
|
+
/**
|
|
153
|
+
* Run async code in the background while client-side navigating. If the
|
|
154
|
+
* browser does a full page load navigation, the callback promise must be
|
|
155
|
+
* settled before the navigation will occur. Errors are ignored so that
|
|
156
|
+
* navigation is guaranteed to succeed.
|
|
157
|
+
*/
|
|
158
|
+
safeWithNav: () => Promise<unknown>;
|
|
159
|
+
target?: never;
|
|
160
|
+
});
|
|
161
|
+
/**
|
|
162
|
+
* A component to turn any custom component into a clickable one.
|
|
163
|
+
*
|
|
164
|
+
* Works by wrapping `ClickableBehavior` around the child element and styling
|
|
165
|
+
* the child appropriately and encapsulates routing logic which can be
|
|
166
|
+
* customized. Expects a function which returns an element as its child.
|
|
167
|
+
*
|
|
168
|
+
* Clickable allows your components to:
|
|
169
|
+
*
|
|
170
|
+
* - Handle mouse / touch / keyboard events
|
|
171
|
+
* - Match the standard behavior of the given role
|
|
172
|
+
* - Apply custom styles based on pressed / focused / hovered state
|
|
173
|
+
* - Perform Client Side Navigation when href is passed and the component is a
|
|
174
|
+
* descendent of a react-router Router.
|
|
175
|
+
*
|
|
176
|
+
* ### Usage
|
|
177
|
+
*
|
|
178
|
+
* ```jsx
|
|
179
|
+
* <Clickable onClick={() => alert("You clicked me!")}>
|
|
180
|
+
* {({hovered, focused, pressed}) =>
|
|
181
|
+
* <div
|
|
182
|
+
* style={[
|
|
183
|
+
* hovered && styles.hovered,
|
|
184
|
+
* focused && styles.focused,
|
|
185
|
+
* pressed && styles.pressed,
|
|
186
|
+
* ]}
|
|
187
|
+
* >
|
|
188
|
+
* Click Me!
|
|
189
|
+
* </div>
|
|
190
|
+
* }
|
|
191
|
+
* </Clickable>
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare const Clickable: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement | typeof Link>>;
|
|
137
195
|
export default Clickable;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-clickable",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"description": "Clickable component for Wonder-Blocks.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@babel/runtime": "^7.18.6",
|
|
19
19
|
"@khanacademy/wonder-blocks-color": "^2.0.1",
|
|
20
|
-
"@khanacademy/wonder-blocks-core": "^
|
|
20
|
+
"@khanacademy/wonder-blocks-core": "^6.0.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"aphrodite": "^1.2.5",
|
|
@@ -27,6 +27,6 @@
|
|
|
27
27
|
"react-router-dom": "5.3.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"wb-dev-build-settings": "^0.
|
|
30
|
+
"@khanacademy/wb-dev-build-settings": "^1.0.0"
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import ClickableBehavior from "../clickable-behavior";
|
|
4
|
+
|
|
5
|
+
<ClickableBehavior>
|
|
6
|
+
{(_, childrenProps) => <div {...childrenProps} />}
|
|
7
|
+
</ClickableBehavior>;
|
|
8
|
+
|
|
9
|
+
<ClickableBehavior target="_blank">
|
|
10
|
+
{(_, childrenProps) => <div {...childrenProps} />}
|
|
11
|
+
</ClickableBehavior>;
|
|
12
|
+
|
|
13
|
+
<ClickableBehavior beforeNav={() => Promise.resolve()}>
|
|
14
|
+
{(_, childrenProps) => <div {...childrenProps} />}
|
|
15
|
+
</ClickableBehavior>;
|
|
16
|
+
|
|
17
|
+
// @ts-expect-error `target` and `beforeNav` can't be used together.
|
|
18
|
+
<ClickableBehavior target="_blank" beforeNav={() => Promise.resolve()}>
|
|
19
|
+
{(_, childrenProps) => <div {...childrenProps} />}
|
|
20
|
+
</ClickableBehavior>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import Clickable from "../clickable";
|
|
4
|
+
|
|
5
|
+
<Clickable>{(_) => "Hello, world!"}</Clickable>;
|
|
6
|
+
|
|
7
|
+
<Clickable href="/foo">{(_) => "Hello, world!"}</Clickable>;
|
|
8
|
+
|
|
9
|
+
<Clickable href="/foo" target="_blank">
|
|
10
|
+
{(_) => "Hello, world!"}
|
|
11
|
+
</Clickable>;
|
|
12
|
+
|
|
13
|
+
// @ts-expect-error - `href` must be set when using `target="_blank"`.
|
|
14
|
+
<Clickable target="_blank">{(_) => "Hello, world!"}</Clickable>;
|
|
15
|
+
|
|
16
|
+
<Clickable href="/foo" beforeNav={() => Promise.resolve()}>
|
|
17
|
+
{(_) => "Hello, world!"}
|
|
18
|
+
</Clickable>;
|
|
19
|
+
|
|
20
|
+
<Clickable href="/foo" target="_blank" safeWithNav={() => Promise.resolve()}>
|
|
21
|
+
{(_) => "Hello, world!"}
|
|
22
|
+
</Clickable>;
|
|
23
|
+
|
|
24
|
+
// @ts-expect-error - `target="_blank"` cannot be used with `beforeNav`.
|
|
25
|
+
<Clickable href="/foo" target="_blank" beforeNav={() => Promise.resolve()}>
|
|
26
|
+
{(_) => "Hello, world!"}
|
|
27
|
+
</Clickable>;
|
|
28
|
+
|
|
29
|
+
// @ts-expect-error - `target="_blank"` cannot be used with `beforeNav`.
|
|
30
|
+
<Clickable
|
|
31
|
+
href="/foo"
|
|
32
|
+
target="_blank"
|
|
33
|
+
beforeNav={() => Promise.resolve()}
|
|
34
|
+
safeWithNav={() => Promise.resolve()}
|
|
35
|
+
>
|
|
36
|
+
{(_) => "Hello, world!"}
|
|
37
|
+
</Clickable>;
|
|
38
|
+
|
|
39
|
+
<Clickable
|
|
40
|
+
href="/foo"
|
|
41
|
+
beforeNav={() => Promise.resolve()}
|
|
42
|
+
safeWithNav={() => Promise.resolve()}
|
|
43
|
+
>
|
|
44
|
+
{(_) => "Hello, world!"}
|
|
45
|
+
</Clickable>;
|
|
@@ -3,13 +3,14 @@ import * as React from "react";
|
|
|
3
3
|
// NOTE: Potentially add to this as more cases come up.
|
|
4
4
|
export type ClickableRole =
|
|
5
5
|
| "button"
|
|
6
|
-
| "link"
|
|
7
6
|
| "checkbox"
|
|
8
|
-
| "
|
|
7
|
+
| "link"
|
|
9
8
|
| "listbox"
|
|
10
|
-
| "option"
|
|
11
|
-
| "menuitem"
|
|
12
9
|
| "menu"
|
|
10
|
+
| "menuitem"
|
|
11
|
+
| "option"
|
|
12
|
+
| "radio"
|
|
13
|
+
| "switch"
|
|
13
14
|
| "tab";
|
|
14
15
|
|
|
15
16
|
const getAppropriateTriggersForRole = (role?: ClickableRole | null) => {
|
|
@@ -41,8 +42,7 @@ const getAppropriateTriggersForRole = (role?: ClickableRole | null) => {
|
|
|
41
42
|
}
|
|
42
43
|
};
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
type Props = Readonly<{
|
|
45
|
+
type CommonProps = Readonly<{
|
|
46
46
|
/**
|
|
47
47
|
* A function that returns the a React `Element`.
|
|
48
48
|
*
|
|
@@ -119,28 +119,40 @@ type Props = Readonly<{
|
|
|
119
119
|
* Respond to raw "keyup" event.
|
|
120
120
|
*/
|
|
121
121
|
onKeyUp?: (e: React.KeyboardEvent) => unknown;
|
|
122
|
-
/**
|
|
123
|
-
* A target destination window for a link to open in. Should only be used
|
|
124
|
-
* when `href` is specified.
|
|
125
|
-
*/
|
|
126
|
-
// TODO(WB-1262): only allow this prop when `href` is also set.
|
|
127
|
-
target?: "_blank";
|
|
128
|
-
/**
|
|
129
|
-
* Run async code before navigating to the URL passed to `href`. If the
|
|
130
|
-
* promise returned rejects then navigation will not occur.
|
|
131
|
-
*
|
|
132
|
-
* If both safeWithNav and beforeNav are provided, beforeNav will be run
|
|
133
|
-
* first and safeWithNav will only be run if beforeNav does not reject.
|
|
134
|
-
*
|
|
135
|
-
* WARNING: Using this with `target="_blank"` will trigger built-in popup
|
|
136
|
-
* blockers in Firefox and Safari. This is because we do navigation
|
|
137
|
-
* programmatically and `beforeNav` causes a delay which means that the
|
|
138
|
-
* browser can't make a directly link between a user action and the
|
|
139
|
-
* navigation.
|
|
140
|
-
*/
|
|
141
|
-
beforeNav?: () => Promise<unknown>;
|
|
142
122
|
}>;
|
|
143
123
|
|
|
124
|
+
type Props =
|
|
125
|
+
| (CommonProps &
|
|
126
|
+
Readonly<{
|
|
127
|
+
/**
|
|
128
|
+
* A target destination window for a link to open in. Should only be used
|
|
129
|
+
* when `href` is specified.
|
|
130
|
+
*/
|
|
131
|
+
// TODO(WB-1262): only allow this prop when `href` is also set.
|
|
132
|
+
target?: "_blank";
|
|
133
|
+
|
|
134
|
+
beforeNav?: never; // disallow beforeNav when target="_blank"
|
|
135
|
+
}>)
|
|
136
|
+
| (CommonProps &
|
|
137
|
+
Readonly<{
|
|
138
|
+
/**
|
|
139
|
+
* Run async code before navigating to the URL passed to `href`. If the
|
|
140
|
+
* promise returned rejects then navigation will not occur.
|
|
141
|
+
*
|
|
142
|
+
* If both safeWithNav and beforeNav are provided, beforeNav will be run
|
|
143
|
+
* first and safeWithNav will only be run if beforeNav does not reject.
|
|
144
|
+
*
|
|
145
|
+
* WARNING: Using this with `target="_blank"` will trigger built-in popup
|
|
146
|
+
* blockers in Firefox and Safari. This is because we do navigation
|
|
147
|
+
* programmatically and `beforeNav` causes a delay which means that the
|
|
148
|
+
* browser can't make a directly link between a user action and the
|
|
149
|
+
* navigation.
|
|
150
|
+
*/
|
|
151
|
+
beforeNav?: () => Promise<unknown>;
|
|
152
|
+
|
|
153
|
+
target?: never; // disallow target="_blank" when beforeNav is set
|
|
154
|
+
}>);
|
|
155
|
+
|
|
144
156
|
export type ClickableState = Readonly<{
|
|
145
157
|
/**
|
|
146
158
|
* Whether the component is hovered.
|
|
@@ -11,8 +11,7 @@ import getClickableBehavior from "../util/get-clickable-behavior";
|
|
|
11
11
|
import type {ClickableRole, ClickableState} from "./clickable-behavior";
|
|
12
12
|
import {isClientSideUrl} from "../util/is-client-side-url";
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
type Props =
|
|
14
|
+
type CommonProps =
|
|
16
15
|
/**
|
|
17
16
|
* aria-label should be used when `spinner={true}` to let people using screen
|
|
18
17
|
* readers that the action taken by clicking the button will take some
|
|
@@ -24,7 +23,7 @@ type Props =
|
|
|
24
23
|
* which should be made Clickable. The function is passed an object with
|
|
25
24
|
* three boolean properties: hovered, focused, and pressed.
|
|
26
25
|
*/
|
|
27
|
-
children: (
|
|
26
|
+
children: (clickableState: ClickableState) => React.ReactNode;
|
|
28
27
|
/**
|
|
29
28
|
* An onClick function which Clickable can execute when clicked
|
|
30
29
|
*/
|
|
@@ -88,12 +87,6 @@ type Props =
|
|
|
88
87
|
* a custom focus ring within your own component that uses Clickable.
|
|
89
88
|
*/
|
|
90
89
|
hideDefaultFocusRing?: boolean;
|
|
91
|
-
/**
|
|
92
|
-
* A target destination window for a link to open in.
|
|
93
|
-
*
|
|
94
|
-
* TODO(WB-1262): only allow this prop when `href` is also set.t
|
|
95
|
-
*/
|
|
96
|
-
target?: "_blank";
|
|
97
90
|
/**
|
|
98
91
|
* Set the tabindex attribute on the rendered element.
|
|
99
92
|
*/
|
|
@@ -118,6 +111,79 @@ type Props =
|
|
|
118
111
|
safeWithNav?: () => Promise<unknown>;
|
|
119
112
|
};
|
|
120
113
|
|
|
114
|
+
type Props =
|
|
115
|
+
| (CommonProps & {
|
|
116
|
+
target?: never;
|
|
117
|
+
beforeNav?: never;
|
|
118
|
+
safeWithNav?: never;
|
|
119
|
+
})
|
|
120
|
+
| (CommonProps & {
|
|
121
|
+
href: string;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A target destination window for a link to open in.
|
|
125
|
+
*/
|
|
126
|
+
target?: "_blank";
|
|
127
|
+
|
|
128
|
+
beforeNav?: never;
|
|
129
|
+
safeWithNav?: never;
|
|
130
|
+
})
|
|
131
|
+
| (CommonProps & {
|
|
132
|
+
href: string;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Run async code before navigating. If the promise returned rejects then
|
|
136
|
+
* navigation will not occur.
|
|
137
|
+
*
|
|
138
|
+
* If both safeWithNav and beforeNav are provided, beforeNav will be run
|
|
139
|
+
* first and safeWithNav will only be run if beforeNav does not reject.
|
|
140
|
+
*/
|
|
141
|
+
beforeNav: () => Promise<unknown>;
|
|
142
|
+
|
|
143
|
+
safeWithNav?: never;
|
|
144
|
+
target?: never;
|
|
145
|
+
})
|
|
146
|
+
| (CommonProps & {
|
|
147
|
+
href: string;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Run async code in the background while client-side navigating. If the
|
|
151
|
+
* browser does a full page load navigation, the callback promise must be
|
|
152
|
+
* settled before the navigation will occur. Errors are ignored so that
|
|
153
|
+
* navigation is guaranteed to succeed.
|
|
154
|
+
*/
|
|
155
|
+
safeWithNav: () => Promise<unknown>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A target destination window for a link to open in.
|
|
159
|
+
*/
|
|
160
|
+
target?: "_blank";
|
|
161
|
+
|
|
162
|
+
beforeNav?: never;
|
|
163
|
+
})
|
|
164
|
+
| (CommonProps & {
|
|
165
|
+
href: string;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Run async code before navigating. If the promise returned rejects then
|
|
169
|
+
* navigation will not occur.
|
|
170
|
+
*
|
|
171
|
+
* If both safeWithNav and beforeNav are provided, beforeNav will be run
|
|
172
|
+
* first and safeWithNav will only be run if beforeNav does not reject.
|
|
173
|
+
*/
|
|
174
|
+
beforeNav: () => Promise<unknown>;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Run async code in the background while client-side navigating. If the
|
|
178
|
+
* browser does a full page load navigation, the callback promise must be
|
|
179
|
+
* settled before the navigation will occur. Errors are ignored so that
|
|
180
|
+
* navigation is guaranteed to succeed.
|
|
181
|
+
*/
|
|
182
|
+
safeWithNav: () => Promise<unknown>;
|
|
183
|
+
|
|
184
|
+
target?: never;
|
|
185
|
+
});
|
|
186
|
+
|
|
121
187
|
const StyledAnchor = addStyle("a");
|
|
122
188
|
const StyledButton = addStyle("button");
|
|
123
189
|
const StyledLink = addStyle(Link);
|