@hyphen/hyphen-components 3.0.0 → 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/dist/components/Button/Button.d.ts +12 -51
- package/dist/components/Button/Button.stories.d.ts +1 -1
- package/dist/components/Drawer/Drawer.d.ts +2 -8
- package/dist/components/Sidebar/Sidebar.d.ts +2 -6
- package/dist/css/index.css +1 -1
- package/dist/hyphen-components.cjs.development.js +41 -104
- package/dist/hyphen-components.cjs.development.js.map +1 -1
- package/dist/hyphen-components.cjs.production.min.js +1 -1
- package/dist/hyphen-components.cjs.production.min.js.map +1 -1
- package/dist/hyphen-components.esm.js +42 -105
- package/dist/hyphen-components.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Button/Button.mdx +7 -7
- package/src/components/Button/Button.module.scss +3 -0
- package/src/components/Button/Button.stories.tsx +12 -12
- package/src/components/Button/Button.test.tsx +128 -112
- package/src/components/Button/Button.tsx +80 -178
- package/src/components/Drawer/Drawer.tsx +8 -12
- package/src/components/Sidebar/Sidebar.stories.tsx +4 -4
- package/src/lib/tokens.ts +11 -8
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import { BoxShadowSize, IconName, ResponsiveProp } from '../../types';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
ButtonHTMLAttributes,
|
|
5
|
-
FocusEvent,
|
|
6
|
-
MouseEvent,
|
|
7
|
-
ReactNode,
|
|
8
|
-
createElement,
|
|
9
|
-
forwardRef,
|
|
10
|
-
} from 'react';
|
|
2
|
+
import { Slot, Slottable } from '@radix-ui/react-slot';
|
|
3
|
+
import React, { ButtonHTMLAttributes, forwardRef } from 'react';
|
|
11
4
|
|
|
12
|
-
import { Box } from '../Box/Box';
|
|
13
5
|
import { Icon } from '../Icon/Icon';
|
|
14
6
|
import { Spinner } from '../Spinner/Spinner';
|
|
15
7
|
import classNames from 'classnames';
|
|
16
8
|
import { generateResponsiveClasses } from '../../lib/generateResponsiveClasses';
|
|
17
|
-
import { getElementType } from '../../lib/getElementType';
|
|
18
|
-
import { handleReactRouterClick } from '../../lib/reactRouterClickHandler';
|
|
19
9
|
import styles from './Button.module.scss';
|
|
20
10
|
|
|
21
11
|
export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'danger';
|
|
@@ -24,9 +14,9 @@ export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
|
24
14
|
|
|
25
15
|
export interface BaseButtonProps {
|
|
26
16
|
/**
|
|
27
|
-
*
|
|
17
|
+
* The button element to render as. Useful for when you want to render a Link that looks like a button.
|
|
28
18
|
*/
|
|
29
|
-
|
|
19
|
+
asChild?: boolean;
|
|
30
20
|
/**
|
|
31
21
|
* Additional ClassNames to add to button.
|
|
32
22
|
*/
|
|
@@ -36,116 +26,54 @@ export interface BaseButtonProps {
|
|
|
36
26
|
*/
|
|
37
27
|
fullWidth?: boolean;
|
|
38
28
|
/**
|
|
39
|
-
*
|
|
29
|
+
* Icon displayed before the button label
|
|
40
30
|
*/
|
|
41
31
|
iconPrefix?: IconName;
|
|
42
32
|
/**
|
|
43
|
-
*
|
|
33
|
+
* Icon displayed after the button label
|
|
44
34
|
*/
|
|
45
35
|
iconSuffix?: IconName;
|
|
46
36
|
/**
|
|
47
|
-
*
|
|
48
|
-
*/
|
|
49
|
-
id?: string;
|
|
50
|
-
/**
|
|
51
|
-
* URL to navigate to when clicked. Passing this attribute automatically
|
|
52
|
-
* renders an anchor <a> tag, NOT a <button> element.
|
|
53
|
-
*/
|
|
54
|
-
href?: string;
|
|
55
|
-
/**
|
|
56
|
-
* Disables the button, making it inoperable.
|
|
37
|
+
* Disables the button
|
|
57
38
|
*/
|
|
58
39
|
isDisabled?: boolean;
|
|
59
40
|
/**
|
|
60
|
-
*
|
|
41
|
+
* Displays a loading spinner and disables the button
|
|
61
42
|
*/
|
|
62
43
|
isLoading?: boolean;
|
|
63
44
|
/**
|
|
64
|
-
*
|
|
65
|
-
*/
|
|
66
|
-
navigate?: () => void;
|
|
67
|
-
/**
|
|
68
|
-
* Callback when Button is pressed.
|
|
69
|
-
*/
|
|
70
|
-
onClick?: (event: MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void;
|
|
71
|
-
/**
|
|
72
|
-
* Callback when focus leaves Button.
|
|
73
|
-
*/
|
|
74
|
-
onBlur?: (event: FocusEvent<HTMLButtonElement | HTMLAnchorElement>) => void;
|
|
75
|
-
/**
|
|
76
|
-
* Callback when Button receives focus.
|
|
77
|
-
*/
|
|
78
|
-
onFocus?: (event: FocusEvent<HTMLButtonElement | HTMLAnchorElement>) => void;
|
|
79
|
-
/**
|
|
80
|
-
* The size of the drop shadow applied to the Box
|
|
45
|
+
* Size of the drop shadow applied to the Box
|
|
81
46
|
*/
|
|
82
47
|
shadow?: BoxShadowSize | ResponsiveProp<BoxShadowSize>;
|
|
83
48
|
/**
|
|
84
|
-
*
|
|
85
|
-
*/
|
|
86
|
-
tabIndex?: number;
|
|
87
|
-
/**
|
|
88
|
-
* Useful when using button as an anchor tag.
|
|
89
|
-
*/
|
|
90
|
-
target?: AnchorHTMLAttributes<HTMLAnchorElement>['target'];
|
|
91
|
-
/**
|
|
92
|
-
* The size of the button.
|
|
49
|
+
* Size of the button.
|
|
93
50
|
*/
|
|
94
51
|
size?: ButtonSize | ResponsiveProp<ButtonSize>;
|
|
95
52
|
/**
|
|
96
|
-
*
|
|
53
|
+
* Visual variant of the button
|
|
97
54
|
*/
|
|
98
55
|
variant?: ButtonVariant;
|
|
99
|
-
/**
|
|
100
|
-
* ref - currently cannot be typed due to limitations of using the `as` prop
|
|
101
|
-
*/
|
|
102
56
|
}
|
|
103
57
|
|
|
104
|
-
export type
|
|
105
|
-
|
|
106
|
-
React.DetailedHTMLProps<
|
|
107
|
-
AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
108
|
-
HTMLAnchorElement
|
|
109
|
-
>,
|
|
110
|
-
'ref'
|
|
111
|
-
>;
|
|
112
|
-
|
|
113
|
-
export type NormalButtonProps = { as?: 'button' } & BaseButtonProps &
|
|
114
|
-
Omit<
|
|
115
|
-
React.DetailedHTMLProps<
|
|
116
|
-
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
117
|
-
HTMLButtonElement
|
|
118
|
-
>,
|
|
119
|
-
'ref'
|
|
120
|
-
>;
|
|
58
|
+
export type ButtonMergedProps = BaseButtonProps &
|
|
59
|
+
ButtonHTMLAttributes<HTMLButtonElement>;
|
|
121
60
|
|
|
122
|
-
export
|
|
123
|
-
|
|
124
|
-
export const Button = forwardRef<
|
|
125
|
-
HTMLAnchorElement | HTMLButtonElement,
|
|
126
|
-
ButtonProps
|
|
127
|
-
>(
|
|
61
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonMergedProps>(
|
|
128
62
|
(
|
|
129
63
|
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
className
|
|
133
|
-
fullWidth
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
onFocus = undefined,
|
|
143
|
-
onBlur = undefined,
|
|
144
|
-
shadow = undefined,
|
|
64
|
+
asChild,
|
|
65
|
+
children,
|
|
66
|
+
className,
|
|
67
|
+
fullWidth,
|
|
68
|
+
iconPrefix,
|
|
69
|
+
iconSuffix,
|
|
70
|
+
isDisabled,
|
|
71
|
+
isLoading,
|
|
72
|
+
onClick,
|
|
73
|
+
onBlur,
|
|
74
|
+
onFocus,
|
|
75
|
+
shadow,
|
|
145
76
|
size = 'md',
|
|
146
|
-
tabIndex = undefined,
|
|
147
|
-
target = undefined,
|
|
148
|
-
type = undefined,
|
|
149
77
|
variant = 'primary',
|
|
150
78
|
...restProps
|
|
151
79
|
},
|
|
@@ -153,9 +81,9 @@ export const Button = forwardRef<
|
|
|
153
81
|
) => {
|
|
154
82
|
const disabled = isLoading || isDisabled;
|
|
155
83
|
|
|
156
|
-
const responsiveClasses = generateResponsiveClasses('size', size)
|
|
157
|
-
(c) => styles[c]
|
|
158
|
-
|
|
84
|
+
const responsiveClasses = generateResponsiveClasses('size', size)
|
|
85
|
+
.map((c) => styles[c])
|
|
86
|
+
.filter(Boolean);
|
|
159
87
|
|
|
160
88
|
const buttonClasses = classNames(
|
|
161
89
|
'hyphen-components__variables__form-control',
|
|
@@ -170,83 +98,57 @@ export const Button = forwardRef<
|
|
|
170
98
|
}
|
|
171
99
|
);
|
|
172
100
|
|
|
173
|
-
const handleClick =
|
|
174
|
-
|
|
175
|
-
const handleFocus =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
</>
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const buttonElement = getElementType(Button, { as });
|
|
226
|
-
|
|
227
|
-
return createElement(
|
|
228
|
-
buttonElement,
|
|
229
|
-
{
|
|
230
|
-
['aria-disabled']: disabled,
|
|
231
|
-
id,
|
|
232
|
-
href,
|
|
233
|
-
className: buttonClasses,
|
|
234
|
-
disabled,
|
|
235
|
-
target: as === 'a' && href ? target : null,
|
|
236
|
-
rel:
|
|
237
|
-
as === 'a' && href && target === '_blank'
|
|
238
|
-
? 'noopener noreferrer'
|
|
239
|
-
: null,
|
|
240
|
-
onBlur: handleBlur,
|
|
241
|
-
onClick: (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) =>
|
|
242
|
-
handleClick(event, onClick, target, navigate),
|
|
243
|
-
onFocus: handleFocus,
|
|
244
|
-
ref,
|
|
245
|
-
type: type || (as !== 'a' && !href ? 'button' : undefined),
|
|
246
|
-
tabIndex,
|
|
247
|
-
...restProps,
|
|
248
|
-
},
|
|
249
|
-
buttonContent
|
|
101
|
+
const handleClick = !disabled ? onClick : undefined;
|
|
102
|
+
const handleBlur = !disabled ? onBlur : undefined;
|
|
103
|
+
const handleFocus = !disabled ? onFocus : undefined;
|
|
104
|
+
|
|
105
|
+
const Comp = asChild ? Slot : 'button';
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Comp
|
|
109
|
+
{...(disabled && { 'aria-disabled': true })}
|
|
110
|
+
disabled={disabled}
|
|
111
|
+
className={buttonClasses}
|
|
112
|
+
onClick={handleClick}
|
|
113
|
+
onBlur={handleBlur}
|
|
114
|
+
onFocus={handleFocus}
|
|
115
|
+
ref={ref}
|
|
116
|
+
{...restProps}
|
|
117
|
+
>
|
|
118
|
+
{isLoading && <Spinner className={styles['spinner-wrapper']} />}
|
|
119
|
+
{iconPrefix && (
|
|
120
|
+
<Icon
|
|
121
|
+
className={styles.label}
|
|
122
|
+
name={iconPrefix}
|
|
123
|
+
aria-hidden="true"
|
|
124
|
+
focusable="false"
|
|
125
|
+
data-testid="prefixIcon"
|
|
126
|
+
size={size === 'md' ? 'sm' : size}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
{children && (
|
|
130
|
+
<Slottable>
|
|
131
|
+
{asChild ? (
|
|
132
|
+
children
|
|
133
|
+
) : (
|
|
134
|
+
<span className={styles.label}>{children}</span>
|
|
135
|
+
)}
|
|
136
|
+
</Slottable>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
{iconSuffix && (
|
|
140
|
+
<Icon
|
|
141
|
+
className={styles.label}
|
|
142
|
+
name={iconSuffix}
|
|
143
|
+
aria-hidden="true"
|
|
144
|
+
focusable="false"
|
|
145
|
+
data-testid="suffixIcon"
|
|
146
|
+
size={size === 'md' ? 'sm' : size}
|
|
147
|
+
/>
|
|
148
|
+
)}
|
|
149
|
+
</Comp>
|
|
250
150
|
);
|
|
251
151
|
}
|
|
252
152
|
);
|
|
153
|
+
|
|
154
|
+
Button.displayName = 'Button';
|
|
@@ -16,7 +16,7 @@ import classNames from 'classnames';
|
|
|
16
16
|
import { DimensionSize, CssDimensionValue } from '../../types';
|
|
17
17
|
import { Box, BoxProps } from '../Box/Box';
|
|
18
18
|
import styles from './Drawer.module.scss';
|
|
19
|
-
import { Button } from '../Button/Button';
|
|
19
|
+
import { Button, ButtonMergedProps } from '../Button/Button';
|
|
20
20
|
|
|
21
21
|
interface DrawerContextProps {
|
|
22
22
|
open: boolean;
|
|
@@ -354,18 +354,14 @@ const DrawerTitle = React.forwardRef<HTMLDivElement, BoxProps>(
|
|
|
354
354
|
}
|
|
355
355
|
);
|
|
356
356
|
|
|
357
|
-
const DrawerCloseButton =
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
>(({ className, onClick, onClose, ...props }, ref) => {
|
|
357
|
+
const DrawerCloseButton = forwardRef<
|
|
358
|
+
HTMLButtonElement,
|
|
359
|
+
ButtonMergedProps & { onClose?: () => void }
|
|
360
|
+
>(({ className, onClick, onClose, ...rest }, ref) => {
|
|
363
361
|
const context = useContext(DrawerContext);
|
|
364
362
|
const isStandalone = !context;
|
|
365
363
|
|
|
366
|
-
const handleClick = (
|
|
367
|
-
event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>
|
|
368
|
-
) => {
|
|
364
|
+
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
369
365
|
onClick?.(event);
|
|
370
366
|
|
|
371
367
|
if (isStandalone) {
|
|
@@ -379,7 +375,6 @@ const DrawerCloseButton = React.forwardRef<
|
|
|
379
375
|
|
|
380
376
|
return (
|
|
381
377
|
<Button
|
|
382
|
-
ref={ref}
|
|
383
378
|
variant="tertiary"
|
|
384
379
|
aria-label="close"
|
|
385
380
|
type="button"
|
|
@@ -388,7 +383,8 @@ const DrawerCloseButton = React.forwardRef<
|
|
|
388
383
|
className={classNames('m-left-auto', className)}
|
|
389
384
|
size="sm"
|
|
390
385
|
onClick={handleClick}
|
|
391
|
-
{
|
|
386
|
+
ref={ref}
|
|
387
|
+
{...rest}
|
|
392
388
|
/>
|
|
393
389
|
);
|
|
394
390
|
});
|
|
@@ -220,7 +220,7 @@ export const SidebarExample = () => {
|
|
|
220
220
|
<DropdownMenuSeparator />
|
|
221
221
|
<DropdownMenuItem>
|
|
222
222
|
<a
|
|
223
|
-
href="
|
|
223
|
+
href="https://ux.hyphen.ai"
|
|
224
224
|
className="display-flex flex-direction-row g-sm align-items-center"
|
|
225
225
|
>
|
|
226
226
|
<Icon name="add" color="tertiary" />
|
|
@@ -313,14 +313,14 @@ export const SidebarExample = () => {
|
|
|
313
313
|
</DropdownMenuTrigger>
|
|
314
314
|
<DropdownMenuContent side="bottom" align="end">
|
|
315
315
|
<DropdownMenuItem>
|
|
316
|
-
<a href="
|
|
316
|
+
<a href="https://ux.hyphen.ai">View</a>
|
|
317
317
|
</DropdownMenuItem>
|
|
318
318
|
<DropdownMenuItem>
|
|
319
|
-
<a href="
|
|
319
|
+
<a href="https://ux.hyphen.ai">Share</a>
|
|
320
320
|
</DropdownMenuItem>
|
|
321
321
|
<DropdownMenuSeparator />
|
|
322
322
|
<DropdownMenuItem>
|
|
323
|
-
<a href="
|
|
323
|
+
<a href="https://ux.hyphen.ai">Remove</a>
|
|
324
324
|
</DropdownMenuItem>
|
|
325
325
|
</DropdownMenuContent>
|
|
326
326
|
</DropdownMenu>
|
package/src/lib/tokens.ts
CHANGED
|
@@ -44,14 +44,17 @@ export const BREAKPOINT_OPTIONS = Object.keys(
|
|
|
44
44
|
export const BREAKPOINTS = [
|
|
45
45
|
...Object.entries(designTokens.size.breakpoint),
|
|
46
46
|
['base', { value: '0' }],
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
47
|
+
]
|
|
48
|
+
.map(([name, data]) => {
|
|
49
|
+
if (typeof data === 'object' && data !== null && 'value' in data) {
|
|
50
|
+
return {
|
|
51
|
+
name,
|
|
52
|
+
minWidth: parseInt(data['value'] as string, 10),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
})
|
|
57
|
+
.filter((breakpoint): breakpoint is Breakpoint => breakpoint !== undefined);
|
|
55
58
|
|
|
56
59
|
export const BASE_COLOR_OPTIONS = (
|
|
57
60
|
Object.keys(designTokens.color.base) as ColorName[]
|