@dhis2-ui/button 10.16.1 → 10.16.3-alpha.1
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/package.json +8 -7
- package/src/button/__tests__/Button.test.js +126 -0
- package/src/button/button.e2e.stories.js +24 -0
- package/src/button/button.js +162 -0
- package/src/button/button.prod.stories.js +305 -0
- package/src/button/button.styles.js +298 -0
- package/src/button/features/can_be_blurred/index.js +19 -0
- package/src/button/features/can_be_blurred.feature +6 -0
- package/src/button/features/can_be_clicked/index.js +18 -0
- package/src/button/features/can_be_clicked.feature +6 -0
- package/src/button/features/can_be_focused/index.js +18 -0
- package/src/button/features/can_be_focused.feature +6 -0
- package/src/button/index.js +1 -0
- package/src/button-strip/button-strip.e2e.stories.js +13 -0
- package/src/button-strip/button-strip.js +58 -0
- package/src/button-strip/button-strip.prod.stories.js +88 -0
- package/src/button-strip/features/accepts_children/index.js +10 -0
- package/src/button-strip/features/accepts_children.feature +5 -0
- package/src/button-strip/index.js +1 -0
- package/src/dropdown-button/__tests__/dropdown-button.test.js +135 -0
- package/src/dropdown-button/dropdown-button.e2e.stories.js +67 -0
- package/src/dropdown-button/dropdown-button.js +239 -0
- package/src/dropdown-button/dropdown-button.prod.stories.js +129 -0
- package/src/dropdown-button/features/accepts_children/index.js +10 -0
- package/src/dropdown-button/features/accepts_children.feature +5 -0
- package/src/dropdown-button/features/accepts_component/index.js +18 -0
- package/src/dropdown-button/features/accepts_component.feature +5 -0
- package/src/dropdown-button/features/accepts_icon/index.js +10 -0
- package/src/dropdown-button/features/accepts_icon.feature +5 -0
- package/src/dropdown-button/features/accepts_initial_focus/index.js +11 -0
- package/src/dropdown-button/features/accepts_initial_focus.feature +5 -0
- package/src/dropdown-button/features/button_is_clickable/index.js +15 -0
- package/src/dropdown-button/features/button_is_clickable.feature +6 -0
- package/src/dropdown-button/features/can_be_disabled/index.js +11 -0
- package/src/dropdown-button/features/can_be_disabled.feature +6 -0
- package/src/dropdown-button/features/common/index.js +5 -0
- package/src/dropdown-button/features/opens_a_dropdown/index.js +26 -0
- package/src/dropdown-button/features/opens_a_dropdown.feature +11 -0
- package/src/dropdown-button/index.js +1 -0
- package/src/index.js +4 -0
- package/src/locales/en/translations.json +3 -0
- package/src/locales/index.js +16 -0
- package/src/split-button/features/accepts_children/index.js +12 -0
- package/src/split-button/features/accepts_children.feature +5 -0
- package/src/split-button/features/accepts_icon/index.js +16 -0
- package/src/split-button/features/accepts_icon.feature +5 -0
- package/src/split-button/features/accepts_initial_focus/index.js +13 -0
- package/src/split-button/features/accepts_initial_focus.feature +5 -0
- package/src/split-button/features/arrow_opens_menu/index.js +34 -0
- package/src/split-button/features/arrow_opens_menu.feature +15 -0
- package/src/split-button/features/button_is_clickable/index.js +13 -0
- package/src/split-button/features/button_is_clickable.feature +6 -0
- package/src/split-button/features/can_be_disabled/index.js +25 -0
- package/src/split-button/features/can_be_disabled.feature +11 -0
- package/src/split-button/features/common/index.js +9 -0
- package/src/split-button/index.js +1 -0
- package/src/split-button/split-button.e2e.stories.js +56 -0
- package/src/split-button/split-button.js +273 -0
- package/src/split-button/split-button.prod.stories.js +162 -0
- package/src/split-button/split-button.test.js +84 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dhis2-ui/button",
|
|
3
|
-
"version": "10.16.1",
|
|
3
|
+
"version": "10.16.3-alpha.1",
|
|
4
4
|
"description": "UI Button",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,17 +33,18 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@dhis2/prop-types": "^3.1.2",
|
|
36
|
-
"@dhis2-ui/layer": "10.16.1",
|
|
37
|
-
"@dhis2-ui/loader": "10.16.1",
|
|
38
|
-
"@dhis2-ui/popper": "10.16.1",
|
|
39
|
-
"@dhis2/ui-constants": "10.16.1",
|
|
40
|
-
"@dhis2/ui-icons": "10.16.1",
|
|
36
|
+
"@dhis2-ui/layer": "10.16.3-alpha.1",
|
|
37
|
+
"@dhis2-ui/loader": "10.16.3-alpha.1",
|
|
38
|
+
"@dhis2-ui/popper": "10.16.3-alpha.1",
|
|
39
|
+
"@dhis2/ui-constants": "10.16.3-alpha.1",
|
|
40
|
+
"@dhis2/ui-icons": "10.16.3-alpha.1",
|
|
41
41
|
"classnames": "^2.3.1",
|
|
42
42
|
"prop-types": "^15.7.2"
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
45
|
"build",
|
|
46
|
-
"types"
|
|
46
|
+
"types",
|
|
47
|
+
"src"
|
|
47
48
|
],
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"react": "^18.3.1",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { render, fireEvent, screen } from '@testing-library/react'
|
|
2
|
+
import { mount } from 'enzyme'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Button } from '../button.js'
|
|
5
|
+
|
|
6
|
+
describe('<Button>', () => {
|
|
7
|
+
let consoleSpy
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
consoleSpy = jest.spyOn(console, 'debug').mockImplementation()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
consoleSpy.mockRestore()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe('warning for missing aria-label and title', () => {
|
|
18
|
+
it('No warning if children exist but aria-label and title is missing', () => {
|
|
19
|
+
render(<Button>Children content</Button>)
|
|
20
|
+
|
|
21
|
+
expect(consoleSpy).not.toHaveBeenCalled()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('does not warn if aria-label and title is present', () => {
|
|
25
|
+
render(
|
|
26
|
+
<Button aria-label="Test" title="Test">
|
|
27
|
+
Children content
|
|
28
|
+
</Button>
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
expect(consoleSpy).not.toHaveBeenCalled()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('warns if no children are present with no arial-label and title', () => {
|
|
35
|
+
render(<Button>{/* No children */}</Button>)
|
|
36
|
+
|
|
37
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
38
|
+
'Button component has no children but is missing title and ariaLabel attribute.'
|
|
39
|
+
)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('No warning if there are no children but arial label and title', () => {
|
|
43
|
+
render(
|
|
44
|
+
<Button aria-label="Test" title="Test">
|
|
45
|
+
{/* No children */}
|
|
46
|
+
</Button>
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
expect(consoleSpy).not.toHaveBeenCalled()
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('renders a default data-test attribute', () => {
|
|
54
|
+
const dataTest = 'dhis2-uicore-button'
|
|
55
|
+
const wrapper = mount(<Button dataTest={dataTest} />)
|
|
56
|
+
|
|
57
|
+
const actual = wrapper.find({ 'data-test': dataTest })
|
|
58
|
+
|
|
59
|
+
expect(actual.length).toBe(1)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('renders a custom data-test attribute', () => {
|
|
63
|
+
const dataTest = 'button-data-test'
|
|
64
|
+
const wrapper = mount(<Button dataTest={dataTest} />)
|
|
65
|
+
|
|
66
|
+
const actual = wrapper.find({ 'data-test': dataTest })
|
|
67
|
+
|
|
68
|
+
expect(actual.length).toBe(1)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('has the accessibility attributes', () => {
|
|
72
|
+
const dataTest = 'button-data-test'
|
|
73
|
+
const wrapper = mount(
|
|
74
|
+
<Button
|
|
75
|
+
dataTest={dataTest}
|
|
76
|
+
ariaLabel="test aria label"
|
|
77
|
+
title="title for button"
|
|
78
|
+
/>
|
|
79
|
+
)
|
|
80
|
+
const buttonElement = wrapper.find('button').getDOMNode()
|
|
81
|
+
expect(buttonElement).toHaveAttribute('title', 'title for button')
|
|
82
|
+
expect(buttonElement).toHaveAttribute('ariaLabel', 'test aria label')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('toggle', () => {
|
|
86
|
+
it('should have class "toggled" if toggled-prop is true', () => {
|
|
87
|
+
const wrapper = mount(<Button toggled />)
|
|
88
|
+
|
|
89
|
+
const actual = wrapper.find('button')
|
|
90
|
+
expect(actual.hasClass('toggled')).toBe(true)
|
|
91
|
+
})
|
|
92
|
+
it('should not have class "toggled" if toggled-prop is not passed', () => {
|
|
93
|
+
const wrapper = mount(<Button />)
|
|
94
|
+
|
|
95
|
+
const actual = wrapper.find('button')
|
|
96
|
+
expect(actual.hasClass('toggled')).toBe(false)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should call the onKeyDown callback when provided', () => {
|
|
101
|
+
const onKeyDown = jest.fn()
|
|
102
|
+
|
|
103
|
+
render(
|
|
104
|
+
<Button
|
|
105
|
+
name="button-name"
|
|
106
|
+
value="button-value"
|
|
107
|
+
onKeyDown={onKeyDown}
|
|
108
|
+
>
|
|
109
|
+
btn
|
|
110
|
+
</Button>
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
fireEvent.keyDown(screen.getByRole('button'), {
|
|
114
|
+
key: 'Enter',
|
|
115
|
+
code: 'Enter',
|
|
116
|
+
charCode: 13,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
expect(onKeyDown).toHaveBeenCalledWith(
|
|
120
|
+
{ name: 'button-name', value: 'button-value' },
|
|
121
|
+
expect.objectContaining({})
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
expect(onKeyDown).toHaveBeenCalledTimes(1)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Button } from './button.js'
|
|
3
|
+
|
|
4
|
+
window.onClick = window.Cypress && window.Cypress.cy.stub()
|
|
5
|
+
window.onBlur = window.Cypress && window.Cypress.cy.stub()
|
|
6
|
+
window.onFocus = window.Cypress && window.Cypress.cy.stub()
|
|
7
|
+
|
|
8
|
+
export default { title: 'Button' }
|
|
9
|
+
|
|
10
|
+
export const WithOnClick = () => (
|
|
11
|
+
<Button name="Button" value="default" onClick={window.onClick}>
|
|
12
|
+
Label me!
|
|
13
|
+
</Button>
|
|
14
|
+
)
|
|
15
|
+
export const WithInitialFocusAndOnBlur = () => (
|
|
16
|
+
<Button name="Button" value="default" initialFocus onBlur={window.onBlur}>
|
|
17
|
+
Label me!
|
|
18
|
+
</Button>
|
|
19
|
+
)
|
|
20
|
+
export const WithOnFocus = () => (
|
|
21
|
+
<Button name="Button" value="default" onFocus={window.onFocus}>
|
|
22
|
+
Label me!
|
|
23
|
+
</Button>
|
|
24
|
+
)
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import { CircularLoader } from '@dhis2-ui/loader'
|
|
3
|
+
import cx from 'classnames'
|
|
4
|
+
import PropTypes from 'prop-types'
|
|
5
|
+
import React, { useEffect, useRef } from 'react'
|
|
6
|
+
import styles from './button.styles.js'
|
|
7
|
+
|
|
8
|
+
export const Button = ({
|
|
9
|
+
children,
|
|
10
|
+
className,
|
|
11
|
+
dataTest = 'dhis2-uicore-button',
|
|
12
|
+
destructive,
|
|
13
|
+
disabled,
|
|
14
|
+
icon,
|
|
15
|
+
initialFocus,
|
|
16
|
+
large,
|
|
17
|
+
name,
|
|
18
|
+
primary,
|
|
19
|
+
secondary,
|
|
20
|
+
small,
|
|
21
|
+
tabIndex,
|
|
22
|
+
toggled,
|
|
23
|
+
type = 'button',
|
|
24
|
+
value,
|
|
25
|
+
onBlur,
|
|
26
|
+
onClick,
|
|
27
|
+
onFocus,
|
|
28
|
+
onKeyDown,
|
|
29
|
+
loading,
|
|
30
|
+
...otherProps
|
|
31
|
+
}) => {
|
|
32
|
+
const ref = useRef()
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (initialFocus && ref.current) {
|
|
36
|
+
ref.current.focus()
|
|
37
|
+
}
|
|
38
|
+
}, [initialFocus, ref.current])
|
|
39
|
+
|
|
40
|
+
const { 'aria-label': ariaLabel, title } = otherProps
|
|
41
|
+
|
|
42
|
+
if (!children && !title && !ariaLabel) {
|
|
43
|
+
console.debug(
|
|
44
|
+
'Button component has no children but is missing title and ariaLabel attribute.'
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const handleClick = (event) => onClick && onClick({ value, name }, event)
|
|
49
|
+
const handleBlur = (event) => onBlur && onBlur({ value, name }, event)
|
|
50
|
+
const handleFocus = (event) => onFocus && onFocus({ value, name }, event)
|
|
51
|
+
const handleKeyDown = (event) =>
|
|
52
|
+
onKeyDown && onKeyDown({ value, name }, event)
|
|
53
|
+
|
|
54
|
+
const iconOnly = icon && !children
|
|
55
|
+
const buttonClassName = cx(className, {
|
|
56
|
+
primary,
|
|
57
|
+
secondary,
|
|
58
|
+
destructive,
|
|
59
|
+
small,
|
|
60
|
+
large,
|
|
61
|
+
'icon-only': iconOnly,
|
|
62
|
+
toggled,
|
|
63
|
+
loading: loading,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<button
|
|
68
|
+
ref={ref}
|
|
69
|
+
name={name}
|
|
70
|
+
className={buttonClassName}
|
|
71
|
+
data-test={dataTest}
|
|
72
|
+
disabled={disabled || loading}
|
|
73
|
+
tabIndex={tabIndex}
|
|
74
|
+
type={type}
|
|
75
|
+
onBlur={handleBlur}
|
|
76
|
+
onClick={handleClick}
|
|
77
|
+
onFocus={handleFocus}
|
|
78
|
+
onKeyDown={handleKeyDown}
|
|
79
|
+
{...otherProps}
|
|
80
|
+
>
|
|
81
|
+
{loading && (
|
|
82
|
+
<span className="loader">
|
|
83
|
+
{destructive || primary ? (
|
|
84
|
+
<CircularLoader extrasmall invert />
|
|
85
|
+
) : (
|
|
86
|
+
<CircularLoader extrasmall />
|
|
87
|
+
)}
|
|
88
|
+
</span>
|
|
89
|
+
)}
|
|
90
|
+
{icon && <span className="button-icon">{icon}</span>}
|
|
91
|
+
{children}
|
|
92
|
+
<style jsx>{styles}</style>
|
|
93
|
+
</button>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Button.propTypes = {
|
|
98
|
+
/** Component to render inside the button */
|
|
99
|
+
children: PropTypes.node,
|
|
100
|
+
/** A className that will be passed to the `<button>` element */
|
|
101
|
+
className: PropTypes.string,
|
|
102
|
+
/**
|
|
103
|
+
* A string that will be applied as a `data-test` attribute on the button element
|
|
104
|
+
* for identification during testing
|
|
105
|
+
*/
|
|
106
|
+
dataTest: PropTypes.string,
|
|
107
|
+
/**
|
|
108
|
+
* Applies 'destructive' button appearance, implying a dangerous action.
|
|
109
|
+
*/
|
|
110
|
+
destructive: PropTypes.bool,
|
|
111
|
+
/** Applies a greyed-out appearance and makes the button non-interactive */
|
|
112
|
+
disabled: PropTypes.bool,
|
|
113
|
+
/** An icon element to display inside the button */
|
|
114
|
+
icon: PropTypes.element,
|
|
115
|
+
/** Use this variant to capture the initial focus on the page. */
|
|
116
|
+
initialFocus: PropTypes.bool,
|
|
117
|
+
/** Makes the button large. Mutually exclusive with `small` */
|
|
118
|
+
large: sharedPropTypes.sizePropType,
|
|
119
|
+
/** Sets the button into a loading state */
|
|
120
|
+
loading: PropTypes.bool,
|
|
121
|
+
/**
|
|
122
|
+
* Sets `name` attribute on button element.
|
|
123
|
+
* Gets passed as part of the first argument to callbacks (see `onClick`).
|
|
124
|
+
*/
|
|
125
|
+
name: PropTypes.string,
|
|
126
|
+
/**
|
|
127
|
+
* Applies 'primary' button appearance, implying the most important action.
|
|
128
|
+
*/
|
|
129
|
+
primary: PropTypes.bool,
|
|
130
|
+
/**
|
|
131
|
+
* Applies 'secondary' button appearance.
|
|
132
|
+
*/
|
|
133
|
+
secondary: PropTypes.bool,
|
|
134
|
+
/** Makes the button small. Mutually exclusive with `large` prop */
|
|
135
|
+
small: sharedPropTypes.sizePropType,
|
|
136
|
+
/** Tab index for focusing the button with a keyboard */
|
|
137
|
+
tabIndex: PropTypes.string,
|
|
138
|
+
/** Changes appearance of button to an on/off state */
|
|
139
|
+
toggled: PropTypes.bool,
|
|
140
|
+
/** Sets `type` attribute on `<button>` element */
|
|
141
|
+
type: PropTypes.oneOf(['submit', 'reset', 'button']),
|
|
142
|
+
/**
|
|
143
|
+
* Value associated with the button.
|
|
144
|
+
* Gets passed as part of the first argument to callbacks (see `onClick`).
|
|
145
|
+
*/
|
|
146
|
+
value: PropTypes.string,
|
|
147
|
+
/**
|
|
148
|
+
* Callback to trigger on de-focus (blur).
|
|
149
|
+
* Called with same args as `onClick`
|
|
150
|
+
* */
|
|
151
|
+
onBlur: PropTypes.func,
|
|
152
|
+
/**
|
|
153
|
+
* Callback to trigger on click.
|
|
154
|
+
* Called with args `({ value, name }, event)`
|
|
155
|
+
* */
|
|
156
|
+
onClick: PropTypes.func,
|
|
157
|
+
|
|
158
|
+
/** Callback to trigger on focus. Called with same args as `onClick` */
|
|
159
|
+
onFocus: PropTypes.func,
|
|
160
|
+
/** Callback to trigger on key-down. Called with same args as `onClick` */
|
|
161
|
+
onKeyDown: PropTypes.func,
|
|
162
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { sharedPropTypes } from '@dhis2/ui-constants'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Button } from './button.js'
|
|
4
|
+
|
|
5
|
+
// Note: make sure 'fenced code blocks' are not indentend in this template string
|
|
6
|
+
const description = `Buttons are used for triggering actions.
|
|
7
|
+
There are different types of buttons in the design system which are intended
|
|
8
|
+
for different types of actions.
|
|
9
|
+
|
|
10
|
+
\`\`\`js
|
|
11
|
+
import { Button } from '@dhis2/ui'
|
|
12
|
+
\`\`\``
|
|
13
|
+
|
|
14
|
+
const { buttonVariantArgType, sizeArgType } = sharedPropTypes
|
|
15
|
+
|
|
16
|
+
const logger = ({ name, value }) => console.log(`${name}: ${value}`)
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
title: 'Button',
|
|
20
|
+
component: Button,
|
|
21
|
+
parameters: {
|
|
22
|
+
componentSubtitle: 'Initiates an action',
|
|
23
|
+
docs: {
|
|
24
|
+
description: {
|
|
25
|
+
component: description,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
args: {
|
|
30
|
+
children: 'Label me!',
|
|
31
|
+
value: 'default',
|
|
32
|
+
onClick: logger,
|
|
33
|
+
title: 'Button',
|
|
34
|
+
ariaLabel: 'Button',
|
|
35
|
+
},
|
|
36
|
+
argTypes: {
|
|
37
|
+
primary: { ...buttonVariantArgType },
|
|
38
|
+
secondary: { ...buttonVariantArgType },
|
|
39
|
+
destructive: { ...buttonVariantArgType },
|
|
40
|
+
small: { ...sizeArgType },
|
|
41
|
+
large: { ...sizeArgType },
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DemoIcon24 = (
|
|
46
|
+
<svg
|
|
47
|
+
height="24"
|
|
48
|
+
viewBox="0 0 24 24"
|
|
49
|
+
width="24"
|
|
50
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
51
|
+
>
|
|
52
|
+
<path
|
|
53
|
+
d="m10.7071068 13.2928932c.3604839.360484.3882135.927715.0831886 1.3200062l-.0831886.0942074-5.2921068 5.2918932 2.585.001c.51283584 0 .93550716.3860402.99327227.8833789l.00672773.1166211c0 .5128358-.38604019.9355072-.88337887.9932723l-.11662113.0067277h-5l-.0193545-.0001861c-.02332655-.0004488-.04664039-.0017089-.06989557-.0037803l.08925007.0039664c-.05062028 0-.10036209-.0037612-.14896122-.0110193-.01698779-.0026088-.03441404-.0056829-.05176454-.0092208-.02202032-.0043997-.04371072-.0095935-.06511385-.0154809-.01562367-.0043767-.03101173-.0090077-.04630291-.0140171-.01965516-.0063844-.03943668-.0135776-.058916-.0213659-.01773713-.0070924-.03503998-.014575-.05216303-.0225694-.02066985-.0097032-.0410724-.0201205-.0610554-.0312024-.01211749-.006623-.02433616-.0137311-.0364318-.0211197-.0255662-.0157232-.05042194-.0324946-.07445055-.050318-.00744374-.0054399-.01468311-.010971-.02186305-.0166142-.0631594-.049624-.12042594-.1068905-.17019169-.1703222l.08010726.0903567c-.03539405-.0353941-.06758027-.0727812-.09655864-.1118002-.01784449-.0241759-.03461588-.0490316-.05026715-.0746464-.00746051-.0120471-.0145686-.0242658-.02139626-.0365981-.01087725-.0197682-.02129453-.0401707-.03101739-.060963-.00797473-.0170006-.01545736-.0343035-.02242829-.0517631-.00790975-.0197568-.015103-.0395383-.02167881-.0595996-.00481796-.0148851-.00944895-.0302731-.01370154-.0457434-.00601151-.0215565-.01120534-.0432469-.01567999-.0651989-.00346298-.0174188-.00653707-.0348451-.00914735-.0523272-.00160026-.010231-.00303174-.021012-.00429007-.0318458l-.00276132-.027371c-.00207143-.0232552-.00333152-.0465691-.00378026-.0698956l-.00018615-.0193545v-5c0-.5522847.44771525-1 1-1 .51283584 0 .93550716.3860402.99327227.8833789l.00672773.1166211v2.584l5.29289322-5.2911068c.39052429-.3905243 1.02368928-.3905243 1.41421358 0zm9.2928932-3.2928932v10h-10v-2h8v-8zm-6-6v2h-8v7h-2v-9zm7-2 .0193545.00018615c.0233265.00044874.0466404.00170883.0698956.00378026l-.0892501-.00396641c.0506203 0 .1003621.00376119.1489612.01101934.0169878.00260874.0344141.00568283.0517646.00922073.0220203.00439973.0437107.00959356.0651138.0154809.0156237.00437676.0310117.00900775.0463029.01401712.0196552.0063844.0394367.01357765.058916.02136587.0177371.00709246.03504.01457509.052163.0225694.0206699.00970328.0410724.02012056.0610555.03120241.0121174.00662306.0243361.01373115.0364318.02111968.0255662.01572325.0504219.03249464.0744505.05031806.0074437.00543993.0146831.01097097.021863.01661418.0631595.04962402.120426.10689056.1701917.17032223l-.0801072-.0903567c.035394.03539405.0675802.0727812.0965586.11180017.0178445.02417592.0346159.04903166.0502672.07464642.0074605.01204708.0145686.02426575.0213962.03659809.0108773.01976815.0212946.0401707.0310174.06096295.0079748.01700065.0154574.0343035.0224283.05176313.0079098.01975682.015103.03953834.0216788.05959961.004818.01488507.009449.03027313.0137016.04574344.0060115.02155649.0112053.04324689.0156799.06519887.003463.01741884.0065371.03484509.0091474.05232723.0016003.01023098.0030317.02101195.0042901.03184574l.0030256.03039033c.0015457.01796531.0026074.03596443.003185.05397618l.0005171.03225462v5c0 .55228475-.4477153 1-1 1-.5128358 0-.9355072-.38604019-.9932723-.88337887l-.0067277-.11662113v-2.586l-5.2928932 5.2931068c-.3905243.3905243-1.0236893.3905243-1.4142136 0-.3604839-.360484-.3882135-.92771504-.0831886-1.32000624l.0831886-.09420734 5.2911068-5.29289322h-2.584c-.5128358 0-.9355072-.38604019-.9932723-.88337887l-.0067277-.11662113c0-.51283584.3860402-.93550716.8833789-.99327227l.1166211-.00672773z"
|
|
54
|
+
fill="inherit"
|
|
55
|
+
/>
|
|
56
|
+
</svg>
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const DemoIcon16 = (
|
|
60
|
+
<svg
|
|
61
|
+
height="16"
|
|
62
|
+
viewBox="0 0 16 16"
|
|
63
|
+
width="16"
|
|
64
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
65
|
+
>
|
|
66
|
+
<path
|
|
67
|
+
d="m6.85355339 9.14644661c.17356635.17356635.1928515.44299075.05785545.63785889l-.05785545.06924789-4.14755339 4.14644661h2.794c.24545989 0 .44960837.1768752.49194433.4101244l.00805567.0898756c0 .2454599-.17687516.4496084-.41012437.4919443l-.08987563.0080557h-4c-.24545989 0-.44960837-.1768752-.49194433-.4101244l-.00805567-.0898756v-4c0-.2761424.22385763-.5.5-.5.24545989 0 .44960837.1768752.49194433.4101244l.00805567.0898756v2.792l4.14644661-4.14555339c.19526215-.19526215.51184463-.19526215.70710678 0zm6.14644661-2.14644661v6h-6v-1h5v-5zm-4-4v1h-5v5h-1v-6zm5.5-2c.2454599 0 .4496084.17687516.4919443.41012437l.0080557.08987563v4c0 .27614237-.2238576.5-.5.5-.2454599 0-.4496084-.17687516-.4919443-.41012437l-.0080557-.08987563v-2.794l-4.14644661 4.14755339c-.19526215.19526215-.51184463.19526215-.70710678 0-.17356635-.17356635-.1928515-.44299075-.05785545-.63785889l.05785545-.06924789 4.14655339-4.14744661-2.793.001c-.2454599 0-.4496084-.17687516-.4919443-.41012437l-.0080557-.08987563c0-.24545989.1768752-.44960837.4101244-.49194433l.0898756-.00805567z"
|
|
68
|
+
fill="inherit"
|
|
69
|
+
/>
|
|
70
|
+
</svg>
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const Template = (args) => <Button {...args} />
|
|
74
|
+
|
|
75
|
+
export const Basic = Template.bind({})
|
|
76
|
+
Basic.args = {
|
|
77
|
+
name: 'Basic button',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const Primary = Template.bind({})
|
|
81
|
+
Primary.args = {
|
|
82
|
+
primary: true,
|
|
83
|
+
name: 'Primary button',
|
|
84
|
+
}
|
|
85
|
+
Primary.parameters = {
|
|
86
|
+
docs: {
|
|
87
|
+
description: {
|
|
88
|
+
story: 'Used to highlight the most important/main action on a page. \
|
|
89
|
+
A "Save" button for a form page should be primary, for example. \
|
|
90
|
+
Use sparingly, rarely should there be more than a single primary \
|
|
91
|
+
button per page.',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const Secondary = Template.bind({})
|
|
97
|
+
Secondary.args = {
|
|
98
|
+
secondary: true,
|
|
99
|
+
name: 'Secondary button',
|
|
100
|
+
}
|
|
101
|
+
Secondary.parameters = {
|
|
102
|
+
docs: {
|
|
103
|
+
description: {
|
|
104
|
+
story: 'Used for passive actions, often as an alternative to the primary \
|
|
105
|
+
action. If "Save" is primary, "Cancel" could be secondary. \
|
|
106
|
+
Not intended to draw user attention. Do not use for the only \
|
|
107
|
+
action on a page.',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const Destructive = Template.bind({})
|
|
113
|
+
Destructive.args = {
|
|
114
|
+
destructive: true,
|
|
115
|
+
name: 'Destructive button',
|
|
116
|
+
}
|
|
117
|
+
Destructive.parameters = {
|
|
118
|
+
docs: {
|
|
119
|
+
description: {
|
|
120
|
+
story: 'Used instead of a primary button when the main action is \
|
|
121
|
+
destructive in nature. Used to highlight to the user the \
|
|
122
|
+
seriousness of the action. \
|
|
123
|
+
**Destructive buttons must only be used for destructive actions.**',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
export const DestructiveSecondary = Template.bind({})
|
|
128
|
+
DestructiveSecondary.args = {
|
|
129
|
+
destructive: true,
|
|
130
|
+
secondary: true,
|
|
131
|
+
name: 'Destructive secondary button',
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const Disabled = (args) => (
|
|
135
|
+
<>
|
|
136
|
+
<Button name="Disabled button" {...args} />
|
|
137
|
+
<Button primary name="Disabled primary button" {...args} />
|
|
138
|
+
<Button secondary name="Disabled button" {...args} />
|
|
139
|
+
<Button destructive name="Disabled button" {...args} />
|
|
140
|
+
</>
|
|
141
|
+
)
|
|
142
|
+
Disabled.args = {
|
|
143
|
+
disabled: true,
|
|
144
|
+
}
|
|
145
|
+
Disabled.parameters = {
|
|
146
|
+
docs: {
|
|
147
|
+
description: {
|
|
148
|
+
story: "Use disabled buttons when an action is being prevented for some reason. \
|
|
149
|
+
Always communicate to the user why the button can't be clicked. This can \
|
|
150
|
+
be done through a tooltip on hover, or with supplementary text underneath \
|
|
151
|
+
the button. Do not change the button label between disabled/enabled states.",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const Small = Template.bind({})
|
|
157
|
+
Small.args = {
|
|
158
|
+
small: true,
|
|
159
|
+
name: 'Small button',
|
|
160
|
+
}
|
|
161
|
+
Small.parameters = {
|
|
162
|
+
docs: {
|
|
163
|
+
description: {
|
|
164
|
+
story: 'Buttons are available in three sizes: `small`, `medium`, and `large`. \
|
|
165
|
+
Medium is usually the correct choice. Use small buttons in an information-\
|
|
166
|
+
dense ui.',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const Large = Template.bind({})
|
|
172
|
+
Large.args = {
|
|
173
|
+
large: true,
|
|
174
|
+
name: 'Large button',
|
|
175
|
+
}
|
|
176
|
+
Large.parameters = {
|
|
177
|
+
docs: {
|
|
178
|
+
description: {
|
|
179
|
+
story: 'Buttons are available in three sizes: `small`, `medium`, and `large`. \
|
|
180
|
+
Medium is usually the correct choice. Large buttons can be used on very simple, \
|
|
181
|
+
single-action pages.',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const InitialFocus = Template.bind({})
|
|
187
|
+
InitialFocus.args = {
|
|
188
|
+
initialFocus: true,
|
|
189
|
+
name: 'Focused button',
|
|
190
|
+
}
|
|
191
|
+
// When enabled, this story grabs focus every time a control is changed
|
|
192
|
+
// in the docs page. Disabled for better UX
|
|
193
|
+
InitialFocus.parameters = { docs: { disable: true } }
|
|
194
|
+
|
|
195
|
+
export const Icon = (args) => (
|
|
196
|
+
<>
|
|
197
|
+
<Button {...args} />
|
|
198
|
+
<Button primary {...args} />
|
|
199
|
+
<Button secondary {...args} />
|
|
200
|
+
<Button destructive {...args} />
|
|
201
|
+
</>
|
|
202
|
+
)
|
|
203
|
+
Icon.args = {
|
|
204
|
+
icon: DemoIcon24,
|
|
205
|
+
name: 'Icon button',
|
|
206
|
+
}
|
|
207
|
+
Icon.parameters = {
|
|
208
|
+
docs: {
|
|
209
|
+
description: {
|
|
210
|
+
story: 'Icons can be included in Basic, Primary, Secondary and Destructive buttons. \
|
|
211
|
+
Use an icon to supplement the text label. Remember that the user may not be \
|
|
212
|
+
fluent in the working language, so an accompanying icon on an important action \
|
|
213
|
+
can be a welcome addition. Buttons with icons only should be used for \
|
|
214
|
+
supplementary actions and should include a text tooltip on hover.',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export const IconRTL = (args) => (
|
|
220
|
+
<div dir="rtl">
|
|
221
|
+
<Button {...args} />
|
|
222
|
+
</div>
|
|
223
|
+
)
|
|
224
|
+
IconRTL.args = {
|
|
225
|
+
icon: DemoIcon24,
|
|
226
|
+
name: 'RTL icon button',
|
|
227
|
+
children: 'RTL text',
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export const IconOnly = Template.bind({})
|
|
231
|
+
IconOnly.args = {
|
|
232
|
+
icon: DemoIcon24,
|
|
233
|
+
name: 'Icon only button',
|
|
234
|
+
children: null,
|
|
235
|
+
title: 'Icon Button',
|
|
236
|
+
ariaLabel: 'Icon Button',
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export const IconSmall = Template.bind({})
|
|
240
|
+
IconSmall.args = {
|
|
241
|
+
icon: DemoIcon16,
|
|
242
|
+
small: true,
|
|
243
|
+
name: 'Icon small button',
|
|
244
|
+
children: 'Label me!',
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const IconOnlySmall = Template.bind({})
|
|
248
|
+
IconOnlySmall.args = {
|
|
249
|
+
icon: DemoIcon16,
|
|
250
|
+
small: true,
|
|
251
|
+
name: 'Icon only small button',
|
|
252
|
+
children: null,
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const Toggled = Template.bind({})
|
|
256
|
+
Toggled.args = {
|
|
257
|
+
toggled: true,
|
|
258
|
+
icon: DemoIcon24,
|
|
259
|
+
name: 'Toggled button',
|
|
260
|
+
children: null,
|
|
261
|
+
}
|
|
262
|
+
Toggled.parameters = {
|
|
263
|
+
docs: {
|
|
264
|
+
description: {
|
|
265
|
+
story: 'A button can represent an on/off state using the toggle option. \
|
|
266
|
+
Use a toggle button when the user can enable or disable an option and \
|
|
267
|
+
a checkbox or switch is not suitable. This will most often be in the case of \
|
|
268
|
+
a toolbar, such as bold or italic options in a text editing toolbar. \
|
|
269
|
+
A toggle button in this example uses an icon and does not need text. \
|
|
270
|
+
A text label should be provided in a tooltip on hover. The toggle option \
|
|
271
|
+
is available for basic and secondary type buttons.',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export const ToggledDisabled = Template.bind({})
|
|
277
|
+
ToggledDisabled.args = {
|
|
278
|
+
toggled: true,
|
|
279
|
+
disabled: true,
|
|
280
|
+
icon: DemoIcon24,
|
|
281
|
+
name: 'Toggled button',
|
|
282
|
+
children: null,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const Loading = (args) => (
|
|
286
|
+
<>
|
|
287
|
+
<Button {...args} />
|
|
288
|
+
<Button primary {...args} />
|
|
289
|
+
<Button secondary {...args} />
|
|
290
|
+
<Button destructive {...args} />
|
|
291
|
+
</>
|
|
292
|
+
)
|
|
293
|
+
Loading.args = {
|
|
294
|
+
name: 'Loading button',
|
|
295
|
+
loading: true,
|
|
296
|
+
children: 'Loading...',
|
|
297
|
+
icon: DemoIcon24,
|
|
298
|
+
}
|
|
299
|
+
Loading.parameters = {
|
|
300
|
+
docs: {
|
|
301
|
+
description: {
|
|
302
|
+
story: 'A button can be in a loading state. Use the loading state to show a pending action after the button has been triggered. The button text should change to let the user know what is happening. For example, a button labelled "Send" might changed to "Sending..." when in a loading state.',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
}
|